一、背景及问题出现原因
项目是移动端服务,考虑到移动网络不稳定、信号不好等因素,采用了一个异步非阻塞框架Vert.x,该框架是基于EventLoop思想实现的,可以看作是java中的node,基本上和node的思想一样,唯一不同的是node的eventLoop线程只有一个,是单线程的,而vert.x的eventLoop线程可以设置多个,与cpu核数有关,感兴趣的具体可以参考vert.x官方文档。
eventLoop线程是用轮训的方式处理事件的,当有事件到达时,eventLoop线程会拿到该事件,并找到对应的事件处理器来响应事件,事件处理器中一定不能阻塞(如读取网络、查询db、读取文件等),一旦阻塞,那么后续的所有请求都会被拒绝,整个系统就会不可用,直到崩溃。官方文档中也明确说明千万不要阻塞eventLoop线程,如果有耗时任务,可以放到work线程中执行。
但是由于历史原因,系统中的所有响应都是在eventLoop线程中执行的,db查询、网络读取等,我们也意识到这样是有问题的,也正在重构整个系统,出现问题就维护性重启,突然在一个周六的晚上,整个系统不能使用,拒绝所有的http请求,造成系统所有模块回滚,经排查,是因为一个服务中httpClient在调用其他部门的服务时,没有设置connectionTimeout,正好那个部门的服务也慢,造成一直阻塞等待,阻塞eventLoop线程,导致服务不可用。
用vert.x框架的初衷是能够更多更快的接收用户请求,结果由于使用不当,造成了严重后果。
二、解决过程
弄清楚问题的原因,解决起来就比较同意了,首先对httpClient的调用的调用超时进行设置,这样当第三方服务慢,或者不可用时,顶多阻塞几秒(设置的超时),而不会一直阻塞下去;其次,将该任务放到work线程中执行,不让其阻塞eventLoop线程。修改后上线没有该问题在出现。所以在使用类httpClient调用http服务时,一定要设置超时时间,而不是让线程一直等待下去。
三、总结
经过这次严重的线上问题,自己对eventLoop思想有了更深一层的认识,也吸取教训,不要存在侥幸心里,不合规矩的使用,问题日积月累,总有一天会把问题放大暴露出来,造成重大的损失。
另外,vert.x是很好的一个技术,相信用好后,会给系统的性能带来质的提升,我也打算写一系列vert.x框架的技术博客,将自己在项目中遇到的坑描述出来,帮助对该框架感兴趣的同学绕过这些坑,一起学习进步。