Flume进阶

Flume Agent内部原理

1.Source对接数据源,负责封装Event;

2.然后发送给Channel Processor,Channel Processor调用事件拦截器链;

3.将每个事件给Channel Selector事件选择器,选择出要发送的事件;

4.将Channel Selector的结果写入相应的Channel;

5.若多个Sinks组从一个Channel中拉取数据,由SinkProcessor挑选Sinks组中的一个Sink去Channel中读取数据;

即一个时刻只能有一个Sink从Channel中拉取数据;

                                       图-1 Flume Agent内部原理

重要组件:

1)ChannelSelector

ChannelSelector的作用就是选出Event将要被发往哪个Channel。其共有两种类型,分别是Replicating(复制)和Multiplexing(多路复用)。

ReplicatingSelector会将同一个Event发往所有的Channel,Multiplexing会根据相应的原则将不同的Event发往不同的Channel

2)SinkProcessor

SinkProcessor共有三种类型,分别是DefaultSinkProcessorLoadBalancingSinkProcessorFailoverSinkProcessor

DefaultSinkProcessor对应的是单个的Sink,LoadBalancingSinkProcessor和FailoverSinkProcessor对应的是Sink Group,LoadBalancingSinkProcessor可以实现负载均衡的功能,FailoverSinkProcessor可以实现故障转移的功能.

 

Flume拓扑结构:

1.简单串联

图1-3 Flume Agent连接

这种模式是将多个flume顺序连接起来了,从最初的source开始到最终sink传送的目的存储系统。此模式不建议桥接过多的flume数量, flume数量过多不仅会影响传输速率,而且一旦传输过程中某个节点flume宕机,会影响整个传输系统。

多个Agent串联场景

1. 如果AgentA需要将Event对象发送到其他的agent进程中!
        AgentA的sink,必须为AvroSink;其他的agent在接收时,必须选择AvroSource
        
2. 常用组件
①avrosource:  监听一个avro的端口,从另一个avro客户端接收event!
    必须配置:
    type    –    The component type name, needs to be avro
    bind    –    hostname or IP address to listen on
    port    –    Port # to bind to
    
②avrosink: 将event转为avro格式的event发送给指定的主机和端口
    必须配置:
    type    –    The component type name, needs to be avro.
    hostname    –    The hostname or IP address to bind to.
    port    –    The port # to listen on.

 

案例:多agent串联  

在hadoop101,agent1:   netcatsource---memorychannel-- arvosink
    hadoop102,agent2:    avrosource----memorychannel-- loggersink

#agent1
#a1是agent的名称,a1中定义了一个叫r1的source,如果有多个,使用空格间隔
a1.sources = r1
a1.sinks = k1
a1.channels = c1


#source组名名.属性名=属性值
a1.sources.r1.type=netcat
a1.sources.r1.bind=hadoop101
a1.sources.r1.port=44444

#定义sink
a1.sinks.k1.type=avro
a1.sinks.k1.hostname=hadoop102
a1.sinks.k1.port=33333


#定义chanel
a1.channels.c1.type=memory
a1.channels.c1.capacity=1000

#连接组件 同一个source可以对接多个channel,一个sink只能从一个channel拿数据
a1.sources.r1.channels=c1
a1.sinks.k1.channel=c1

--------------------------------------------------------

#agent2
#a1是agent的名称,a1中定义了一个叫r1的source,如果有多个,使用空格间隔
a1.sources = r1
a1.sinks = k1
a1.channels = c1


#source组名名.属性名=属性值
a1.sources.r1.type=avro
a1.sources.r1.bind=hadoop102
a1.sources.r1.port=33333

#定义sink
a1.sinks.k1.type=logger

#定义chanel
a1.channels.c1.type=memory
a1.channels.c1.capacity=1000

#连接组件 同一个source可以对接多个channel,一个sink只能从一个channel拿数据
a1.sources.r1.channels=c1

注:先启动agent2,因为要先绑定端口;

flume-ng agent -n a1  -c conf/  -f  myagent/avrosource-loggersink.conf  -Dflume.root.logger=DEBUG,console

再启动agent1 

flume-ng agent -n a1  -c conf/  -f   myagent/netcatsource-avrosink.conf  -Dflume.root.logger=DEBUG,console

 nc  hadoop101  44444

2.复制和多路复用(Channel Selector)

图1-4 单source,多channel、sink

Flume支持将事件流向一个或者多个目的地。这种模式可以将相同数据复制到多个channel中,或者将不同数据分发到不同的channel中sink可以选择传送不同的目的地

 

Channel Selector选择器:

Channel Selector选择器使用场景:复制和多路复用。

图1-4 单source,多channel、sink

1.Replicating Channel Selector(复制)
        复制的channel选择器,也是默认的选择器!当一个source使用此选择器选择多个channel时,source会将event在每个channel都复制一份
        
        可选的channel:(selector.optional) 向可选的channel写入event时,即便发生异常,也会忽略
        
File Roll Sink
        存储event到本地文件系统!
        必需配置:
        type    –    The component type name, needs to be file_roll.
        sink.directory    –    The directory where files will be stored
        
案例一:复制
                (execsource----memory channel1----avrosink1)------(arvosource----memory channel----loggersink)
                (execsource----memory channel2----avrosink2)------(arvosource----memory channel----filerollsink)

Flume1采用复制ChannelSelector选择器,有两个Channel c1,c2;这时source中的数据,会在两个Channel中各有一份;发送到sink1,sink2中;然后Flume2落HDFS,Flume3落本地(File Roll Sink).

agent1:  在hadoop102  (execsource----memory channel1----avrosink1)/(execsource----memory channel2----avrosink2)
#a1是agent的名称,a1中定义了一个叫r1的source,如果有多个,使用空格间隔
a1.sources = r1
a1.sinks = k1 k2
a1.channels = c1 c2 
#source组名名.属性名=属性值
a1.sources.r1.type=exec
a1.sources.r1.command=tail -f /home/hadoop/test
#声明r1的channel选择器
a1.sources.r1.selector.type = replicating

#定义chanel
a1.channels.c1.type=memory
a1.channels.c1.capacity=1000

a1.channels.c2.type=memory
a1.channels.c2.capacity=1000

##定义sink
a1.sinks.k1.type=avro
a1.sinks.k1.hostname=hadoop101
a1.sinks.k1.port=33333

a1.sinks.k2.type=avro
a1.sinks.k2.hostname=hadoop103
a1.sinks.k2.port=33333

#连接组件 同一个source可以对接多个channel,一个sink只能从一个channel拿数据!
a1.sources.r1.channels=c1 c2
a1.sinks.k1.channel=c1
a1.sinks.k2.channel=c2

-------------------------------------------------------

#agent2在hadoop101  -(arvosource----memory channel----loggersink)
a1.sources = r1
a1.sinks = k1
a1.channels = c1
#组名名.属性名=属性值
a1.sources.r1.type=avro
a1.sources.r1.bind=hadoop101
a1.sources.r1.port=33333


#定义sink
a1.sinks.k1.type=logger

#定义chanel
a1.channels.c1.type=memory
a1.channels.c1.capacity=1000

#连接组件 同一个source可以对接多个channel,一个sink只能从一个channel拿数据!
a1.sources.r1.channels=c1
a1.sinks.k1.channel=c1

-------------------------------------------------------

#agent3 在hadoop103  (arvosource----memory channel----filerollsink)
a1.sources = r1
a1.sinks = k1
a1.channels = c1
#source组名名.属性名=属性值
a1.sources.r1.type=avro
a1.sources.r1.bind=hadoop103
a1.sources.r1.port=33333

#定义sink
a1.sinks.k1.type=file_roll
a1.sinks.k1.sink.directory=/home/hadoop/flumefile


#定义chanel
a1.channels.c1.type=memory
a1.channels.c1.capacity=1000

a1.sources.r1.channels=c1
a1.sinks.k1.channel=c1

2.Multiplexing Channel Selector(多路复用)

Multiplexing Channel Selector根据evnet header中属性,参考用户自己配置的映射信息,将event发送到指定的channel中;

header的值,可使用拦截器interceptors增加;

如:

a1.sources = r1
a1.channels = c1 c2 c3 c4
a1.sources.r1.selector.type = multiplexing
a1.sources.r1.selector.header = state
a1.sources.r1.selector.mapping.CZ = c1
a1.sources.r1.selector.mapping.US = c2 c3
a1.sources.r1.selector.default = c4

r1中每个event根据header中key为state的值,进行选择,如果state=CZ,这类event发送到c1,如果state=US,这类event发送到c2,c3;state=其他,发送到c4

 

案例二:多路复用

agent1:  在hadoop102
#a1是agent的名称,a1中定义了一个叫r1的source,如果有多个,使用空格间隔
a1.sources = r1
a1.sinks = k1 k2
a1.channels = c1 c2 
#source组名名.属性名=属性值
a1.sources.r1.type=exec
a1.sources.r1.command=tail -f /home/hadoop/test
#声明r1的channel选择器
a1.sources.r1.selector.type = multiplexing
a1.sources.r1.selector.header = state
a1.sources.r1.selector.mapping.CZ = c1
a1.sources.r1.selector.mapping.US = c2

#使用拦截器为event加上某个header
a1.sources.r1.interceptors = i1
a1.sources.r1.interceptors.i1.type = static
a1.sources.r1.interceptors.i1.key = state
a1.sources.r1.interceptors.i1.value = CZ


#定义chanel
a1.channels.c1.type=memory
a1.channels.c1.capacity=1000

a1.channels.c2.type=memory
a1.channels.c2.capacity=1000

##定义sink
a1.sinks.k1.type=avro
a1.sinks.k1.hostname=hadoop101
a1.sinks.k1.port=33333

a1.sinks.k2.type=avro
a1.sinks.k2.hostname=hadoop103
a1.sinks.k2.port=33333

#连接组件 同一个source可以对接多个channel,一个sink只能从一个channel拿数据!
a1.sources.r1.channels=c1 c2
a1.sinks.k1.channel=c1
a1.sinks.k2.channel=c2

-------------------------------------------------------

#运行在hadoop101
a1.sources = r1
a1.sinks = k1
a1.channels = c1
#source组名名.属性名=属性值
a1.sources.r1.type=avro
a1.sources.r1.bind=hadoop101
a1.sources.r1.port=33333


#定义sink
a1.sinks.k1.type=logger

#定义chanel
a1.channels.c1.type=memory
a1.channels.c1.capacity=1000

#连接组件 同一个source可以对接多个channel,一个sink只能从一个channel拿数据!
a1.sources.r1.channels=c1
a1.sinks.k1.channel=c1

-------------------------------------------------------

#运行在hadoop103
a1.sources = r1
a1.sinks = k1
a1.channels = c1
#组名名.属性名=属性值
a1.sources.r1.type=avro
a1.sources.r1.bind=hadoop103
a1.sources.r1.port=33333

#定义sink
a1.sinks.k1.type=logger


#定义chanel
a1.channels.c1.type=memory
a1.channels.c1.capacity=1000

a1.sources.r1.channels=c1
a1.sinks.k1.channel=c1

 

SinkProcessor:(使用场景负载均衡或故障转移

SinkProcessor共有三种类型,分别是DefaultSinkProcessorLoadBalancingSinkProcessorFailoverSinkProcessor

DefaultSinkProcessor对应的是单个的Sink,LoadBalancingSinkProcessor和FailoverSinkProcessor对应的是Sink Group,LoadBalancingSinkProcessor可以实现负载均衡的功能,FailoverSinkProcessor可以实现故障转移的功能。

 

1.Default Sink Processor
        如果agent中,只有一个sink,默认就使用Default Sink Processor,这个sink processor是不强制用户,将sink组成一个组!
        如果有多个sink,多个sink对接一个channel,不能选择Default Sink Processor
        
2.Failover Sink Processor
        Failover Sink Processor维护了一个多个sink的有优先级的列表,按照优先级保证,至少有一个sink是可以干活的!
        如果根据优先级发现,优先级高的sink故障了,故障的sink会被转移到一个故障的池中冷却
        在冷却时,故障的sink也会不管尝试发送event,一旦发送成功,此时会将故障的sink再移动到存活的池中
        
        必需配置:
        sinks – Space-separated list of sinks that are participating in the group 
        processor.type default The component type name, needs to be failover 
        processor.priority.<sinkName> – Priority value. <sinkName> must be one of the sink instances associated with the current sink group A higher priority value Sink gets activated earlier. A larger absolute value indicates higher priority 

3.Load balancing sink processor

       负载均衡的sink processor,维持了sink组中active状态的sink!使用round_robin或random算法,来分散sink组织的存活的sink之间的负载

        processor.sinks – Space-separated list of sinks that are participating in the group 
        processor.type default The component type name, needs to be load_balance

 

图1-6 Flume负载均衡或故障转移

案例一:故障转移  


        agent1:   execsource--memorychannel----avrosink1--------agent2: avroSource---memorychannel----loggersink
                       execsource--memorychannel----avrosink2--------agent3: avroSource---memorychannel----loggersink
                                            
       agent1对应两个sink;avrosink1的优先级高优先被Failover Sink Processor选中,此时只有agent2可以输出event
                一旦 agent2挂掉,此时avrosink1故障,由Failover Sink Processor选择剩下avrosink2干活

注:只要你用的不是一个sink,即默认的sink;多个sink时都需要配置sink group组。                
配置:
-----------------------hadoop102--agent1------------------
#a1是agent的名称,a1中定义了一个叫r1的source,如果有多个,使用空格间隔
a1.sources = r1
a1.sinks = k1 k2
a1.channels = c1

a1.sinkgroups = g1
a1.sinkgroups.g1.sinks = k1 k2
a1.sinkgroups.g1.processor.type = failover
a1.sinkgroups.g1.processor.priority.k1=100
a1.sinkgroups.g1.processor.priority.k2=90

#source组名名.属性名=属性值
a1.sources.r1.type=exec
a1.sources.r1.command=tail -f /home/hadoop/test
#source声明r1的channel选择器
a1.sources.r1.selector.type = replicating

#定义chanel
a1.channels.c1.type=memory
a1.channels.c1.capacity=1000

##定义sink
a1.sinks.k1.type=avro
a1.sinks.k1.hostname=hadoop101
a1.sinks.k1.port=33333

a1.sinks.k2.type=avro
a1.sinks.k2.hostname=hadoop103
a1.sinks.k2.port=33333

#连接组件 同一个source可以对接多个channel,一个sink只能从一个channel拿数据!
a1.sources.r1.channels=c1
a1.sinks.k1.channel=c1
a1.sinks.k2.channel=c1

----------------------hadoop101----agent2------------------
a1.sources = r1
a1.sinks = k1
a1.channels = c1
#source组名名.属性名=属性值
a1.sources.r1.type=avro
a1.sources.r1.bind=hadoop101
a1.sources.r1.port=33333


#定义sink
a1.sinks.k1.type=logger

#定义chanel
a1.channels.c1.type=memory
a1.channels.c1.capacity=1000

#连接组件 同一个source可以对接多个channel,一个sink只能从一个channel拿数据!
a1.sources.r1.channels=c1
a1.sinks.k1.channel=c1

----------------------hadoop103----agent3------------------
a1.sources = r1
a1.sinks = k1
a1.channels = c1
#组名名.属性名=属性值
a1.sources.r1.type=avro
a1.sources.r1.bind=hadoop103
a1.sources.r1.port=33333


#定义sink
a1.sinks.k1.type=logger

#定义chanel
a1.channels.c1.type=memory
a1.channels.c1.capacity=1000

#连接组件 同一个source可以对接多个channel,一个sink只能从一个channel拿数据!
a1.sources.r1.channels=c1
a1.sinks.k1.channel=c1
        
案例二:负载均衡

配置:
-----------------------hadoop102--agent1------------------
#a1是agent的名称,a1中定义了一个叫r1的source,如果有多个,使用空格间隔
a1.sources = r1
a1.sinks = k1 k2
a1.channels = c1

a1.sinkgroups = g1
a1.sinkgroups.g1.sinks = k1 k2
a1.sinkgroups.g1.processor.sinks=k1 k2
a1.sinkgroups.g1.processor.type = load_balance
#组名名.属性名=属性值
a1.sources.r1.type=exec
a1.sources.r1.command=tail -f /home/hadoop/test
#声明r1的channel选择器
a1.sources.r1.selector.type = replicating

#定义chanel
a1.channels.c1.type=memory
a1.channels.c1.capacity=1000

##定义sink
a1.sinks.k1.type=avro
a1.sinks.k1.hostname=hadoop101
a1.sinks.k1.port=33333

a1.sinks.k2.type=avro
a1.sinks.k2.hostname=hadoop103
a1.sinks.k2.port=33333

#连接组件 同一个source可以对接多个channel,一个sink只能从一个channel拿数据!
a1.sources.r1.channels=c1
a1.sinks.k1.channel=c1
a1.sinks.k2.channel=c1

----------------------hadoop101----agent2------------------
a1.sources = r1
a1.sinks = k1
a1.channels = c1
#组名名.属性名=属性值
a1.sources.r1.type=avro
a1.sources.r1.bind=hadoop101
a1.sources.r1.port=33333


#定义sink
a1.sinks.k1.type=logger

#定义chanel
a1.channels.c1.type=memory
a1.channels.c1.capacity=1000

#连接组件 同一个source可以对接多个channel,一个sink只能从一个channel拿数据!
a1.sources.r1.channels=c1
a1.sinks.k1.channel=c1

----------------------hadoop103----agent3------------------
a1.sources = r1
a1.sinks = k1
a1.channels = c1
#组名名.属性名=属性值
a1.sources.r1.type=avro
a1.sources.r1.bind=hadoop103
a1.sources.r1.port=33333


#定义sink
a1.sinks.k1.type=logger

#定义chanel
a1.channels.c1.type=memory
a1.channels.c1.capacity=1000

#连接组件 同一个source可以对接多个channel,一个sink只能从一个channel拿数据!
a1.sources.r1.channels=c1
a1.sinks.k1.channel=c1

  

聚合

hadoop102上的Flume-1监控文件/opt/module/group.log,

hadoop103上的Flume-2监控某一个端口的数据流,

Flume-1与Flume-2将数据发送给hadoop104上的Flume-3,Flume-3将最终数据打印到控制台。

     flume1和flume2的sink都是flume3的source;

# Describe the sink

a1.sinks.k1.type = avro

a1.sinks.k1.hostname = hadoop104

a1.sinks.k1.port = 4141

# Describe/configure the source

a2.sources.r1.type = netcat

a2.sources.r1.bind = hadoop103

a2.sources.r1.port = 44444

 

# Describe/configure the source

a3.sources.r1.type = avro

a3.sources.r1.bind = hadoop104

a3.sources.r1.port = 4141

 

Flume事务

Flume如何保证不丢数据

1.只要不用的是异步Source;

2.在Channel满的情况下,缓存Channel的数据;

3.因为在Flume中有实物机制,所以不会丢数据;

Flume中的事务:

1.source向channel中放数据的put事务

Put事务流程

do put 将数据先写入临时缓冲区putList;

do Commit 检查channel内存队列是否足够合并用的;

do Rollackchannel 内存队列空间不足,回滚数据;

2.sink从channel中取数据take事务

Take事务流程

do Take 将数据取到临时缓冲区takeList,并将数据发送到HDFS;

do Commit 若数据全部发送成功,则清除临时缓冲区takeList;

do Rollback 数据发送过程中若发现异常,rollback将临时缓冲区takeList中的数据归还给channel内存队列;

 

事务

1. 数量关系
        batchSize:  每个Source和Sink都可以配置一个batchSize的参数。
                这个参数代表一次性到channel中put|take 多少个event!
                batchSize <=  transactionCapacity
                  
        transactionCapacity: putList和takeList的初始值!
        
        capacity: channel中存储event的容量大小!
                transactionCapacity <=  capacity
                  
2. 概念
    putList:  source在向channel放入数据时的缓冲区
                putList在初始化时,需要根据一个固定的size初始化,这个size在channel中设置
                在channel中,这个size由参数transactionCapacity决定
   
    put事务流程:source将封装好的event,先放入到putList中,放入完成后,
                一次性commit(),这批event就可以写入到channel!
                写入完成后,清空putList,开始下一批数据的写入
                
                假如一批event中的某些event在放入putList时发生了异常,此时
                要执行rollback(),rollback()直接清空putList。而缓冲区内存一般一定会够的,因为有batchSize <=  transactionCapacity
                
    takeList: sink在向channel拉取数据时的缓冲区
    
    take事务流程:  sink不断从channel中拉取event,没拉取一个event,这个event会先放入takeList中
                  当一个batchSize的event全部拉取到takeList中之后,此时由sink执行写出处理!
                  假如在写出过程中,发送了异常,此时执行回滚!将takeList中所有的event全部回滚到channel!
                  反之,如果写出没有异常执行commit(),清空takeList!

 

自定义Source

Source是负责接收数据到Flume Agent的组件。Source组件可以处理各种类型、各种格式的日志数据,包括avro、thrift、exec、jms、spooling directory、netcat、sequence generator、syslog、http、legacy。我们也可以自己定义。

https://flume.apache.org/FlumeDeveloperGuide.html#source根据官方说明自定义MySource需要继承AbstractSource类实现Configurable和PollableSource接口

实现相应方法:

getBackOffSleepIncrement()//暂不用

getMaxBackOffSleepInterval()//暂不用

configure(Context context)//初始化context(读取配置文件内容)

process()//获取数据封装成event并写入channel,这个方法将被循环调用。

使用场景:读取MySQL数据或者其他文件系统。

/*
 * 需求:使用flume接收数据,并给每条数据添加前缀,输出到控制台。前缀可从flume配置文件中配置
 */
public class MySource  extends AbstractSource implements Configurable, PollableSource {

    private String prefix;
    // 最核心方法,在process()中,创建Event,将event放入channel
    // Status{ READY, BACKOFF}
    // READY: source成功第封装了event,存入到channel,返回READY
    // BACKOFF: source无法封装了event,无法存入到channel,返回BACKOFF
    // process()方法会被Source所在的线程循环调用
    @Override
    public Status process() throws EventDeliveryException {
        Status status=Status.READY;        
        //封装event
        List<Event> datas=new ArrayList<>();
        
        for (int i = 0; i < 10; i++) {     

            //创建事件
            SimpleEvent e = new SimpleEvent();
            
            //向body中封装数据
            e.setBody((prefix+"hello"+i).getBytes());            
            datas.add(e);            
        }
              
        try {            
            Thread.sleep(5000);
             // 获取当前source对象对应的channelprocessor
            ChannelProcessor cp = getChannelProcessor(); 

             //将数据放入channel                  
            cp.processEventBatch(datas);
            
        } catch (Exception e) {            
            status=Status.BACKOFF;            
            e.printStackTrace();
        }
        
        return status;
    }

    // 当source没有数据可封装时,会让source所在的线程先休息一会,休息的时间,由以下值*计数器系数
    @Override
    public long getBackOffSleepIncrement() {
        return 2000;
    }

    @Override
    public long getMaxBackOffSleepInterval() {
        return 5000;
    }

    // 从配置中来读取信息
    @Override
    public void configure(Context context) {
        //从配置文件中读取key为prefix的属性值,如果没有配置,提供默认值atguigu:
        prefix=context.getString("prefix", "atguigu:");        
    }

}

 

1.将写好的代码打包,并放到flume的lib目录(/opt/module/flume)下。

2.创建配置文件:

a1.sources = r1
a1.sinks = k1
a1.channels = c1
#组名名.属性名=属性值
a1.sources.r1.type=com.test.flume.custom.MySource
a1.sources.r1.prefix=test:

#定义sink
a1.sinks.k1.type=logger

#定义chanel
a1.channels.c1.type=memory
a1.channels.c1.capacity=1000

#连接组件 同一个source可以对接多个channel,一个sink只能从一个channel拿数据!
a1.sources.r1.channels=c1
a1.sinks.k1.channel=c1

3.启动任务

bin/flume-ng agent -c conf/ -f job/mysource.conf -n a1 -Dflume.root.logger=INFO,console
4.结果展示       

自定义Interceptor

要实现Interceptor接口;

// 为每个event的header中添加key-value:  time=时间戳
public class MyInterceptor implements Interceptor{

    //初始化
    @Override
    public void initialize() {
        
    }

    //拦截处理方法
    // 为每个event的header中添加key-value:  time=时间戳
    @Override
    public Event intercept(Event event) {        
        Map<String, String> headers = event.getHeaders();
       
        headers.put("time", System.currentTimeMillis()+"");        
        return event;
    }

    //拦截处理方法
    @Override
    public List<Event> intercept(List<Event> events) {        
        for (Event event : events) {
            intercept(event);
        }
        return events;
    }

    // 结合时调用的方法
    @Override
    public void close() {
        
    }
    
    //额外提供一个内部的Builder,因为Flume在创建拦截器对象时固定调用Builder来获取
    public static class Builder implements Interceptor.Builder{

        // 读取配置文件中的参数
        @Override
        public void configure(Context context) {            
        }

        //返回一个当前的拦截器对象
        @Override
        public Interceptor build() {
            return new MyInterceptor();
        }      
    }

}
 

配置文件:

a1.sources=r1

a1.sinks=k1

a1.channels=c1

#source配置

a1.sources.r1.type=com.test.flume.MySource

a1.sources.r1.prefix=test:

a1.sources.r1.interceptors = i1

a1.sources.r1.interceptors.i1.type = com.text.flume.MyInterceptor$Builder

#sink

a1.sinks.k1.type = logger

#channel

a1.channels.c1.type = memory

a1.channels.c1.capacity = 1000

#连接

a1.sources.r1.channels=c1

a1.sinks.k1.channels=c1

 

自定义sink

Sink不断地轮询Channel中的事件批量地移除它们,并将这些事件批量写入到存储或索引系统、或者被发送到另一个Flume Agent

Sink是完全事务性的。在从Channel批量删除数据之前,每个Sink用Channel启动一个事务。批量事件一旦成功写出到存储系统或下一个Flume Agent,Sink就利用Channel提交事务。事务一旦被提交,该Channel从自己的内部缓冲区删除事件

Sink组件目的地包括hdfs、logger、avro、thrift、ipc、file、null、HBase、solr、自定义。官方提供的Sink类型已经很多,但是有时候并不能满足实际开发当中的需求,此时我们就需要根据实际需求自定义某些Sink。

官方也提供了自定义sink的接口:

https://flume.apache.org/FlumeDeveloperGuide.html#sink根据官方说明自定义MySink需要继承AbstractSink类实现Configurable接口

实现相应方法:

configure(Context context)//初始化context(读取配置文件内容)

process()//从Channel读取获取数据(event),这个方法将被循环调用。

使用场景:读取Channel数据写入MySQL或者其他文件系统。

//需求: 从配置文件中读取一个后缀,将event的内容读取后,拼接后缀进行输出
public class MySink extends AbstractSink implements Configurable {
    private String suffix;
    
    private Logger logger=LoggerFactory.getLogger(MySink.class);

    //核心方法:处理sink逻辑
    // Status.ready:  成功传输了一个或多个event
    // Status.backoff:  从channel中无法获取数据
    @Override
    public Status process() throws EventDeliveryException {     

        //声明返回值状态信息
        Status status=Status.READY;
        
        //获取当前sink对接的channel
        Channel c = getChannel();
        
        //声明Event,用来接收chanel中的event
        Event e=null;
        //获取take事务对象
        Transaction transaction = c.getTransaction();
                               
        //开启事务
        transaction.begin();
            
        //如果channel中,没有可用的event,此时e会是null           

        //读取Channel中的事件,直到读取到事件结束循环

        while (true) {

               event = ch.take();

               if (event != null) {

                    break;

          }

           
        try {           
           //取到数据后,执行拼接后缀进行输出
           logger.info(new String(e.getBody())+suffix);
            //提交事务
            transaction.commit(); 

            status=Status.READY;          
        } catch (ChannelException e1) {            
            //回滚事务
            transaction.rollback();
            
            status=Status.BACKOFF;
            
            e1.printStackTrace();
        }finally {        
            //关闭事务对象
            transaction.close();            
        }
        
        return status;
    }

    //从配置中读取配置的参数
    @Override
    public void configure(Context context) {        
        suffix=context.getString("suffix", ":hi");
    }

}

打包:

将写好的代码打包,并放到flume的lib目录(/opt/module/flume)下。

配置文件:

# Name the components on this agent

a1.sources = r1

a1.sinks = k1

a1.channels = c1

# Describe/configure the source

a1.sources.r1.type = netcat

a1.sources.r1.bind = localhost

a1.sources.r1.port = 44444

# Describe the sink

a1.sinks.k1.type = com.atguigu.MySink

#a1.sinks.k1.prefix = atguigu:

a1.sinks.k1.suffix = :atguigu

# Use a channel which buffers events in memory

a1.channels.c1.type = memory

a1.channels.c1.capacity = 10000

a1.channels.c1.transactionCapacity = 1000

# Bind the source and sink to the channel

a1.sources.r1.channels = c1

a1.sinks.k1.channel = c1

开启任务:

bin/flume-ng agent -c conf/ -f job/mysink.conf -n a1 -Dflume.root.logger=INFO,console

执行命令:

nc localhost 44444

hello

OK

atguigu

OK

 

Flume数据流监控

1.如何实现监控
    在使用flume期间,我们需要监控什么?
        channel当前的容量是多少?
        channel当前已经使用了多少容量?    若总是很满,那就需要增加。
        source向channel中put成功了多少个event?
        sink从channel中take成功了多少个event?  让sink取快点,多设几个sink,增加sink内存

2. JMX
        J2EE定义了14种技术规范!
            JDBC: java连接数据库的技术规范!
            Servlet:  所有javaweb写的程序,都最终使用Servlet完成请求的接受和响应
            JMX(java monitor extension): java的监控扩展模块
                    JMX可以帮助我们实时监控一个java进程中需要了解的参数,可以实时修改java进程中某个对象的参数
                    
                ①MBean(monitor bean): 监控的参数封装的Bean
                ②JMX的monitor服务,这个服务可以在程序希望获取到MBean参数时,来请求服务,请求后服务帮我们
                    对MBean的参数进行读写!                    
                    flume已经提供了基于JMX的服务实现,如果我们希望使用,只需要启动此服务即可!
                ③客户端,客户端帮我们来向JMX服务发送请求,显示服务返回的Mbean的结果
                
3. 客户端
①使用JCONSOLE程序查看
        在flume的conf/env.sh文件中,配置
        export JAVA_OPTS=”-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=5445 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false

启动:flume-ng agent -n a1 -c conf/ -f myagents/MySource.conf -Dflume.root.logger=DEBUG,console

②使用web浏览器向JMX服务发请求查看
        使用JSON Reporting

        启动:
        bin/flume-ng agent --conf-file example.conf --name a1 -Dflume.monitoring.type=http -Dflume.monitoring.port=34545

③使用第三方框架,例如Ganglia
        可视化MBean:  内置一个可以处理Http请求的服务器(PHP)
                            ganglia-web(选集群的一台机器安装)
        数据库:  需要把采集到的MBean的信息写入到数据库,在查询时,从数据库查询,返回结果
                            ganglia-gmetad(选集群的一台机器)负责将每台机器ganglia-gmond收集的数据汇总,汇总后存入数据库rrdtool
        收集数据的服务:  需要在每个机器都部署一个收集本台机器上运行的进程的指标的服务,此服务
                        将收集的指标数据汇总到数据库中,由PHP程序来查询
                            ganglia-gmond(需要采集哪个机器的指标,就在哪个机器安装)负责监控MBean,采集数据
                            
        
开源方案:  ①需要有一个可以请求JMX服务的框架  JMXTrans
                    ②需要有一个数据库(时序数据库最佳),数据来存储采集到的信息  Influxdb
                    ③可视化框架来显示指标   Graffna

 

Flume常用面试

Flume的Source,Sink,Channel的作用?你们Source是什么类型?

 1、作用

(1)Source组件是专门用来收集数据的,可以处理各种类型、各种格式的日志数据,包括avro、thrift、exec、jms、spooling directory、netcat、sequence generator、syslog、http、legacy

(2)Channel组件对采集到的数据进行缓存,可以存放在Memory或File中。

(3)Sink组件是用于把数据发送到目的地的组件,目的地包括Hdfs、Logger、avro、thrift、ipc、file、Hbase、solr、自定义。

2、我公司采用的Source类型为:

(1)监控后台日志:exec

(2)监控后台产生日志的端口:netcat

Exec  spooldir,实时taildir

Flume的Channel Selectors

Channel Selectors:复制,多路复用

Flume参数调优

1. Source

增加Source个(使用Tair Dir Source时可增加FileGroups个数)可以增大Source的读取数据的能力。例如:当某一个目录产生的文件过多时需要将这个文件目录拆分成多个文件目录,同时配置好多个Source 以保证Source有足够的能力获取到新产生的数据。

batchSize参数决定Source一次批量运输到Channel的event条数,适当调大这个参数可以提高Source搬运Event到Channel时的性能。

2. Channel 

type 选择memory时Channel的性能最好,但是如果Flume进程意外挂掉可能会丢失数据。type选择file时Channel的容错性更好,但是性能上会比memory channel差。

使用file Channel时dataDirs配置多个不同盘下的目录可以提高性能。

Capacity 参数决定Channel可容纳最大的event条数。transactionCapacity 参数决定每次Source往channel里面写的最大event条数和每次Sink从channel里面读的最大event条数。transactionCapacity需要大于Source和Sink的batchSize参数。

3. Sink 

增加Sink的个数可以增加Sink消费event的能力。Sink也不是越多越好够用就行,过多的Sink会占用系统资源,造成系统资源不必要的浪费。增加sink同时,也要增加channel;否则还是只有一个sink从channel里拿数据。

batchSize参数决定Sink一次批量从Channel读取的event条数,适当调大这个参数可以提高Sink从Channel搬出event的性能。

Flume的事务机制

Flume的事务机制(类似数据库的事务机制):Flume使用两个独立的事务分别负责从Soucrce到Channel,以及从Channel到Sink的事件传递。比如spooling directory source 为文件的每一行创建一个事件,一旦事务中所有的事件全部传递到Channel且提交成功,那么Soucrce就将该文件标记为完成。同理,事务以类似的方式处理从Channel到Sink的传递过程,如果因为某种原因使得事件无法记录,那么事务将会回滚。且所有的事件都会保持到Channel中,等待重新传递。

Flume采集数据会丢失吗?

根据Flume的架构原理,Flume是不可能丢失数据的,其内部有完善的事务机制,Source到Channel是事务性的,Channel到Sink是事务性的,因此这两个环节不会出现数据的丢失,唯一可能丢失数据的情况是Channel采用memoryChannel,agent宕机导致数据丢失,或者Channel存储数据已满,导致Source不再写入,未写入的数据丢失。

Flume不会丢失数据,但是有可能造成数据的重复,例如数据已经成功由Sink发出,但是没有接收到响应,Sink会再次发送数据,此时可能会导致数据的重复

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值