JAVA与PHP互相调用造成服务假死

事情的起源是这样的,公司老的服务是由PHP构建的,现在要逐步转向Java。于是要进行新老服务的替换与迁移。我们目前的策略是按模块,一部分一部分替换,直到Java服务能完全替代原来的PHP服务。也就是说会存在PHP与Java服务同时存在的情况。用户鉴权这块目前还是由PHP提供,所以Java需要调用PHP相关的接口,以拿到用户的信息。

说一下大致的业务流程:

  1. JAVA端生成二维码
  2. PHP应用扫描后将当前登录用户的token传递给Java应用
  3. Java端通过token回调PHP接口,获取用户信息

image-20210607201922455

1
2
3
PHP应用 -> JAVA应用 : 步骤1:扫码二维码
PHP应用 -> JAVA应用 : 步骤2:携带二维码信息及PHP TOKEN回调Java接口(/java/code/callback)
JAVA应用 -> PHP应用 : 步骤3:回调PHP用户接口(/php/userinfo)

大致就是这么一个业务流程,在和PHP方联调接口时发现,在执行步骤2(PHP回调/java/code/callback)时,JAVA接口一直无法返回数据,直到超时。但使用postman测试Java接口是正常的。这就令人很疑惑。经过查看Java端的日志发现,在Java端执行步骤3(/php/userifno)时PHP接口一直没有返回用户信息导致超时。如此就形成了一个连锁反应,PHP-JAVA-PHP(超时)导致整个调用链路超时。由于我们没有专业的PHP工程师,遗留的PHP代码也是由Java开发进行维护的,所以导致排查问题很困难。于是开始通过百度查找答案,首先看到的是这一篇文章:PHP是单线程还是多线程?,当中有这么一句话:

每个PHP文件的执行是单线程的,但是,服务器(apache/nigix/php-fpm)是多线程的。每次对某个PHP文件的访问服务器都会创建一个新的进程/线程,用来执行对应的PHP文件。

于是我错误的把这句话理解为,PHP对于单个文件的执行的串行的(其实仔细想想也不太可能,如果真是这样,PHP面对并发基本是无解状态),而且刚好我们的互相调用的代码在同一个文件当中。于是我们将两个接口分开。发现还是有这个问题。于是我们继续百度看看有没有类似的问题,终于我们找到了导致问题的原因:

  1. PHP session阻塞页面的问题
  2. PHP中Session引起的脚本阻塞问题解决办法

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

回想我们应用的调用流程:

  1. 客户端扫码调用PHP接口(将二维码信息给PHP,PHP第一次获取session)
  2. PHP服务端回调Java携带当前用户token
  3. JAVA收到回调后调用PHP接口获取用户信息(PHP第二次获取session,此时无法获取,因为第一次获取的锁还没有释放)

通过这次问题的排查,也明白了不同语言的实现方式是不一样的,像Java就不会有这种问题(Java不会将session信息写入文件,也不存在所谓文件锁)。