关于php session文件锁机制引发的问题和定位过程

本文通过一起因PHP Session文件锁机制导致的并发请求失败案例,详细剖析了问题发生的原因及解决过程,揭示了Session机制对并发处理的影响。

关于php session文件锁机制引发的问题和定位过程

1. 问题描述

php提供一个web服务,接受客户端请求后,用curl同时发出约10个web请求,请求的是同一个主域名下的服务。但是,该域名只有一台机器。

2. 问题现象

php接受到请求后,用curl将web请求并发发出去后,无法收到回包数据,所有请求无一成功。curl_multi_exec 貌似无法从该函数中得到错误码(待确认),排查起来不太方便。

3. 解决过程
3.1. 看log

发现封装的curl并发库没打log,无法知道curl到底有啥错误原因。事实上curl似乎也没有什么地方可以打出有价值的log

3.2. 抓包

第一次抓包就不上图了,因为很简单:域名解析失败。请求的域名是公司内部域名,在机器上没有配host时是访问不了的。

两个解决方法:1. 在curl内指定ip访问,带上host信息,需要改代码,而且有点死板;2. 找运维在内部dns服务器上加上域名信息。

选第二种吧。

3.3. 域名解决后,还是失败。继续抓包

这次截图下来,以便记录。

 



注意:一个是请求时间,一个是错误码。错误码499(客户端主动关闭边接),发送客户端的字节数为0。时间对不上,抓包看到的请求到达nginx的时间与nginx打印access log的时间差了10秒。这10秒刚好是请求方curl中设置的默认超时时间。

于是情况基本认定是:客户端用curl并发发出请求,请求正常到达nginx端,但请求送到后端php后而一直在等待。而后客户端超时,主动关闭连接。nginx收到关闭连接的请求,结束往后端php的发送,打印access log,错误码为499。

 

php为什么挂起着,无法处理请求?

因为并发了有10个请求过来,难道是php-fpm进程不够,处理不了?按理即便处理不了,也不至于10个请求无一成功。但不管它,在开发环境先修改php-fpm.conf配置,调大进程数。果然错误依旧。

 

继续排查,php-fpm进程到底在干啥?

上strace吧。找一个php-fpm进程,查看进程号,starce -v -tt -p {进程号} ,结果如下(略去一堆前置后置信息,主要看请求到nginx的这个时间点,它在干啥):

.


看时间点差不多,其实php已经收到请求了,那它在干啥?再往下,


就是在这里了!从11秒到22秒,堵了10秒多的时间!flock 显而易见,这是在加锁。什么操作会加锁?往上看到有 open("/tmp/sess_...") 的文件操作,这是php的session文件(php的session机制,session默认保存在本机文件,如果没指定目录,默认在/tmp/目录下)。open session文件是什么时候操作的?session_start() 函数。该函数会检查传入的session_id,如果传入有session_id,那么尝试从session文件中还原session信息。如果没有则新创建一个。这个是php session机制决定的。

 

为什么会加锁?回想起来,因为需要登录态,那10来个并发的curl请求都设置了cookie信息,都是同一个帐号的cookie信息,并且因为只有1台web服务器,所有的请求最终都落在同一台机器上,就是发起者自身这台机器,而发起者在发出curl请求之前,已经session_start()了。难道是发起者session_start() 后,会锁定session文件,等请求结束后才会释放?如果是这样,就可以解释为什么所有发出的curl请求都失败了,因为php收到curl的请求都,调用session_start()后,获得session信息,然后尝试加锁,但失败了!全部都锁在这行 flock 上了!

 

赶紧查下session_start 函数的信息。本来想查看源码,但这个好像比较痛苦,php函数对应的c++源码,层层跟进不太易找(主要是之前没太找过,不熟悉这个套路),先php.net看看手册:http://php.net/manual/zh/function.session-start.php


看样子确实有锁机制!我们的session_start没有传参数,read_and_close 默认选项是False?

关键字 session_start read_and_close 来google,搜到如下信息(url不贴了,里面有不少广告):


发现原来这个配置是从php7才开始的,我们才php5。看到有 session_write_close() 函数,跟 read_and_close 效果类似,拿来试试,在发起curl 请求前一试,果然成功!

 

那么问题就在这里了。再google多一点,发现一篇文章:

http://konrness.com/php5/how-to-prevent-blocking-php-requests/ 讲得通俗易懂。

可以收工了。但回想起来,为什么查了这么久?还是因为对 php 的 session 机制不够熟悉,不然一切都不需要了。

问题虽然解决了,但session还有几种不同的处理方式,比如自定义handler等,其实顺便也可以一并了解清楚,说不定以后再踩坑呢。


1:变量的传值与引用。\n2:变量的类型转换判断类型方法。\n3:php运算符优先级,一般是写出运算符的运算结果。\n4:PHP中函数传参,闭包,判断输出的echo,print是不是函数等。\n5:PHP数组,数组函数,数组遍历,预定义数组(面试必出)。\n6:PHP面向对象,魔术方法,封装、继承、多态。设计模式,包括(单利、工厂、迭代器、装饰、命令、策略)。\n7:正则表达式,每个标号含义,邮箱、网址、标签匹配,正则函数(面试必出)。\n8:PHP异常处理(级别,错误日志,控制错误输出)(面试必出)。\n9:PHP时间函数,日期计算函数。\n10:文件系统,记录日志、目录、文件的遍历、上传、多方法得到文件扩展名、文件引用方式、引用函数区别。(面试必出)。\n11:会话控制,主要说原理。session与cookie在分布式应用中出现问题的解决方案。\n12:PHP模板引擎,常用模板引擎特点,MVC好与不好的地方。\n13:PHP安全处理,过滤函数。\n14:XML的使用。\n15:PHP字符串的处理,包括转义(安全)、编码、截取、定位、与数组间的转换、处理函数等。(面试必出)。\n16:Socket编程,各种协议,head头,curl参数含义。\n17:网络状态码含义,常用(204,304, 404, 504,502)。\n18:Apache配置文件,PHP配置文件,各个含义字段的含义。\n19:网络各种攻击的名词含义(SQL攻击、XSS、CSRF、DDos),防止措施。\n20:url的处理函数,得到url指定的部分。\nMysql基础\n1:基础sql语句书写(一般让写关联子查询语句)\n2:索引的创建,优缺点,最左原则\n3:存储引擎,常用的几个,优缺点,差别,原理(面试必出)\n4:sql注入的处理方法\n5:mysql处理函数(PHP中封装的)\n6:PDO的使用方法,为什么使用\n7:mysql的优化,表拆分等\n8:事务处理,sql语句的处理效率等\n9:数据表字段的类型,同类型间的区别,改如何选取,int(10)与int(11)的区别等。\n10:数据库索引使用的那种数据结构,画出数据结构\nLinux\n1:常用命令的使用,vim编辑器的使用。\n2:进程,cpu等信息的查看命令。\n3:文件内查看命令(主要涉及统计信息)。\n4:Shell的使用,命令操作。\n5:awk的用法\n6:shell杀掉所有的php-fpm进程\nNoSql\n1:Redis的应用场景,结合微博业务说出他的具体应用。\n2:Redis与MC支持数据的不同点,两者都支持哪些数据结构的存储,写越多越好。\n3:Redis持久化存储的原理,与Mysql的应用区别。怎样保持持久化数据与内存数据同步的关系(Redis同步机制)\n4:Redis与MC在并发状态下的性能比较。\n5:MC的内存管理机制,当一个数据需要存储的时候怎样分配内存空间\n6:Redis的内存管理机制,与MC有哪些不同点。\n开发环境\n   1:PHP7中的新特性与废弃的特性\n   2:为什么要使用PHP7,PHP7快在哪里\n   3:PHP7中对异常错误的理解\n版本控制\n   1:git的使用命令,例如:写出版本回退命令。\n   2:git与svn的区别。\n   3:如何进行多分支开发,包括多人开发协同,分段测试,上线。将上面这些具体解答
最新发布
10-09
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值