php session的锁和并发


Posted in PHP onJanuary 22, 2016

本文分享PHP的session在使用过程中的锁和并发的问题,与之相关的现象有请求阻塞、session数据丢失、session数据读不到。

我登录不了了
某天,我准备登录我们一个后台系统,前去解决一个bug,在账户密码验证码都准确输入的情况下,我登录不上,经过多次实验发现主要有两个错误信息:

  • csrf验证失败
  • 验证码错误【我对码神起誓我用半角输入了我看到的验证码,且顺序一致,无多加字符】

我们的系统
我们的系统是基于phalcon 2.0.8 开发的,如你所见,我们在表单域加入了防止csrf攻击的域。也启用了验证码。

<input type="hidden" 
  name="{{ security.getTokenKey() }}"
  value="{{ security.getToken() }}"/>
<img src="/login/getCaptcha" id="img-captcha"/>

我首先对这两个组件进行查阅,发现他们都是将数据存于session:

# phalcon/security.zep
# Security::getToken()
let session = <SessionInterface> dependencyInjector->getShared("session"); 
session->set(this->_tokenValueSessionID, token); 
$this->session->set('admin_get_captcha_action', $captcha);

然后我又查阅了我们session的实现,发现是将数据存储于redis的。

找啊找
什么问题导致我登录不上呢?既然是数据验证上出现问题,就从数据着手吧,我登陆我们测试环境的redis机器,执行 redis-cli monitor,然后走一遍登录流程,发现输出如下(意思意思):

  • GET sessionId 
  • GET sessionId 
  • SETEX sessionId 3600 csrf=xxxx 
  • SETEX sessionId 3600 captcha=abcd 

我们可以看到:

1、这里存在两次请求,一次是表单加载,一次是生成验证码的。
2、存在“并发”的情况,这两个请求应该是表单加载渲染后才请求验证码的,也就是session顺序应该是get->set->get->set,看起来怎么是并发请求了。
3、后面那个SETEX没有csrf的内容,也就是覆盖掉前面的数据了
整个世界都不好了,不过也稍微明白是什么问题了。什么问题呢,说来话长,要从PHP的session数据的存取说起。

php的session数据的存取
session的数据是经过编码成字符串存储在存储器【file、db、redis、memcache等】的,在我们使用session的时候,是什么时候去储存器取数据的?又是什么时候将数据写入存储器的?

这个问题的答案可能和一些朋友想的不一样,一个请求里面,PHP只会读取一次存储器,在session_start的时候,然后也只会写入一次存储器,在请求结束的时候,或调用session_write_close的时候,将数据刷回存储器,关闭session。

那么问题来了:

1、如果一个会话,同时出现两个读写session请求,没有保证获取1-写入1-获取2-写入2,同时没有cas版本管理机制的情况下,这些并发请求就会彼此读取不到对方的写入,最后写入的会把前面请求写入的session覆盖掉。
2、如果请求是串行的,像登录页面的表单和验证码,也有可能前面的请求已经输出内容了,但是session还没写入,后面的请求就已经发起了。
锁与不锁
解决这种资源的并发一般会通过锁或版本管理来处理。但是版本管理我看不到好的方法。就聊聊锁吧。

其实锁是不大适合,有弊端的。

php的session,默认是用文件存储的,在打开session的时候,会对文件加独占锁,这样,其它请求就无法获取锁了,只能等待直到前面的锁解了。

这样保证了 读取-写入,读取-写入的顺序。

其它存储器,例如mysql,可以借助select for update进行行锁。redis可以通过一个自增键,返回1的获取到锁等来实现。

这个实现的话,对数据流来说很理想,但是,对于目前这种页面大量应用ajax的情况,所有请求排队处理,将大大加大页面展现的耗时,甚至出现请求超时等不可用故障。

没有解决的解决
不建议过多使用session,其一次读取一次写入的机制所引发的问题,会造成坑的存在。
在模版渲染前,或请求输出前调用session_write_close

# 立刻回写session,避免session覆盖
$eventManager = $this->view->getEventsManager();
if (!$eventManager) { 
  $eventManager = new Manager();
  $this->view->setEventsManager($eventManager);
}
$eventManager->attach("view:afterRender",function(){
  session_write_close();
});
return $this->view;
if($login) { 
  # 立刻回写session,避免session读取不到
  $eventManager = $this->dispatcher->getEventsManager();
  if (!$eventManager) {
    $eventManager = new Manager();
    $this->dispatcher->setEventsManager($eventManager);
  }
  $eventManager->attach('dispatch:afterDispatchLoop',function(){
    session_write_close();
  });
  return $this->response->setHeader('Location', '/');
}

以上就是关于php session的锁和并发,希望对大家的学习有所帮助。

PHP 相关文章推荐
php上传、管理照片示例
Oct 09 PHP
PHP 采集获取指定网址的内容
Jan 05 PHP
PHP 全角转半角实现代码
May 16 PHP
几种有用的变型 PHP中循环语句的用法介绍
Jan 30 PHP
PHP写的获取各搜索蜘蛛爬行记录代码
Aug 21 PHP
10个超级有用值得收藏的PHP代码片段
Jan 22 PHP
PHP中使用GD库绘制折线图 折线统计图的绘制方法
Nov 09 PHP
PHP操作MySQL的mysql_fetch_* 函数的常见用法教程
Dec 25 PHP
php使用函数pathinfo()、parse_url()和basename()解析URL
Nov 25 PHP
PHP读取、解析eml文件及生成网页的方法示例
Sep 04 PHP
PHP htmlspecialchars_decode()函数用法讲解
Mar 01 PHP
浅谈Laravel中的三种中间件的作用
Oct 13 PHP
php5.4传引用时报错问题分析
Jan 22 #PHP
php实现word转html的方法
Jan 22 #PHP
高质量PHP代码的50个实用技巧必备(上)
Jan 22 #PHP
php中namespace use用法实例分析
Jan 22 #PHP
PHP爬虫之百万级别知乎用户数据爬取与分析
Jan 22 #PHP
两种php实现图片上传的方法
Jan 22 #PHP
php+html5实现无刷新图片上传教程
Jan 22 #PHP
You might like
高分R级DC动画剧《哈莉·奎茵》第二季正式预告首发
2020/04/09 欧美动漫
php中长文章分页显示实现代码
2012/09/29 PHP
PHP获取远程图片并保存到本地的方法
2015/05/12 PHP
PHP进制转换实例分析(2,8,16,36,64进制至10进制相互转换)
2017/02/04 PHP
PHP框架自动加载类文件原理详解
2017/06/06 PHP
php7 参数、整形及字符串处理机制修改实例分析
2020/05/25 PHP
PHP常用字符串输出方法分析(echo,print,printf及sprintf)
2021/03/09 PHP
JQuery+JS实现仿百度搜索结果中关键字变色效果
2011/08/02 Javascript
JavaScript与DOM组合动态创建表格实例
2012/12/23 Javascript
js操作checkbox遇到的问题解决
2013/06/29 Javascript
js学习阶段总结(必看篇)
2016/06/16 Javascript
jQuery中ScrollTo用法示例
2016/09/04 Javascript
使用jquery如何获取时间
2016/10/13 Javascript
微信小程序之ES6与事项助手的功能实现
2016/11/30 Javascript
bootstrap折叠调用collapse()后data-parent不生效的快速解决办法
2017/02/23 Javascript
layui select动态添加option的实例
2018/03/07 Javascript
js 下拉菜单点击旁边收起实现(踩坑记)
2019/09/29 Javascript
JS 创建对象的模式实例小结
2020/04/28 Javascript
微信小程序实现列表滚动头部吸顶的示例代码
2020/07/12 Javascript
Vue 防止短时间内连续点击后多次触发请求的操作
2020/11/11 Javascript
[37:37]DAC2018 4.4 淘汰赛 Optic vs Mineski 第二场
2018/04/05 DOTA
python3读取excel文件只提取某些行某些列的值方法
2018/07/10 Python
Python检查ping终端的方法
2019/01/26 Python
python 求定积分和不定积分示例
2019/11/20 Python
安装python依赖包psycopg2来调用postgresql的操作
2021/01/01 Python
socket.io 和canvas 实现的共享画板功能
2019/05/22 HTML / CSS
HTML5的结构和语义(5):内嵌媒体
2008/10/17 HTML / CSS
为世界各地的女性设计和生产时尚服装:ROMWE
2016/09/17 全球购物
瑞典最大的儿童用品网上商店:pinkorblue.se
2021/03/09 全球购物
工商治理实习生的自我评价
2014/01/15 职场文书
党员大会主持词
2014/04/02 职场文书
机械设备与数控技术专业求职信
2014/08/10 职场文书
反四风对照检查材料
2014/09/22 职场文书
2015年网管个人工作总结
2015/05/22 职场文书
清明节主题班会
2015/08/14 职场文书
60条职场经典语录,总有一条能触动你的心
2019/08/21 职场文书