之前产品线上发生过若干次因为tomcat连接池被耗尽而导致宕机的故障,而具体根源原因则各不尽相同。有因为调用和被调用的服务申请相同的分布式锁而导致死锁的,有因为发送内部或外部的JMS消息发生堵塞的,有因为某个存在性能问题的接口被较多调用导致的,还有某些超高频接口没有做好专门优化而导致的。。。
所有上述问题的本质解决,肯定是要针对各种问题根源,分别予以解决。解决死锁问题,外部接口做好严格的访问超时控制,非核心业务逻辑尽量异步处理,尽可能的通过增加cache来减少数据库压力等等。但除此之外,系统中任何一个业务点都可能拖垮整个系统,系统整体的脆弱程度值得深思。为什么瘸了腿的人会死?不,他应该还要能编程。
其实,这个问题的本质在于tomcat只有一个共享线程池,所有的业务请求都会分配给一个线程,直至请求处理完毕,线程才会被回收。任何一个长请求,都会长期侵占导致宝贵的tomcat连接池资源。这点其实在tomcat7上已经做了优化支持,tomcat7支持了异步处理。此特性将http线程池和业务service线程池做了分离,这样一个IO等待的业务请求,只会侵占业务service线程池。但这没有解决本质问题,因为虽然解决了http线程池的资源问题,但累积膨胀的业务service线程池,对jvm内存、cpu,数据库等仍是一个威胁。坏孩子和调皮的孩子其实放到哪里都是问题,他们需要被好好管理。需要控制他们的发言次数,这样哪些无辜的乖孩子才有机会发言,老师也才能听到有意义的反馈。
那么,我们需要做什么呢?
1.我们需要做好资源隔离,防止任何一个业务功能消耗过多的连接池资源,甚至,在必要的时候,可以完全禁止这个问题业务功能。
2.业务功能的流量阈值或开关,能够及时、动态的予以调整。
3.业务功能如果流量超限,可以定制的做异常处理。
解决问题一,我们设计了一个Aspect。它拦截了所有的业务功能请求,然后根据具体业务功能的流量阈值对请求进行控制。如果未超限,则放行,放行前增加计数,业务功能执行完成后回收计数;如果超限,则获取相应的异常处理策略进行处理。主要包含了流量计数器,阈值管理器和异常处理器这3个部分。
框架简图如下:
处理流程图如下:
解决问题二和三,即阈值和处理策略动态调整的问题。显然,配置文件是不合适的,那意味着要做应用重启。将配置信息保存到memcache中?那意味着每个业务请求都需要访问至少2次网络请求,这是不小的开销。那看来,如果能将这些信息冗余在服务器节点的内存里会性能不错,那内容如何获取和更新呢?服务器节点应该初始化的时候从一个中心节点拉取配置,然后通过长连接的方式监听在中心节点的配置变更时间,在发生变更新,从中心节点拉取相应新内容。中心节点最好还能是高可用的,高性能的,以及拥有良好的一致性。听起来美好,要真开发起来可还真不简单。幸运的是,牛人们早已为我们铺好大路了,那就是Zookeeper!它从核心上完美地解决了我们的需求。不过,到这里,其实还不够。我们的配置需要能持久保存,如果zookeeper真的挂了怎么办?最好有个漂亮的配置管理界面,你会喜欢在一个黑乎乎的界面上疯狂的敲打键盘吗,那是黑客帝国的剧情。此外,最好还能有非常方便的客户端编程接口API,不,甚至不要给我任何API,我添加几个注解就一切搞定最好,好吧,我们程序员就是懒!~
让我们来看看开源的配置管理服务框架吧,diamond(阿里),disconf(百度),qconf(奇虎360)。随都是师出名门,但由于研发年代、测重点都会略有不同。先看看Diamond和Disconf的对比:
推拉模型和编程模型上,disconf都更加符合我们的需求。可能后来者好是因为能站在前面巨人的肩膀上。下图的disconf的核心流程图:
更完整的的关于disconf的信息请大家看:
https://github.com/knightliao/disconf/wiki/%E5%88%86%E5%B8%83%E5%BC%8F%E9%85%8D%E7%BD%AE%E7%AE%A1%E7%90%86%E5%B9%B3%E5%8F%B0Disconf 。
至于qconf,基本特性同disconf差不多,当相关文档比较少,也没有特别详细,也就没有考虑了。感兴趣的同学可以看下:
https://github.com/Qihoo360/QConf/blob/master/README_ZH.md ,和http://v.youku.com/v_show/id_XOTI1NTI2ODI0.html(视频教程)
现在增加到并发拦截监控到公司监控系统。下图是一个某段时间内的监控结果。
本文章为作者原创
??禁止??
其他公众账号转载,若有转载,请标明出处