CI框架(3 or 4) session锁问题引发的讨论

 角色介绍

sskaje:提起疑问者,疑似国人

narfbg:CI作者之一

sskaje:
在CI3和CI4中,用来实现session的redis驱动与memcached驱动关于session key锁的设计是不对的.

估计是sskaje一针见血的表达导致了作者的第一次回复会带有不满情绪..

CI4中,代码老套,虽然更加兼容但是仍然有bug
CI4中关于session锁的循环中的伪代码如下:
复制代码
if redis::ttl(lock_key) > 0 then          sleep 1s          continue loop      redis::set(lock_key, ttl)
复制代码

 

这里sskaje拿出了CI4中的代码,估计是当时CI4中代码还没更新,作者也提到在后来的版本中升级了这段代码.但是作者的方法就是接下来的这段代码,直接kill掉了其他的并发请求.

没错,两个并发请求会同时通过ttl(有效时间)>0的检查,并且同时上锁.并发的请求对session执行写操作可能会导致数据污染,然而
1.并发的session读操作
2.一个请求执行session写操作,多个并发请求执行session读操作
都能正常执行

在CI3的某个提交中,引进了如下逻辑(伪代码):
复制代码
    if redis::ttl(lock_key) > 0 then          sleep 1s          continue loop      if redis::ttl(lock_key) == key-not-exists then          redis::setNx(lock_key, ttl)     else          redis::set(lock_key, ttl)
复制代码

 

然而并没有什么卵用反而错的更离谱了
与CI4中的情况一样,都是并发请求可以同时越过ttl>0并且匹配到ttl===-2的条件,因为此时还不存在key.请求1在执行redis::setNx后返回成功,请求2因为已经存在了key而失败了.因此请求2将无法获取到session数据

redis::setNx与redis::set不同点在于只对不存在的key生效,若key已存在则会返回false,然后sskaje给出了简单的解决方案:

redis:

复制代码
 protected function _get_lock($session_id)     {         // PHP 7 reuses the SessionHandler object on regeneration,         // so we need to check here if the lock key is for the         // correct session ID.        if ($this->_lock_key === $this->_key_prefix.$session_id.':lock')         {             log_message('error', 'SESSION Redis _get_lock');             return $this->_redis->setTimeout($this->_lock_key, 300);         }          // 30 attempts to obtain a lock, in case another request already has it        $lock_key = $this->_key_prefix.$session_id.':lock';         $attempt = 0

                    
                
50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信