锁文件又见死锁--吐槽&记录

       老早时候开发了定时触发的PHP脚本,使用Linux的crontab间隔N分钟通过CURL调用。当时是使用一个文件来作为锁,来避免并发导致多个脚本同时运行,同时项目有负载,而日志目录是NFS挂载的,所以便将锁文件置于日志目录内。基于新需求开发的新版本,已经更换成用Redis来控制并发,旧版本目前来说仍需要维护,然而不仅仅是维护不方便这么简单了。

前面总结过,是如何使用文件锁来阻止PHP脚本并发的。无非就是使用file_exist()函数来判断文件是否存在,再使用register_shutdown_function()确保能执行到unlink()函数(删除锁文件)。


又见死锁

       业务设置了监控,若20分钟未执行业务,会报警。大概在两周前,运行可能2个月都没出过问题的脚本,第一次警告,我一查发现锁文件没被删除,导致后续定时器触发的HTTP请求,全部被拦截。当时的代码版本唯一做了的紧急处理是,检测锁文件生成距离当前时间是否超过1小时,若超过直接强制删除。理论上,业务执行难得超过5分钟。但是问题来了,这个值个人觉得不能设置的太小,若太小在业务量慢慢上来后,难免会出现脚本还在执行,但是锁文件被删的情况。若我之后离职,总不能埋坑,或者说要交代N个代码要随时监控改动。暂时将时间改成了30分钟,这样一来,若产生死锁,也能保证30分钟后锁会被删除,业务重新恢复正常。但是20分钟的不断警告,觉得还是必须得查原因出来。

仔细一查日志,情况略微尴尬。那个时间点的日志未生成,原因在于日志都是先存在内存里,最后通过register_shutdown_function()打印,这个函数注册的方法先是删除锁文件,再将日志从内存输出到文件。

代码执行顺序

  1. 锁文件判断
  2. 查表,获取所有需要被执行的数据
  3. for循环遍历数据
  4. 生成订单号等数据请求第三方平台
  5. 平台返回成功或失败,将订单号等数据写入表中
  6. 遍历完成,清除锁,日志写入

       业务很简单,而register_shutdown_function()锁文件既没删除,日志也没打印。
调用第三方平台后,会有异步回调通知。定位回调日志,发现业务有在执行,也已执行多条,时间节点末尾存在一条异常日志。脚本逻辑是先请求第三方平台后,再将订单号存入数据表内,异常日志提示查询订单号不存在。那么原因应该是出在这里,可能的问题初一想,觉得要么是第三方平台异常,要么是数据库执行失败。

与第三方平台交流后表示,该笔订单在3秒内已经返回。
与运维交流后表示,当时数据库未有异常情况,不存在死锁,同时未查找到此条执行记录。
同时查询TP3.1的Runtime日志和PHP的错误日志,皆无信息。

死锁情况日益严重,一天可能会出现多达3~5次。
继续对代码做调整,先更新订单号,后请求第三方平台,再根据平台返回结果更新订单的状态。另外在for循环内部用try catch包住代码,增加对异常情况的监控,同时在循环底部直接输出日志。

效果不见好转,死锁频现,异常日志也未有捕获。而且发现,原本死锁前业务会执行多条,现在改了后,应该是只执行了一条数据。唯一的改动是调整了日志输出位置,难道和这个有关?

在测试环境发现请求第三方平台时,会存在未及时响应,等待的情况。感觉能发现点异常,就是好事,特地设置了15秒的CURL请求超时时间,测试无误后赶着当天赶忙发布了。再观察发现,还真有超时15秒的。但是仍存在死锁。

刚好听到有同事说NFS挂在的日志目录,在创建文件时提示没有权限。两边不是同一套项目,甚至不在相同服务器上。虽说可能性应该不大,只不过感觉在已经无可考究的情况下,万一是这个原因呢?刚好这套项目不使用负载了,于是将锁文件的位置从日志目录里转移出来,并且在多个地方就去做删除操作,日志输出触发点在代码的最后一步。理论上,即使日志输出异常,锁文件也应该已经被删除。

当天晚上又收到20分钟警报。服务器环境是PHP5.3版本的,如果真的警告无法避免,在我能操作的范围里,只能想到基于PHP5.6版本和TP5.0版本重写代码,来看看代码的异常捕获和PHP的错误日志能否体现。

       现实情况是,业务量并不大,没有从业务的角度上达到需要重写的地步,即使某次触发异常中止了,只要不死锁下一个定时器能正常触发即可。


暂时解决策略

       最后做了一个弥补操作吧,定时器触发的for循环,一个数据执行时间在3~5秒,那么就新增一个配置表,在for循环的头部,先更新一个当前时间。每执行一次,时间就更新一次,然后在判断是否死锁前,先查询该时间距离当前时间是否超过3分钟,若已超过且锁文件也存在,可判断已死锁。

观察数天后,从日志上来看,死锁确有发生,不过保证了死锁文件的超时删除,因此20分钟警报未有发生。


总结

       这篇流水账仅为记录和吐槽,最后想出一个弥补死锁操作暂时用着。目前找不到原因,有点遗憾,希望以后能够深入了解到。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值