Storm实践

业务场景:

读取nginx日志,根据$HOST、$PATH、$Cluste、$APP来统计PV,UV,错误码,响应时间等


进程模型:

Spout: 读取日志,解析日志,根据配置的规则对日志过滤和分类,同时对记录打上分钟为单位的标签$TIME

=>根据$PATH  Hash,emit日志对象

Bolt_Class1:根据配置规则,对日志的不同维度进行PV,UV等等汇总,汇总的维度默认的$HOST,$PATH,$APP,$CLUSTER,$TIME,也可以自定义的比如$CONTENT_TYPE, $METHOD,$TIME等

=>根据$APP  Hash,emit汇总对象($APP是所有的维度的必选字段)

Bolt_Class2:

根据维度汇总(这里的汇总主要是对非空PATH 也统计到根路径下),然后以入库的单位进行排序,只保留topN,然后批量入库


程序的关键点---时间敏感:

我们是以分钟为单位来进行信息的统计,因此,有个消息确认和超时机制。

Spout时间切换,发送运维消息到它的每个下流:   Task1_Bolt_Class1=>[$TIME, LOG_CNT], Task2_Bolt_Class1=>[$TIME, LOG_CNT]

TaskBolt_Class1收到的$TIME的消息满足SUM([$TIME, LOG_CNT]),则往每个下流发Task1_Bolt_Class2=>[$TIME, CALC_CNT], Task2_Bolt_Class2=>[$TIME, CALC_CNT]

TaskBolt_Class2收到的$TIME的消息满足SUM([$TIME, CALC_CNT]),就开始入库

当系统异常导致消息丢失或者超时时,也将结束等待(后面的超时消息将被忽略),往下流发运维消息或者入库。


需求+高性能的实现我们的需求:

1.  关闭Storm的Acker(我们无需重发超时的消息,而且对少量数据丢失不敏感),使用我们自己对时间敏感的运维消息+超时的确认机制。(实现上,都会保存time=>taskids=>cnt的关系,触发切换时,就根据taskid,发其相应的运维消息[time, cnt])

2. 我们的系统运行开销主要是在日志的解析和过滤,因此spout要增多,比例是spout(24), blot1(9), blot2(6)

3. 不要在回调函数中做耗时操作,比如操作DB。因为它们是运行在调度线程中,这里耗时的话,将会导致消息处理慢,从而导致消息超时统计数据不准确

4. 自定义的schedule,支持按照数据库中不同机器的不同weight配置来调度spout(24), blot1(9), blot2(6),另外也可以实现die all / work all的系统要求(这个要求的原因有:让代码每台机器只运行在一个worker里,自动重拉时会分配到不同的worker中,从而占用系统比较珍贵的port资源同时增加了通讯开销)

5. 每个机器上只使用一个worker,这样避免了我们消息的跨进程传输数量


Storm帮我们做的:

1. 基于对象的消息通讯: 可以基于指定字段HASH来保证消息自动汇聚;避免我们装包,拆包,连接,重连等一系列操作;可以通过taskid对不同的下流发送消息

2. 管道配置:Spout、Bolt1、Bolt2、、、、可以直接通过配置来实现

3. 所有的回调都在同一个线程中,这样我们就避免了各种锁操作,减少系统的复杂度

4. 可配置系统超时消息tick: 这样及时我们没有收到消息,也可以有机会做那些超时操作,大大简化了编码
5. 系统的toppology可以获取到,也能通过其客户端接口进行操作,这样可以将部分控制逻辑写在代码里,比如我们设计的平滑升级(新的系统在分钟切换的时候接管老的系统)


目前系统的吞吐量:

高峰: Spout(日志27GB),  emit 300W;   Bolt_Class1, emit 21W;   Bolt_Class2 

日常:高峰的1/3

你可能感兴趣的:(Storm实践)