关于webman的swoole中session存redis的问题【😁已解决😁】

青鱼

问题描述

如果session用redis的话在swoole环境中,只要并发上来就报错。

我用的ab -n10000 -c100 测试的

但是默认的是文件保存,就没一点问题。
感觉是连接池的问题,但是没找到关于session方面对于redis的文档和文章

有没有大佬讲解一下这是为啥🤔🤔🤔🤔🤔🤔
➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖

昨天提问之后,大佬给了个方案。但是作为新手的俺有点不咋会。但是我能懂是啥意思。

然后我就按照这个思路去鞭打我的Ai牛马。。。。。

然后我彻底明白了,因为默认的RedisSessionHandler没有复用webman/redis

然后ai牛马给我个解决方案是自己写一个redis处理器给session用

然后就有了下面的代码⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇

<?php
declare(strict_types=1);
namespace app\support\Session;

use Illuminate\Redis\Connections\PhpRedisConnection;
use RuntimeException;
use Throwable;
use support\Redis as UseRedis;
use Workerman\Protocols\Http\Session;

class RedisSessionHandler implements \Workerman\Protocols\Http\Session\SessionHandlerInterface
{
    protected array $config;
    protected string $sessionPrefix;

    public function __construct(array $config)
    {
        // 检查 Redis 扩展
        if (false === extension_loaded('redis')) {
            throw new RuntimeException('Please install redis extension.');
        }

        //初始化配置:指定用 Session 专属连接池
        $this->config = $config + [
            'connection' => 'session', 
            'prefix'     => 'webman_session:',
        ];

        $this->sessionPrefix = $this->config['prefix'];
    }

    /**
     * 每次操作都重新获取 Redis 连接
     * 避免协程间连接竞争
     */
    protected function getRedis(): PhpRedisConnection
    {
        return UseRedis::connection($this->config['connection']);
    }

    // ------------------- 修改所有方法,使用 getRedis() 获取连接 -------------------
    public function open(string $savePath, string $name): bool
    {
        return true;
    }

    public function read(string $sessionId): string|false
    {
        try {
            $redis = $this->getRedis(); // 每次操作获取新连接
            $key = $this->sessionPrefix . $sessionId;
            $data = $redis->get($key);
            return $data ?: '';
        } catch (Throwable $e) {
            throw $e;
        }
    }

    public function write(string $sessionId, string $sessionData): bool
    {
        $redis = $this->getRedis(); // 每次操作获取新连接
        $key = $this->sessionPrefix . $sessionId;
        return true === $redis->setex($key, Session::$lifetime, $sessionData);
    }

    public function updateTimestamp(string $sessionId, string $data = ""): bool
    {
        $redis = $this->getRedis(); // 每次操作获取新连接
        $key = $this->sessionPrefix . $sessionId;
        return true === $redis->expire($key, Session::$lifetime);
    }

    public function destroy(string $sessionId): bool
    {
        $redis = $this->getRedis(); // 每次操作获取新连接
        $key = $this->sessionPrefix . $sessionId;
        $redis->del($key);
        return true;
    }

    public function close(): bool
    {
        return true;
    }

    public function gc(int $maxLifetime): bool
    {
        return true;
    }
}

然后修改config/redis.php增加一个session的专属连接配置

然后再修改config/session.php把处理器改成这个就行

结论:目前我测试的时候没一点问题,也木有出现抢socket的导致swoole报错的问题了😏😏😏

不过作为新人,我还是必须要请教一下各位大佬。这样的做法可不可行?或者大佬有啥其他更好的方法呢😆😆😆

72 2 0
2个回答

北月妖王

workermanRedisSessionHandler 没有使用连接池导致的,你可以参考 连接池 Pool 提交一下PR

北月妖王

你的做法是正确的,没有侵入性,在没有改workerman源码的情况下是最佳实践了。


但最好还是在workerman内部去实现,因为workerman5.1引入了协程支持,而且这个功能在非webman环境也是需要的。

  • 青鱼 14小时前

    😝😝😝谢谢大佬

  • luoyue 3小时前

    这部分的代码在workerman,不仅仅是webman的功能,所以workerman内部复用webman/redis不太可行,如果在原代码的基础上使用连接池还是可以的

  • 北月妖王 3小时前

    不是说在 workerman 内部使用 webman/redis,这不是倒反天罡了吗?
    而是在说既然 workerman 5.1 引入了协程,那么就应该由 workerman 内部来实现 Redis 连接池。

🔝