linux WEB服务器故障两则,兼谈分布式系统

  前段时间公司网站出过两次事故,前台跑PHP的Apache进程大量死锁,造成服务器无负载但不能提供服务,不断重启服务也未能解决问题,但过几小时后自动恢复。这些系统已经稳定运行数月之久,虽然不断有升级但没有出现这类情况。

  一周后第二次出现相同情况时公司组织人力进行了排查,没有发现问题。由于没有WEB机器登录权限,于是让运维同事做了这些操作来查找原因:

  1、strace -p xxxx查看任意httpd进程,查看进程运行状态。如果没有阻塞在futex上,换一个进程再查看。

  这一步一般可以查出很多原因,比如死循环,如果strace没有显示一个系统调用正在进行,一般是限入无终结条件的死循环,这种情况一般伴随着高CPU占用。如果不断有系统调用,就更方便找出死循环的原因了,当然一般也是逻辑错误。

  经过查看,大部分进程阻塞在futex上。记下futex死锁的文件描述符,比如是10。

  2、lsof -p xxxx查看进程打开的文件描述符,找到10对应的资源。结果是这是一个session文件,于是可以推断是session锁导致服务进程挂死。

  记下这个session文件路径,比如是/tmp/abcdefg。

  3、lsof /tmp/abcdefg,查看所有打开这个文件的进程ID。

  4、strace -p xxxx对第三步找出的进程ID进行分析,一般会发现有一个进程没有阻塞在futex上,记下这个进程ID,比如1111,并记下当前阻塞的操作,通常也是个文件描述符,比如是11。或者这个进程也处于死循环,注意观察一下就能发现。

  5、lsof -p 1111查看这个进程,查找11这个文件描述符对应的资源,有可能是个socket连接或者其它,总之这里一般可以找到真正原因了。

  公司的服务器经过排查,原因在于某一时刻网络质量下降,连接到远端另一个socket服务器的主动关闭的包丢失,甚至多次重传也没有成功,所以远端已经无法看到这个连接,但该WEB服务器上却显示ESTABLISHED,PHP代码一直接收socket数据直到断开,自然是无法返回了。很多时候由于安全问题没有办法拿到服务器权限,如何通过简单的办法找出问题就很重要了。

  另一个例子是PHP上传程序,由于使用了分布式存储,数据是通过socket发送到存储服务器上。PHP程序使用stream_set_timeout来设置超时,但某次事故后分析发现它只影响read,对write没有作用,于是改为socket_send,用setsockopt设置超时。

  这种情况应该会经常发生,只是大部分时候网络状况没有问题,也就不想写一些复杂代码处理这种异常情况,或者很多时候认为这只是小事故,不过对于大型网站来说一般是计算影响了多少人、多少时间、造成多少功能无法使用来确定事故等级的。

  事故无大小,应该从事故中吸取经验避免再次发生。公司每周都有超过10个运行事故,我到公司后共造成3个事故,当然主要是刚接管时旧系统架构问题或者库的稳定性造成的,但也有代码BUG造成的。BUG只能减少,要做到完全没有BUG只有理论上可行,而且可能成本过高,相对低成本的是增加容错和报警监控机制,但发现问题还是要及时修复并升级系统,这时候单元测试就很重要了。

  分布式系统简单说就是一组服务器协同工作,主要目的在于均衡负载、均衡数据、冗余等,面对的主要难点在于数据同步、防止宕机造成连锁反应、减少服务器间通讯及依赖、处理任意一个节点缓慢造成任务堆积或失败等方面,还有一点就是不要信任网络,这是最不稳定的部分,即便是同一机房。最近几个月通过改善开发过程、规范单元测试、增加容错机制、消除单点增加冗余、完善监控系统、细化统计分析等方面来提高质量,效果还是明显的,但还有很多路要走。系统设计阶段着眼于大方向上,开发阶段小细节上也需要特别在意,比如日志,系统一旦上线这可能是最丰富稳定的信息源。