小程序长连接被中断时导致php进程被强制退出
前言
微信小程序请求长连接时,请求还在接受数据的时候关闭小程序,php后端会出现很奇怪的数据不全问题。
问题
通常,我们请求后端时,某个客户端关闭后php进程开启后不会退出(我们习惯上的认知是这样的)。
但当我们假设服务端有一个接口stream.php
写作:
此时我们再实现一个接口answer.php
去请求上述接口:
然后写一个小程序请求案例:
运行小程序,点击“提交”,等有数据显示时,马上退出小程序。
正常来说,即时小程序关闭了,20秒后服务器依然会生成一个finish.txt
。
但是持续操作几遍后都没有发现finish.txt
文件
过程
如果没生成finish.txt
文件,那一定是程序运行到写文件
前就退出了:
如果是要退出文件要么是
die
函数或者抛出异常
,代码里我没有写die
这类函数,那么就有可能是抛出异常了,改动代码:尝试再操作一遍,发现依然没有文件产生。
还是围绕进程退出分析,如果不是代码问题,那就只有是有某个服务将这个进程kill掉了,我选择把锅甩给nginx。按照猜想,退出进程那就一定会有一个信号传进来,所以使用
pcntl_signal
捕获这个信号,顺便使用register_shutdown_function
来辅助分析,继续改写代码:操作一遍小程序,这次服务器产生了2个文件
shutdown.txt
、interupt.txt
,没有signal.txt
文件,但说明程序确实是被意外终止
了,且代码没有执行完。进一步分析程序退出时产生了什么signal,如果小程序能够输出内容,那么代码一定是执行到了
CURLOPT_WRITEFUNCTION
回调,那么就有可能是这个回调机制导致程序信号被拦截(猜想),现在我直接去掉curl,改动代码:继续操作小程序,这时候服务器产生了3个文件
shutdown.txt
、interupt.txt
、signal.txt
,其中signal.txt
的内容是13
,对照信号表得知是SIGPIPE
信号。我尝试调试为什么
CURLOPT_WRITEFUNCTION
回调为什么会导致pcntl_signal
收不到进程信号,但是没有找到原因,下次再看看。
关于SIGPIPE信号
当服务器close一个连接时,若client端接着发数据。根据TCP协议的规定,会收到一个RST响应,client再往这个服务器发送数据时,系统会发出一个SIGPIPE信号给进程,告诉进程这个连接已经断开了,不要再写了。
解决
通过SIGPIPE
得知,客户端是在某种情况下关闭了连接,导致进程退出了。如果想在程序退出的时候,无论什么原因退出的都要保存一些数据,可以直接套用下方例子代码: