基于业务功能级别的流量控制

阿凡达2018-06-28 15:03
 之前产品线上发生过若干次因为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的对比:


淘宝Diamond Disconf
比较
数据持久性 存储在mysql上 存储在mysql上
都持久化到数据库里,都易于管理
推拉模型 拉模型,每隔15s拉一次全量数据 基于Zookeeper的推模型,实时推送
disconf基于分布式的Zookeeper来实时推送,不断是在稳定性、实效性、易用性上均优于diamond
配置读写 支持实例对配置读写。支持某台实例写配置数据,并广播到其它实例上 只支持实例对配置读。通过在disconf-web上更新配置到达到广播写到所有应用实例
从目前的应用场景来看,实例对配置的写需求不是那么明显。disconf支持的中心化广播方案可能会与人性思考更加相似。
容灾 多级容灾模式,配置数据会dump在本地,避免中心服务挂机时无法使用 多级容灾模式,优先读取本地配置文件。
双方均支持在中心服务挂机时配置实例仍然可以使用
配置数据模型 只支持KV结构的数据,非配置文件模式 支持传统的配置文件模式(配置文件),亦支持KV结构数据(配置项)
使用配置文件的编程方式可能与程序员的编程习惯更为相似,更易于接受和使用。
编程模型 需要将配置文件拆成多个配置项,没有明显的编程模型 在使用配置文件的基础上,提供了注解式和基于XML的两种编程模型

并发性 多条配置要同时生效时,无法解决并发同时生效的问题 基于注解式的配置,可以解决并发性问题


推拉模型和编程模型上,disconf都更加符合我们的需求。可能后来者好是因为能站在前面巨人的肩膀上。下图的disconf的核心流程图:


更完整的的关于disconf的信息请大家看:
至于qconf,基本特性同disconf差不多,当相关文档比较少,也没有特别详细,也就没有考虑了。感兴趣的同学可以看下:
现在增加到并发拦截监控到哨兵系统。下图是一个某段时间内的监控结果。

本文来自网易实践者社区,经作者方金德授权发布。