记录服务工厂单例遇到的问题

平头有杀气
/**
 * 服务工厂类
 * 统一管理服务实例创建,实现单例模式,支持依赖注入
 */
class ServiceFactory
{
    /**
     * 服务实例缓存
     */
    private static array $instances = [];

    /**
     * 获取缓存服务实例
     */
    public static function getCacheService(
        $redis = null,
        string|null $prefix = null
    ): CacheService {
        $key = 'CacheService';
        if (!isset(self::$instances[$key])) {
            self::$instances[$key] = new CacheService($redis, $prefix);
        }
        return self::$instances[$key];
    }
}
/**
 * 缓存服务
 * 提供统一的缓存操作接口,封装Redis操作
 */
class CacheService
{
    private $redis;
    private $prefix;

    public function __construct($redis = null, string|null $prefix = null)
    {
        $this->redis = $redis ?? Redis::connection('default');
        $this->redis->select(getenv('redisDb'));
        $this->prefix = $prefix;
    }

    public function rPush(string $key, string ...$values):int|bool
    {
        array_walk($values, function(&$value) {
            $value = (string)$value;
        });
        $cacheKey = $this->prefix ? $this->prefix . $key : $key;
        return $this->redis->rpush($cacheKey, ...$values);
    }

    public function get(string $key): ?string
    {
        $cacheKey = $this->prefix ? $this->prefix . $key : $key;
        $value = $this->redis->get($cacheKey);
        return $value === false ? null : $value;
    }
}

// 伪代码A

$this->cacheService = ServiceFactory::getCacheService();
$cacheKey = 'key';
$this->cacheService->pipeline(); // 开启管道
foreach ($idMap as $id) {
    $this->cacheService->rPush($cacheKey, $id);
}
$this->cacheService->exec(); // 提交

// 伪代码B

$this->cacheService = ServiceFactory::getCacheService();
$cacheKey = 'key';
$this->cacheService->get($cacheKey);

问题1

rPush定义了int|bool返回类型,在管道下返回的是redis对象报错

Return value must be of type int|bool, Redis returned

解决方法是立即去掉返回类型后重启

public function rPush(string $key, string ...$values)

这时问题2来了(🤦‍♀️)

问题2

TypeError: app\common\service\CacheService::get(): Return value must be of type ?string, Redis returned

(问号脸) (问号脸) (问号脸)
想到了问题1,马上去修改返回类型,又想到了get它怎么会有类型有问题(问号脸)
去看了一下伪代码1使用了管道,这个时候电话来了....一边对喷一边执行 php start.php stop

最后发现是单例 管道污染

改造计划, 新增一个redis的方法

    public function redis()
    {
        $connection = Redis::connection('default');
        $connection->select(getenv('redisDb'));
        return $connection;
    }

    public function get(string $key): ?string
    {
        $cacheKey = $this->prefix ? $this->prefix . $key : $key;
        $value = $this->redis()->get($cacheKey);
        return $value === false ? null : $value;
    }
$this->redis()->get($cacheKey);

这样每次都是一个新的连接,然后把pipeline和exec方法删除

Ctrl+ H

$this->redis->

替换成

$this->redis()->

需要使用管道的地方

$this->cacheService = ServiceFactory::getCacheService();
$redis = $this->cacheService->redis();
$cacheKey = 'key';
$redis->pipeline(); // 开启管道
foreach ($idMap as $id) {
    $redis->rPush($cacheKey, $id);
}
$redis->exec(); // 提交

思考了一会其实不改$this->redis添加redis()那个方法获取一个新的连接也行的

35 1 0
1个评论

平头有杀气

本帖纯属骗积分

  • 暂无评论

平头有杀气

290
积分
0
获赞数
0
粉丝数
2023-11-07 加入
🔝