对七牛Flume+Kafka+SparkStreaming实践经验的总结 2015-08-02 23:30

说明

本文是基于本人的理解,对《七牛是如何搞定每天500亿条日志的》一文的总结。

架构

  • Agent:数据采集
  • Flume:数据路由——数据传输工具
  • Kafka:用于数据生产者与数据消费者之间的解耦——消息中间件
  • SparkStreaming,HDFS:数据消费者,解析日志、过滤、统计.

架构说明

Agent

对日志的采集,采用独立的Agent从系统日志文件中进行采集,而不是由业务模块通过网络直接发送日志出去。因为后者的侵入性太大,一旦日志接收模块故障,会影响业务进程的发送,从而造成日志丢失、甚至对业务产生影响。而从日志文件中采集日志是对业务系统侵入性最小的方式。

Agent应该足够轻。Agent不应该有解析日志、过滤、统计等动作,这些逻辑应该给数据消费者。倘若Agent有较多的逻辑,那不可避免的经常会有升级变更动作。

七牛的Agent采用Go语言开发。

Flume

具体架构上,Agent并没把数据直接发送到Kafka,在Kafka前面有层Flume。这样做有两个原因:

  1. Kafka的API对非JVM系的语言支持很不友好,Flume对外提供更加通用的HTTP接口。
  2. Flume可以做数据路由、Kafka topic定义和Kafka partition key策略等逻辑,进一步减少Agent端的逻辑。

Flume不含状态,完全可以做到水平扩展,不用担心成为瓶颈。Agent按一定规则(round-robin、failover等)来选择Flume实例。

由于Flume层的存储,整个过程中,消息是不保证有序达到数据消费者的。如果业务对顺序性有要求,那得让Agent把数据直接发到Kafka,跳过Flume,并选择好partition key,Kafka只能保证partition级的顺序性。

Flume使用注意点:

  1. memory-channel效率高但可能有丢数据的风险,file-channel安全性高但性能不高。七牛是用memory-channel,但把capacity设置的足够小,使内存中的数据尽可能少,在意外重启和断电时丢的数据很少。七牛比较排斥file-channel,效率是一方面,另一个是对Flume的期望是数据传输,引入file-channel时,它的角色会向存储转变,这在整个流程中是不合适的。通常Flume的sink端是Kafka和HDFS这种可用性和扩张性比较好的系统,不用担心数据拥堵问题。
  2. 默认的HTTP source没有设置线程池,有性能问题,如果有用到,需要修改代码。
  3. 单sink速度跟不上时,需要多个sink。像跨机房数据传输网络延迟高单rpc sink吞吐上不去和HDFS sink效率不高情形,我们在一个channel后会配十多个sink。

Kafka

  1. Topic的划分,大topic对生产者有利且维护成本低,小topic对消费者比较友好。如果是完全不相关的相关数据源且topic数不是发散的,优先考虑分topic。
  2. Kafka的并行单位是partition,partition数目直接关系整体的吞吐量,但partition数并不是越大越高,3个partition就能吃满一块普通硬盘IO了。所以partition数是由数据规模决定,最终还是需要硬盘来抗。
  3. partition key选择不当,可能会造成数据倾斜。在对数据有顺序性要求才需使用partition key。Kafka的producer sdk在没指定partition key时,在一定时间内只会往一个partition写数据,这种情况下当producer数少于partition数也会造成数据倾斜,可以提高producer数目来解决这个问题。

根据实际情况,可以选择数据先汇到本地机房Kafka集群,然后汇聚到核心机房的Kafka,最终供消费者使用。

参考文档

  1. 七牛是如何搞定每天500亿条日志的