https://mp.weixin.qq.com/s/pZadyJ3YUYK3Q30w8dTymw
CAT 是大众点评开源的一个基于 Trace 的应用监控系统。2014 年底,携程开始引入 CAT 并落地。在这几年里,我们的数据量一直呈现着爆发式的增长,在这期间我们不断地对 CAT 做了很多大大小小的优化,使得我们的机器数目并没有像数据量那样呈现出指数级的增加。到目前为止,我们已经有超过 7 万的客户端,每天处理的消息树已经超过了 8000 亿,每天处理的日志量超过 4 万亿行,峰值流量达到 1.5 亿行 / 秒。
CAT 客户端的监控数据会组装成一种树状结构,也就是 CAT 里面的 MessageTree,然后发送到 CAT 服务端。CAT 服务端会把这份数据同时分发给多个不同用途的报表分析器进行实时计算,计算出来的结果会被存到服务端的内存报表里面。
那么我们的报表是什么样子的呢?这里是一个 CAT 里面最基本的 Transaction 报表的截图,我们可以把它简单理解成一段时间内的一些监控指标的聚合。这个例子里面展示的是 RPCService 这个 Transaction 在这一小时内每一分钟发生的次数、平均耗时、每分钟的失败以及这个小时的平均耗时,99 线、95 线。
1)CAT 线程模型
CAT 客户端的监控数据发送到服务端后,服务端会同时将这份数据分发给多个不同用途的报表分析器,报表分析器内部会根据这个监控数据的客户端信息(APP ID 和 IP)计算一个哈希值,然后再分发到报表分析器内部的一个队列里面,这个队列后面会绑定一个线程进行实时分析,计算出来的结果会放到这个线程绑定的内存报表中。这种模型有一个好处,就是它把数据跟线程绑定在一起,使得对同一个客户端过来的监控数据的实时分析、计算和内存报表的更新都是无锁的。
2)遇到的问题
随着流量不断增加,我们发现其实不同的应用和客户端发送过来的数据是非常不均的,数据的不均就会导致队列堆积,最终的结果就是某些堆积的队列对应的客户端监控数据丢失。对于数据不均的问题,天然的我们就会想到通过增加更多的队列,让哈希算法更加均匀就可以解决。
但是,在这种模型里面,增加队列同时意味着也要增加对应的处理线程。一开始我们一直是用这个方法去解决数据不均的问题,直到有一次我们发现当前的队列以及处理线程已经太多了,再加下去甚至还会出现处理能力的下降。
3)分析问题
于是我们就开始去分析为什么处理能力反而下降了。查看监控指标之后,我们发现操作系统的上下文切换已经达到了每秒钟几百万次之多。这时候我们就赶紧去看一下服务端的线程,发现经过我们前面的一顿操作,最后所有的报表加在一起已经有了几千个线程,非常的多。于是我们回过头来审视我们的模型,当数据不均的时候,我们的核心需求是要通过增加队列来将数据进一步的打散,为什么我们的处理线程也要随之增加呢?
所以,我们第一个想法就是把这模型里面的队列和线程做个解耦。
在谈及队列和线程解耦的模型时,我们的第一想法就是 IO 模型。我们可以看看现在的线程模型,它跟 IO 里面最老的 BIO 模型是不是非常相似?它们都是每个队列或者 channel 后面有一个线程在阻塞等待数据的到来,然后去计算,所以会有非常多的线程。每个 channel 即使没有数据到达也需要有个线程在阻塞等待,非常浪费。
BIO 模型往下的一个演进就是 NIO 模型。NIO 通过引入一个 Selector 模块, 很轻量地就可以监听非常多的队列数据。于是我们就想,是不是也类似这样,通过引入一个 Selector 去监听多个队列来达到队列跟线程解耦的目的呢?