编者荐语:
当业务量快速增长的时候,业务保障平台就要应运而生,预判问题发出告警,越快越好,从宏观到微观一路下钻响应越快越好,尤其是交易量暴涨的高峰时段。怎么做到?看思源的现身说法:
以下文章来源于云纵达摩院 ,作者刘勤红
——业务保障平台性能提升走过的那些路
禧云信息/研发中心/刘勤红(思源) 2019年11月
业务保障平台需从多维度去监控业务的可靠性,快速定位问题并自动解决或推荐出解决方案,所以时效性显得非常重要。接下来我将从以下三个维度展开如何将百亿级数据量监控钻取从十几秒提升到秒级体验:
· 工具升级:OpenTSDB-->Druid
· 调用链收集的优化
· 聚合查询的优化
一、背景回顾
· 禧云的团餐业务发展非常迅速,短短几个月的时间,日交易量就从数百万拉高到千万级,随着调用链追踪得愈发细致,业务数据量也从亿级上升到百亿级别。
· 团餐的高峰交易量比外卖更集中,外卖可以提前预订,而团餐的早中晚就餐非常集中,全国的学生几乎都是一个点儿下课,从下单到吃完也就集中在20-30分钟内,下图0为交易量的曲线图,坡度几乎是直线上升,真是争分夺秒!
图0 团餐高峰期争分夺秒不容有失
在这种高强度业务场景下,业务保障平台应运而生,它主要是多场景多维度实时监控大盘,从设备终端到服务端全链路监控,让技术团队从事后追查和整改,转变为事前预警和快速判定根因。它主要涉及交易、业务调用链路、网络监控、设备运行指标、业务日志等维度数据。架构如下图1所示。
图1 基于OpenTSDB版的大致架构
· 千万级日交易数据:从各业务系统或支付网关回流的交易数据—>交易回流服务—>多维度聚合入库OpenTSDB和ElasticSearch集群;
· 十亿级业务调用链数据:基于Jaeger Trace植入-->Nginx多路分发-->经Jaeger-collector收集到Kafka-->被多组消费者处理(Flink实时写入OpenTSDB,批量写入ES);
· 百亿级网络和设备指标数据:主要是智能设备上报的各种监控指标:基于HTTP/HTTPS层监视-->Nginx-->太空桥(我司IoT平台)设备监控-->批量入库ES。
技术栈为:
· 链路采集:Uber开源的Jaeger
· 基础数据存储:ES
· 指标数据存储:OpenTSDB
· 削峰填谷的消息队列:Kafka
· 实时计算:Flink
二、工具升级:OpenTSDB升级到Druid
在禧云业务保障平台上,OpenTSDB确实没有太好的性能表现,查询数据量在百万级的时候,速度还是可以的,在暑假交易额低谷期并未暴露出OpenTSDB的性能问题。但是进入9月开学季之后,数据延时和查询缓慢的问题立马就暴露出来了,中途也寄希望于升级OpenTSDB版本,但依然效果不佳。
A. OpenTSDB性能表现不佳的原因
第一个原因,Rowkey设计过长的问题。Rowkey第一大设计原则是保证唯一性,否则原先的数据会被覆盖掉,第二大设计原则是长度原则,Rowkey是一个二进制,它的长度建议设计在10~100个字节,越短越好。Rowkey如果过长,对性能有以下影响:
1)HBase的持久化文件HFile是按照KeyValue存储的,如果Rowkey过长,比如500个字节,1000万列数据,光Rowkey就要占用500*1000万=50亿个字节,将近1G数据,这会极大影响HFile的存储效率。
2)MemStore缓存部分数据到内存,如果Rowkey字段过长,内存的有效利用率会降低,系统无法缓存更多的数据,这会降低检索效率。
需要指出的是不仅Rowkey的长度越短越好,列族名、列名等也尽量使用短名字,因为这些名字都是会写入HFile中的,过长的Rowkey、列族名、列名都会导致整体的存储量成倍增加。
第二个原因,业务监控业务场景非常多,涉及到不同业务线的多个维度,比如多机房、多支付渠道、商户/门店/设备/码等多维度聚合,很难设计出一个满足全场景的Rowkey方案。在任意维度的组合查询下,OpenTSDB 查询效率会明显降低。
比如对于组合条件查询使用的就是scan方式,在使用时有以下几点值得注意:
1)通过setCaching、setBatch方法提高速度,以空间换时间;
2)通过setStartRow与setEndRow来限定范围,范围越小,性能越高;
3)通过setFilter方法添加过滤器,这也是分页、多条件查询的基础,比如使用SingleColumnValueFilter。
以上优化当遇到真正海量数据时,会消耗很大的资源,每次都需要花较长的时间处理。
B. 升级为Druid
我司其他技术团队已经试用了Druid, 其核心是通过数据预先聚合提高查询性能,针对预先定义好的Schema,因此适合实时分析的场景,结果返回时间在亚秒级。
我们切换到Druid之后,响应速度上确实有一个数量级的提升,查询千万级的数据范围基本秒级响应。
三、调用链收集的优化
调用链收集的数据流为:jaeger-collector-->Kafka-->jaeger-ingester 消费-->入库ES。下面讲一下如何优化。
A. 第一阶段:Kafka消息堆积高峰期由千万级降到百万级
强调一点,合理的分区设置很重要。
刚开始我们尝试调整每次拉取的消息条数,将ingester.parallelism由1000调整为6000,消息堆积似乎好了一点,但效果不明显。
考虑到可能是因为并发数不够,所以通过扩充Kafka的分区数去提高并发。先将分区数由5个扩到8个,消息堆积由千万级降到百万级。但继续将分区扩到10个,就几乎没有什么效果了。
B. 第二阶段:Kafka的版本选择不可忽视
莫名其妙的是jaeger-ingester消费非常不稳定,频繁与kafka断开重连,尝试去消费其他分区消息,导致消费速率上不去。
后来发现,当Kafka版本为v2.3时,多个jaeger-ingester节点会反复触发kafka的消费再平衡机制,结果导致jaeger-ingester只能单点消费。
所以我们又将Kafka版本回退到了v2.2,调整jaeger-ingester实例个数和Kafka分区数为1:1,可横向扩容支持高并发。
C. 第三阶段:消息堆积高峰期由百万降到万级,延时秒级已可接受
我们观察到ES的负载已经很高了,单节点高峰期CPU负载达到16。之前为了方便定位问题,给网络请求实时加上了traceId标记,调用的是jaeger原生的trace链路计算。现在分析发现查询QPS太高,所以尝试优化查询逻辑,一方面改为自定义的逻辑直接查询ES,一方面调整好批量阈值查询(指的是1次查多少,目前是按时间10ms和数据条数100条一个批次去查ES)。
优化完成后,消息堆积又下降一个数量级。目前高峰期堆积非常少,秒级消费已可接受。
四、聚合查询的优化
A. 散点图 vs 柱状图
对于调用链宏观展示来说,惯常使用散点图,它可以表达出一段时间内请求的耗时分布情况,如下图2所示。
图2 调用链散点图
但是存在一个问题,当时间区间选得比较大的时候,服务端查询的数据过多而响应变慢,客户端要渲染的点太多也会非常卡。所以点要抽样。抽样方式能想得到的有:
a.过滤出耗时长的数据;
b.取最近一段时间的数据;
c.只取指定数量的数据;
d.对相同耗时进行聚合展示等等。
这些调整可谓牺牲了监控者的真实需求,因为有些数据监控者看不了,或者很难看全。
我们对比了阿里云日志服务的设计,他山之石可以攻玉,借鉴了以下两点:
第一点,人们看问题的方式总是从宏观一路下钻到微观,所以我们加了柱状图做为时间段聚合,方便从宏观上看到请求量的规模分布。下图3是某区域的订单趋势柱状图。
图3 订单趋势柱状图
第二点,如果数据量非常大,可继续点击柱图,展开当前时间条件下的子柱状图。系统根据数据量规模,自动展示出散点图,方便用户浏览耗时分布。这样一层层穿刺下钻,既满足了人的操作习惯,又提高了处理速度,做到了秒级响应。
图4柱状图下钻
B. 从宏观到微观,化繁为简
就宏观到微观的监控下钻,下面讲三个案例。
案例一,单一维度下钻
其实多数时候人们只想关注某一维度的分布情况,比如按应用、按工程、按服务IP、按请求URL、按商户、按门店、按设备看分布。对于ES来说,单一条件聚合速度很快,秒级响应。
案例二,网络质量监察
我司在全国大江南北分布着大量餐饮中心(即食堂),如何快速定位出哪些食堂网络环境糟糕呢?不能等客户告诉我们。
我们从请求量级筛选(系统推荐和自定义)、dns/http/ssl/tcp/mqtt耗时情况、趋势发展等诸多因子中分析出餐饮中心网络情况,哪怕是学校食堂的一次网络抖动,都可以被我们侦查到。不仅能分析出网络问题,而且还能下钻到请求链路上的任何阶段,比如是DNS、TCP、SSL、首包等,并分析出受影响的终端设备。依然是秒级响应,如下图5所示。
图5 设备耗时下钻
案例三,找出掉单
当用户已支付而商户未收款时,如何从千万级订单中快速找到丢失的那一笔订单呢?内部称sos订单:
- 秒级侦查出来;
- 快速定位在哪个环节出了问题;
- 系统自我修复能力
具体做法为:
第一步,将内部可能发生的业务场景圈出来,通过Flink实时计算形成闭环,当其中一个链条断了,就会立马把待排查的sos订单压入队列任务,然后不断主动查询第三方支付渠道确认支付状态。
第二步,对于一些疑难问题如短时间内系统无法解决时,比如第三方支付渠道出现了故障,就会发出告警消息给客服,客服预先跟进,减少用户投诉处理时间。
总结一下:
OpenTSDB切换为Druid,明显提升了从宏观下钻到微观的响应时间,基本能做到百亿级数据量秒级响应。高峰时段Kafka消息堆积也大幅降低。下钻方式做了优化,更符合工程师探查习惯。
-完-
欢迎关注公众号:老兵笔记