同一个服务器上的不同项目要注意session的死锁问题

来源:互联网 发布:淘宝起名字 编辑:程序博客网 时间:2024/05/18 02:26

最近做一个CMS的项目,需要调用服务器上的另一个项目提供的API接口,结果发现总是调用不成功。

但是在开发环境下却是好的,只在测试环境下以及线上环境上不行。

我在cms上用curl来调用另一个项目的api接口,需要传一个sessionId过去,另外就是认证的key以及项目名称/时间,另外又做了一个所有元素的md5,作为api验证数据完整性以及核对认证key。

结果跟踪调试的过程中,发现调用的结果总是false,但是用日志跟踪的话,发现api服务器上已经将返回值写入到日志文件中,但是cms调用的api接口返回结果在这之前已经返回false了,这个返回值是curl_exec方法返回的。


session_start();

$time = time();
        $sessionId = session_id();

        $token = $this->makeToken($sessionId,$time);
        $url = API_SERVER_URL.'/api/doctor/getSomeInfo';
        $post = ['appId'=>'al','time'=>$time,'token'=>$token,'sessionId'=>$sessionId,'type'=>'1','page'=>1,'pageSize'=>6];
        
        $content = "in getStarDoctors(),url=$url,post:".json_encode($post)."\r\n";
        file_put_contents('/tmp/aaa.log',$content,FILE_APPEND);
        
        $result = $this->curl_post($url,$post);

        $content = "in getStarDoctors(),result:".var_export($result,TRUE)."\r\n";
        file_put_contents('/tmp/aaa.log',$content,FILE_APPEND);
        
         $content = "in getStarDoctors(),result:".json_encode($result)."\r\n";
        file_put_contents('/tmp/aaa.log',$content,FILE_APPEND);
        
        echo $result;
        return;

查询了半天,也不知道问题出在了哪里,开发服务器上的日志显示顺序都是正确的,返回结果也是正确的。

两边做了一下对比,开发服务器上的cms调用的同一台机器上的另一个项目,测试服务器上的cms也是调用的同一台服务器上的另一个项目;

这是为什么呢?纠结中......

后来我们就换一下思路,让开发服务器上的cms调用测试服务器上的api接口,结果发现返回结果正常而不是false;

然后又让测试服务器上的cms调用开发服务器的api,也没有问题;

基本上锁定问题就处在测试服务器上;然后这个问题还是没有解决方法;然后我们想只要线上环境没有这个问题就行,

所以线上环境上测试,结果发现线上环境也不行,cms也调不通api接口,看来这个问题非解决不可了。

然后找高手看一下好像也没有太多的头绪,正好旁边一个同事用xhprof跟踪调用的过程,结果发现问题出在api接口的session_start()上了,这一句就要执行8秒的时间

然后就分析一下原因,同一台服务器上的session,php配置的session存储方式为文件方式,cms项目写了session之后没有及时关闭写入,导致cms占用了session文件

因为该文件cms项目正在写,所以api接口无法读取,必须等curl调用结束之后,我这一个curl请求结束之后,然后api服务器才会执行接下来的语句。

所以我们终于找到了原因,也很快找到了解决方法;也就是在cms项目的session_id()之后,立即调用session_write_close(); 来结束写进程,这样api项目就能很快地读取到该session文件了。

结合了PHP的Session机制,找到了阻塞的原因。由于PHP的Session信息是写入文件的,1个客户端占有1个session文件。因此,当 session_start被调用的时候,该文件是被锁住的,而且是以读写模式锁住的(因为程序中可能要修改session的值),这样,第2次调用 session_start的时候就被阻塞了。

所以同一台服务器上的不同项目一定要注意session的死锁问题。

其实同一个项目中的不同代码,如果一个页面调用了session_start()方法,然后他转到的另一个页面也调用了session_start()方法,也可能会出现这种死锁情况。

因为上一个页面没有结束时,就会阻塞下一个页面使用session_start()方法,所以最好的方式是对于session写完值之后立即调用session_write_close()进行关闭。然后下一个调用session_start()方法的话就不会再受影响了。session_commit()是session_write_close()方法的别称,写完后调用session_commit()也可以。

至于开发服务器上为什么能调通呢?我们发现cms项目的session是保存到memcache中的,session_write_close()不影响并发写,所以不会造成死锁的。

session保存到memcache中的话,不会出现并发读的问题,但是会导致memcache中的连接被占用,每一个session_start()都会创建一个连接,这会导致memcache性能下降的,所以这里也应该调用session_write_close()方法,关闭session才好。

太不小心了,这种问题调不通的时候,一定要第一时间检查自己的代码和逻辑有没问题才是,作为开发人员自以为是的毛病一定要改掉才行啊。


0 0
原创粉丝点击