博客园  :: 首页  :: 联系 :: 管理

flume 架构设计优化

Posted on 2018-01-31 00:13  天戈朱  阅读(1122)  评论(0编辑  收藏  举报

对于企业中常用的flume type 概括如下:
ource(获取数据源):

  • exec (文件)
  • spoolingdir (文件夹)
  • taildir(文件夹及文件的变动)
  • kafka
  • syslog
  • http

channel(管道):

  • mem
  • file
  • kafka

sink(将channel中的 数据发送到目标地址):

  • hdfs
  • hive
  • hbase
  • ES 

从集群可用性,可靠性,可扩展性和兼容性等方面,对架构优化进行设计。

1、可用性(availablity)


可用性(availablity)指固定周期内系统无故障运行总时间。要想提高系统的可用性,就需要消除系统的单点,提高系统的冗余度。 

1.1  Agent死掉

  • 机器死机
  • Agent进程死掉
    1. 所有的Agent在supervise的方式下启动,如果进程死掉会被系统立即重启,以提供服务。
    2. 所有的Agent进行存活监控,发现Agent死掉立即报警。
    3. 对于非常重要的日志,建议应用直接将日志写磁盘,Agent使用spooldir的方式获得最新的日志。

1.2 负载均衡与故障转移

  • 模块分解详解图

1.3 Hdfs正常停机

  • 在Collector的HdfsSink中提供了开关选项,可以控制Collector停止写Hdfs,并且将所有的events缓存到FileChannel的功能。

1.4 Hdfs异常停机或不可访问

  • 假如Hdfs异常停机或不可访问,此时Collector无法写Hdfs。由于我们使用DualChannel,Collector可以将所收到的events缓存到FileChannel,保存在磁盘上,继续提供服务。当Hdfs恢复服务以后,再将FileChannel中缓存的events再发送到Hdfs上。这种机制类似于Scribe,可以提供较好的容错性

1.5 Collector变慢或者Agent/Collector网络变慢

  • 如果Collector处理速度变慢(比如机器load过高)或者Agent/Collector之间的网络变慢,可能导致Agent发送到Collector的速度变慢。同样的,对于此种情况,我们在Agent端使用DualChannel,Agent可以将收到的events缓存到FileChannel,保存在磁盘上,继续提供服务。当Collector恢复服务以后,再将FileChannel中缓存的events再发送给Collector。

1.6 Hdfs变慢

  • 当Hadoop上的任务较多且有大量的读写操作时,Hdfs的读写数据往往变的很慢。由于每天,每周都有高峰使用期,所以这种情况非常普遍。对于Hdfs变慢的问题,我们同样使用DualChannel来解决。当Hdfs写入较快时,所有的events只经过MemChannel传递数据,减少磁盘IO,获得较高性能。当Hdfs写入较慢时,所有的events只经过FileChannel传递数据,有一个较大的数据缓存空间。 

2、可靠性(reliability)


   可靠性(reliability)是指Flume在数据流的传输过程中,保证events的可靠传递

   对Flume来说,所有的events都被保存在Agent的Channel中,然后被发送到数据流中的下一个Agent或者最终的存储服务中。那么一个Agent的Channel中的events什么时候被删除呢?当且仅当它们被保存到下一个Agent的Channel中或者被保存到最终的存储服务中。这就是Flume提供数据流中点到点的可靠性保证的最基本的单跳消息传递语义。

   那么Flume是如何做到上述最基本的消息传递语义呢?

   首先,Agent间的事务交换。Flume使用事务的办法来保证event的可靠传递。Source和Sink分别被封装在事务中,这些事务由保存event的存储提供或者由Channel提供。这就保证了event在数据流的点对点传输中是可靠的。在多级数据流中,如下图,上一级的Sink和下一级的Source都被包含在事务中,保证数据可靠地从一个Channel到另一个Channel转移。

    其次,数据流中 Channel的持久性。Flume中MemoryChannel是可能丢失数据的(当Agent死掉时),而FileChannel是持久性的,提供类似mysql的日志机制,保证数据不丢失。

 

3、可扩展性(scalability)


  可扩展性(scalability)是指系统能够线性扩展。当日志量增大时,系统能够以简单的增加机器来达到线性扩容的目的。对于基于Flume的日志收集系统来说,需要在设计的每一层,都可以做到线性扩展地提供服务。下面将对每一层的可扩展性做相应的说明。

  3.1 Agent层

  • 对于Agent这一层来说,每个机器部署一个Agent,可以水平扩展,不受限制。一个方面,Agent收集日志的能力受限于机器的性能,正常情况下一个Agent可以为单机提供足够服务。另一方面,如果机器比较多,可能受限于后端Collector提供的服务,但Agent到Collector是有Load Balance机制,使得Collector可以线性扩展提高能力。 

  3.2 Collector层

  • 对于Collector这一层,Agent到Collector是有Load Balance机制,并且Collector提供无差别服务,所以可以线性扩展。其性能主要受限于Store层提供的能力。

  3.3 Store层

  • 对于Store这一层来说,Hdfs和Kafka都是分布式系统,可以做到线性扩展。Bypass属于临时的应用,只对应于某一类日志,性能不是瓶颈。

4、Channel的选择


  Flume提供常用的MemoryChannel和FileChannel优缺点相反,分别有自己适合的场景。然而,对于大部分应用来说,我们希望Channel可以同提供高吞吐和大缓存。基于此,美团开发了DualChannel。

  • DualChannel:基于 MemoryChannel和 FileChannel开发。当堆积在Channel中的events数小于阈值时,所有的events被保存在MemoryChannel中,Sink从MemoryChannel中读取数据; 当堆积在Channel中的events数大于阈值时, 所有的events被自动存放在FileChannel中,Sink从FileChannel中读取数据。这样当系统正常运行时,我们可以使用MemoryChannel的高吞吐特性;当系统有异常时,我们可以利用FileChannel的大缓存的特性。

5、系统监控


  5.1  发送速度,拥堵情况,写Hdfs速度

  5.2  flume写hfds状态的监控

  5.3 日志大小异常监控

  • 对于重要的日志,我们会每个小时都监控日志大小周同比是否有较大波动,并给予提醒,这个报警有效的发现了异常的日志,且多次发现了应用方日志发送的异常,及时给予了对方反馈,帮助他们及早修复自身系统的异常。

6、美团优化经验参考


6.1 Flume的问题总结

  • Channel“水土不服”:使用固定大小的MemoryChannel在日志高峰时常报队列大小不够的异常;使用FileChannel又导致IO繁忙的问题;
  • HdfsSink的性能问题:使用HdfsSink向Hdfs写日志,在高峰时间速度较慢;
  • 系统的管理问题:配置升级,模块重启等;

6.2 增加Zabbix monitor服务

    Flume本身提供了http, ganglia的监控服务,而我们目前主要使用zabbix做监控。因此,我们为Flume添加了zabbix监控模块,和sa的监控服务无缝融合。

    另一方面,净化Flume的metrics。只将我们需要的metrics发送给zabbix,避免 zabbix server造成压力。目前我们最为关心的是Flume能否及时把应用端发送过来的日志写到Hdfs上, 对应关注的metrics为:

  • Source : 接收的event数和处理的event数
  • Channel : Channel中拥堵的event数
  • Sink : 已经处理的event数

6.3 增加DualChannel

     flumee本身提供了MemoryChannel和FileChannel。MemoryChannel处理速度快,但缓存大小有限,且没有持久化;FileChannel则刚好相反。我们希望利用两者的优势,在Sink处理速度够快,Channel没有缓存过多日志的时候,就使用MemoryChannel,当Sink处理速度跟不上,又需要Channel能够缓存下应用端发送过来的日志时,就使用FileChannel,由此我们开发了DualChannel,能够智能的在两个Channel之间切换。

6.4 增加NullChannel

   Flume提供了NullSink,可以把不需要的日志通过NullSink直接丢弃,不进行存储。然而,Source需要先将events存放到Channel中,NullSink再将events取出扔掉。为了提升性能,我们把这一步移到了Channel里面做,所以开发了NullChannel。

6.5 Flume系统调优经验总结

  • HdfsSink中默认的serializer会每写一行在行尾添加一个换行符,我们日志本身带有换行符,这样会导致每条日志后面多一个空行,修改配置不要自动添加换行符; lc.sinks.sink_hdfs.serializer.appendNewline = false
  • 调大MemoryChannel的capacity,尽量利用MemoryChannel快速的处理能力;
  • 调大HdfsSink的batchSize,增加吞吐量,减少hdfs的flush次数;
  • 适当调大HdfsSink的callTimeout,避免不必要的超时错误;

6.6  HdfsSink获取Filename的优化

  • HdfsSink的path参数指明了日志被写到Hdfs的位置,该参数中可以引用格式化的参数,将日志写到一个动态的目录中。这方便了日志的管理。例如我们可以将日志写到category分类的目录,并且按天和按小时存放:

    lc.sinks.sink_hdfs.hdfs.path = /user/hive/work/orglog.db/%{category}/dt=%Y%m%d/hour=%H
  • dfsS ink中处理每条event时,都要根据配置获取此event应该写入的Hdfs path和filename,默认的获取方法是通过正则表达式替换配置中的变量,获取真实的path和filename。因为此过程是每条event都要做的操作,耗时很长。通过我们的测试,20万条日志,这个操作要耗时6-8s左右。

  • 由于我们目前的path和filename有固定的模式,可以通过字符串拼接获得。而后者比正则匹配快几十倍。拼接定符串的方式,20万条日志的操作只需要几百毫秒。

6.7 日志管理系统:图形化的展示和控制日志收集系统 

 

7、监控 


 7.1 Flume主要有以下集中监控方式:

  • JMX监控
  • HTTP监控
  • Ganglia监控
  • 自定义监控

 7.2 metrics 指标

  •  
    {
    	"SOURCE.src-1":{
    		"OpenConnectionCount":"0",		//目前与客户端或sink保持连接的总数量(目前只有avro source展现该度量)
    		"Type":"SOURCE",					
    		"AppendBatchAcceptedCount":"1355",	//成功提交到channel的批次的总数量
    		"AppendBatchReceivedCount":"1355",	//接收到事件批次的总数量
    		"EventAcceptedCount":"28286",	        //成功写出到channel的事件总数量,且source返回success给创建事件的sink或RPC客户端系统
    		"AppendReceivedCount":"0",		//每批只有一个事件的事件总数量(与RPC调用中的一个append调用相等)
    		"StopTime":"0",			        //source停止时自Epoch以来的毫秒值时间
    		"StartTime":"1442566410435",	        //source启动时自Epoch以来的毫秒值时间
    		"EventReceivedCount":"28286",	        //目前为止source已经接收到的事件总数量
    		"AppendAcceptedCount":"0"		//单独传入的事件到Channel且成功返回的事件总数量
    	},
    	"CHANNEL.ch-1":{
    		"EventPutSuccessCount":"28286",	        //成功写入channel且提交的事件总数量
    		"ChannelFillPercentage":"0.0",	        //channel满时的百分比
    		"Type":"CHANNEL",
    		"StopTime":"0",			        //channel停止时自Epoch以来的毫秒值时间
    		"EventPutAttemptCount":"28286",	        //Source尝试写入Channe的事件总数量
    		"ChannelSize":"0",			//目前channel中事件的总数量
    		"StartTime":"1442566410326",	        //channel启动时自Epoch以来的毫秒值时间
    		"EventTakeSuccessCount":"28286",	//sink成功读取的事件的总数量
    		"ChannelCapacity":"1000000",            //channel的容量
    		"EventTakeAttemptCount":"313734329512"  //sink尝试从channel拉取事件的总数量。这不意味着每次事件都被返回,因为sink拉取的时候channel可能没有任何数据
    	},
    	"SINK.sink-1":{
    		"Type":"SINK",
    		"ConnectionClosedCount":"0",	        //下一阶段或存储系统关闭的连接数量(如在HDFS中关闭一个文件)
    		"EventDrainSuccessCount":"28286",	//sink成功写出到存储的事件总数量
    		"KafkaEventSendTimer":"482493",    
    		"BatchCompleteCount":"0",		//与最大批量尺寸相等的批量的数量
    		"ConnectionFailedCount":"0",	        //下一阶段或存储系统由于错误关闭的连接数量(如HDFS上一个新创建的文件因为超时而关闭)
    		"EventDrainAttemptCount":"0",	        //sink尝试写出到存储的事件总数量
    		"ConnectionCreatedCount":"0",	        //下一个阶段或存储系统创建的连接数量(如HDFS创建一个新文件)
    		"BatchEmptyCount":"0",		        //空的批量的数量,如果数量很大表示souce写数据比sink清理数据慢速度慢很多
    		"StopTime":"0",			
    		"RollbackCount":"9",			//
    		"StartTime":"1442566411897",
    		"BatchUnderflowCount":"0"		//比sink配置使用的最大批量尺寸更小的批量的数量,如果该值很高也表示sink比souce更快
    	}
    	}

      

小结


  • 可用性监控:Agent状态、负载均衡、故障转移
  • NullChannel、DuelChannel
  • 热插拔扩展与维护

参考资料