如果session用redis的话在swoole环境中,只要并发上来就报错。
我用的ab -n10000 -c100 测试的
但是默认的是文件保存,就没一点问题。
感觉是连接池的问题,但是没找到关于session方面对于redis的文档和文章
有没有大佬讲解一下这是为啥🤔🤔🤔🤔🤔🤔
➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖
<?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;
}
}
workerman
的RedisSessionHandler
没有使用连接池导致的,你可以参考 连接池 Pool 提交一下PR
OK。俺去试试
你的做法是正确的,没有侵入性,在没有改workerman源码的情况下是最佳实践了。
但最好还是在workerman内部去实现,因为workerman5.1引入了协程支持,而且这个功能在非webman环境也是需要的。
😝😝😝谢谢大佬
这部分的代码在workerman,不仅仅是webman的功能,所以workerman内部复用webman/redis不太可行,如果在原代码的基础上使用连接池还是可以的
不是说在 workerman 内部使用 webman/redis,这不是倒反天罡了吗?
而是在说既然 workerman 5.1 引入了协程,那么就应该由 workerman 内部来实现 Redis 连接池。