您的位置:首页 > 编程语言 > PHP开发

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

2016-06-14 14:38 537 查看

关于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等,其实顺便也可以一并了解清楚,说不定以后再踩坑呢。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux php curl strace