3.Flume进阶
3.1 Flume事务
3.2 Flume Agent内部原理
3.2.1 ChannelSelector
ChannelSelector的作用就是选出Event将要被发往哪个Channel。其共有两种类型,分别是Replicating(复制)和Multiplexing(多路复用)。
ReplicatingSelector会将同一个Event发往所有的Channel,Multiplexing会根据相应的原则,将不同的Event发往不同的Channel。
3.2.2 SinkSelector
SinkProcessor共有三种类型,分别是DefaultSinkProcessor、LoadBalancingSinkProcessor和FailoverSinkProcessor
DefaultSinkProcessor对应的是单个的Sink,LoadBalancingSinkProcessor和FailoverSinkProcessor对应的是Sink Group,LoadBalancingSinkProcessor可以实现负载均衡的功能,FailoverSinkProcessor可以错误恢复的功能。
3.2.3 Interceptors
Flume中的拦截器(interceptor),用户Source读取events发送到Sink的时候,在events header中加入一些有用的信息,或者对events的内容进行过滤,完成初步的数据清洗。这在实际业务场景中非常有用,Flume-ng 1.6中目前提供了以下拦截器:
Timestamp Interceptor;
Host Interceptor;
Static Interceptor;
UUID Interceptor;
Morphline Interceptor;
Search and Replace Interceptor;
Regex Filtering Interceptor;
Regex Extractor Interceptor;
eg 没有加拦截器:
创建interceptors.conf
a1.sources = r1
a1.channels = c1
a1.sinks = k1
a1.sources.r1.type = netcat
a1.sources.r1.bind = hadoop102
a1.sources.r1.port = 44444
a1.channels.c1.type = memory
a1.channels.c1.capacity = 1000
a1.sinks.k1.type = logger
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
启动flume向hadoop102:44444发送数据
headers字段为空
Timestamp Interceptor:
a1.sources = r1
a1.channels = c1
a1.sinks = k1
a1.sources.r1.type = netcat
a1.sources.r1.bind = hadoop102
a1.sources.r1.port = 44444
#设置一个拦截器(用来向headers中添加时间戳)
a1.sources.r1.interceptors = i1
a1.sources.r1.interceptors.i1.type = timestamp
a1.channels.c1.type = memory
a1.channels.c1.capacity = 1000
a1.sinks.k1.type = logger
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
header字段为当前时间戳。
3.3 Flume拓扑结构
3.3.1 简单串联
这种模式是将多个flume顺序连接起来了,从最初的source开始到最终sink传送的目的存储系统。此模式不建议桥接过多的flume数量, flume数量过多不仅会影响传输速率,而且一旦传输过程中某个节点flume宕机,会影响整个传输系统。
3.3.2 复制和多路复用
Flume支持将事件流向一个或者多个目的地。这种模式可以将相同数据复制到多个channel中,或者将不同数据分发到不同的channel中,sink可以选择传送到不同的目的地。
3.3.3 负载均衡和故障转移
Flume支持使用将多个sink逻辑上分到一个sink组,sink组配合不同的SinkProcessor可以实现负载均衡和错误恢复的功能。
3.3.4 聚合
这种模式是我们最常见的,也非常实用,日常web应用通常分布在上百个服务器,大者甚至上千个、上万个服务器。产生的日志,处理起来也非常麻烦。用flume的这种组合方式能很好的解决这一问题,每台服务器部署一个flume采集日志,传送到一个集中收集日志的flume,再由此flume上传到hdfs、hive、hbase等,进行日志分析。
3.4 开发案例
3.4.1 简单串联
案例需求
监听hadoop102 22222端口,将内容输出到hadoop103 33333端口。
实现
1)分别编写conf文件
hadoop102上的netcatsource_avrosink.conf
#agent1(hadoop102) netcatsource --> memorychannel --> arvosink
a1.sources = r1
a1.channels = c1
a1.sinks = k1
a1.sources.r1.type = netcat
a1.sources.r1.bind = hadoop102
a1.sources.r1.port = 22222
a1.channels.c1.type = memory
a1.channels.c1.capacity = 1000
a1.sinks.k1.type = avro
#hostname是将数据写到的那台机器
a1.sinks.k1.hostname = hadoop103
a1.sinks.k1.port = 33333
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
hadoop103上的avrosource_loggersink.conf
#注意:先启动hadoop103因为hadoop103接收hadoop102的数据的
#agent2(hadoop103) avrosource ---> memorychannel ----> loggersink
a1.sources = r1
a1.channels = c1
a1.sinks = k1
a1.sources.r1.type = avro
a1.sources.r1.bind = hadoop103
a1.sources.r1.port = 33333
a1.channels.c1.type = memory
a1.channels.c1.capacity = 1000
a1.sinks.k1.type = logger
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
2)分别启动两台机器上的flume监听(先hadoop103)
flume-ng agent -n a1 -c conf/ -f datas2/avrosource_loggersink.conf -Dflume.root.logger=INFO,console
flume-ng agent -n a1 -c conf/ -f datas2/netcatsource_avrosink.conf -Dflume.root.logger=INFO,console
3)向hadoop102端口发送数据
hadoop103 33333端口接收
3.4.1 复制和多路复用
案例需求
使用Flume-1监控文件变动,Flume-1将变动内容传递给Flume-2,Flume-2负责存储到HDFS。同时Flume-1将变动内容传递给Flume-3,Flume-3负责输出到Local FileSystem。
实现复制
1)分别在hadoop102,103,104上创建agent1.conf,agent2.conf,agent3.conf
agent1.conf
#agent1(hadoop102)
a1.sources = r1
a1.channels = c1 c2
a1.sinks = k1 k2
a1.sources.r1.type = exec
a1.sources.r1.command = tail -F /opt/module/flume/demo/123.log
#配置channelSelector - replicating(复制-默认不配也可以)
#a1.sources.r1.selector.type = replicating
a1.channels.c1.type = memory
a1.channels.c2.type = memory
a1.sinks.k1.type = avro
a1.sinks.k1.hostname = hadoop103
a1.sinks.k1.port = 33333
a1.sinks.k2.type = avro
a1.sinks.k2.hostname = hadoop104
a1.sinks.k2.port = 44444
#一个source对接两个channel
a1.sources.r1.channels = c1 c2
a1.sinks.k1.channel = c1
a1.sinks.k2.channel = c2
agent2.conf:
#agent2为了便于观察以loggersink形式代替hdfssink
#agent2(hadoop103)
a1.sources = r1
a1.channels = c1
a1.sinks = k1
a1.sources.r1.type = avro
a1.sources.r1.bind = hadoop103
a1.sources.r1.port = 33333
a1.channels.c1.type = memory
a1.channels.c1.capacity = 1000
a1.sinks.k1.type = logger
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
agent3.conf
#agent3(hadoop104)
a1.sources = r1
a1.channels = c1
a1.sinks = k1
a1.sources.r1.type = avro
a1.sources.r1.bind = hadoop104
a1.sources.r1.port = 44444
a1.channels.c1.type = memory
a1.channels.c1.capacity = 1000
#a1.sinks.k1.type = logger
#将event数据存储到本地磁盘上
a1.sinks.k1.type = file_roll
#event存放的目录
a1.sinks.k1.sink.directory = /opt/module/flume/demo
#多久时间滚动一个新文件(30秒)
a1.sinks.k1.sink.rollInterval = 30
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
2)启动,先启动hadoop102与hadoop104监听端口
flume-ng agent -n a1 -c conf/ -f datas2/agent2.conf -Dflume.root.logger=INFO,console(hadoop103)
flume-ng agent -n a1 -c conf/ -f datas2/agent3.conf -Dflume.root.logger=INFO,console(hadoop102)
flume-ng agent -n a1 -c conf/ -f datas2/agent1.conf -Dflume.root.logger=INFO,console(hadoop104)
3)读取情况
hadoop103:
hadoop104:
实现复用
在agent1.conf中添加
#复用
a1.sources.r1.selector.type = multiplexing
#event(headers | body)根据headers中的key和value进行数据的发送
#state指的是headers,key的值
a1.sources.r1.selector.header = state
#CZ指的是key对应的value值那么就发送到c1
a1.sources.r1.selector.mapping.CZ = c1
#US指的是key对应的value值那么就发送到c2
a1.sources.r1.selector.mapping.US = c2
#需求:给event中的headers添加数据
#static拦截器可以给所有的eventheaders设置我们自定义的key和value
a1.sources.r1.interceptors = i1
a1.sources.r1.interceptors.i1.type = static
#设置key值
a1.sources.r1.interceptors.i1.key = state
#设置value值
a1.sources.r1.interceptors.i1.value = CZ
使用拦截器interceptors添加event中的header字段,按照header字段的不同,channel selector选择对应的channel传输
如上写法将全部传输给hadoop103
添加之后重新启动
hadoop103:
hadoop104中没有监听到
3.4.2 负载均衡和故障转移
故障转移:
案例需求
使用Flume1监控一个端口,其sink组中的sink分别对接Flume2和Flume3,采用FailoverSinkProcessor,实现故障转移的功能。
需求分析
实现:
1)分别在hadoop上创建agent1.conf,agent2.conf,agent3.conf
agent1.conf
#agent1(hadoop102)
a1.sources = r1
a1.channels = c1
a1.sinks = k1 k2
a1.sources.r1.type = netcat
a1.sources.r1.bind = hadoop102
a1.sources.r1.port = 22222
#一个channel对应多个sink时要设置一个sinkgroups
a1.sinkgroups = g1
#该sink组有哪些sink的实例
a1.sinkgroups.g1.sinks = k1 k2
#配置sinkProcessor的类型①failover故障转移 ②load_balance负载均衡
a1.sinkgroups.g1.processor.type = failover
#配置sink的优先级数值越大优先级越高
a1.sinkgroups.g1.processor.priority.k1 = 5
a1.sinkgroups.g1.processor.priority.k2 = 10
a1.channels.c1.type = memory
a1.sinks.k1.type = avro
a1.sinks.k1.hostname = hadoop103
a1.sinks.k1.port = 33333
a1.sinks.k2.type = avro
a1.sinks.k2.hostname = hadoop104
a1.sinks.k2.port = 44444
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
a1.sinks.k2.channel = c1
agent2.conf,agent3.conf同案例3.4.1
2)分别启动三台机器上的flume
3)向hadoop102:22222发送数据,可以看到在agent2,agent3都正常运行的时候,会优先由hadoop:44444输出
当agent3挂掉的时候,会故障转移到hadoop103:33333输出。完成故障转移
当agent3冲新启动的时候会重新交由hadoop104:44444输出
负载均衡
配置agent1.conf中的sinkProcessor的类型为负载均衡。
#agent1(hadoop102)
a1.sources = r1
a1.channels = c1
a1.sinks = k1 k2
a1.sources.r1.type = netcat
a1.sources.r1.bind = hadoop102
a1.sources.r1.port = 22222
#定义一个sink组
a1.sinkgroups = g1
#指明sink组中的sink实例
a1.sinkgroups.g1.sinks = k1 k2
#设置sinkProcessor的类型(负载均衡)
a1.sinkgroups.g1.processor.type = load_balance
#①random-随机分配 ②round_robin-轮循
a1.sinkgroups.g1.processor.selector = random
a1.channels.c1.type = memory
a1.sinks.k1.type = avro
a1.sinks.k1.hostname = hadoop103
a1.sinks.k1.port = 33333
a1.sinks.k2.type = avro
a1.sinks.k2.hostname = hadoop104
a1.sinks.k2.port = 44444
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
a1.sinks.k2.channel = c1
分别启动,向hadoop102:22222发送数据
可以看到输出是随机分配到hadoop103:33333 hadoop104:44444上。
3.4.3 聚合
案例需求
hadoop102上的Flume-1监控文件/opt/module/group.log,
hadoop103上的Flume-2监控某一个端口的数据流,
Flume-1与Flume-2将数据发送给hadoop104上的Flume-3,Flume-3将最终数据打印到控制台。
需求分析
即保证flume1,flume2的sink端口与flume3的source端口保持一致。
实现
1)分别编写agnet1.conf, agent2.conf, agent3.conf
添加如下内容
agent1.conf:
# Name the components on this agent
a1.sources = r1
a1.sinks = k1
a1.channels = c1
# Describe/configure the source
a1.sources.r1.type = exec
a1.sources.r1.command = tail -F /opt/module/group.log
a1.sources.r1.shell = /bin/bash -c
# Describe the sink
a1.sinks.k1.type = avro
a1.sinks.k1.hostname = hadoop104
a1.sinks.k1.port = 44444
# Describe the channel
a1.channels.c1.type = memory
a1.channels.c1.capacity = 1000
a1.channels.c1.transactionCapacity = 100
# Bind the source and sink to the channel
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
agent2.conf
# Name the components on this agent
a2.sources = r1
a2.sinks = k1
a2.channels = c1
# Describe/configure the source
a2.sources.r1.type = netcat
a2.sources.r1.bind = hadoop103
a2.sources.r1.port = 44444
# Describe the sink
a2.sinks.k1.type = avro
a2.sinks.k1.hostname = hadoop104
a2.sinks.k1.port = 33333
# Use a channel which buffers events in memory
a2.channels.c1.type = memory
a2.channels.c1.capacity = 1000
a2.channels.c1.transactionCapacity = 100
# Bind the source and sink to the channel
a2.sources.r1.channels = c1
a2.sinks.k1.channel = c1
agent3.conf
# Name the components on this agent
a3.sources = r1
a3.sinks = k1
a3.channels = c1
# Describe/configure the source
a3.sources.r1.type = avro
a3.sources.r1.bind = hadoop104
a3.sources.r1.port = 44444
# Describe the sink
# Describe the sink
a3.sinks.k1.type = logger
# Describe the channel
a3.channels.c1.type = memory
a3.channels.c1.capacity = 1000
a3.channels.c1.transactionCapacity = 100
# Bind the source and sink to the channel
a3.sources.r1.channels = c1
a3.sinks.k1.channel = c1
2)分别执行。
可以看到hadoop102上对日志的监听与hadoop103:33333端口输入的监听都聚合输出到了hadoop104:44444端口。
3.5 自定义Interceptor
3.5.1 案例需求
使用Flume采集服务器本地日志,需要按照日志类型的不同,将不同种类的日志发往不同的分析系统。
3.5.2 需求分析
在实际的开发中,一台服务器产生的日志类型可能有很多种,不同类型的日志可能需要发送到不同的分析系统。此时会用到Flume拓扑结构中的Multiplexing结构,Multiplexing的原理是,根据event中Header的某个key的值,将不同的event发送到不同的Channel中,所以我们需要自定义一个Interceptor,为不同类型的event的Header中的key赋予不同的值。
在该案例中,我们以端口数据模拟日志,以数字(单个)和字母(单个)模拟不同类型的日志,我们需要自定义interceptor区分数字和字母,将其分别发往不同的分析系统(Channel)。
3.5.3 需求实现
1)创建maven项目,创建MyInterceptor类实现Interceptor接口
maven依赖:
<dependency>
<groupId>org.apache.flume</groupId>
<artifactId>flume-ng-core</artifactId>
<version>1.9.0</version>
</dependency>
MyInterceptor.java
import org.apache.flume.Context;
import org.apache.flume.Event;
import org.apache.flume.interceptor.Interceptor;
import java.util.List;
/**
* 自定义拦截器
* 作用:根据body中的内容在headers中添加指定的key-value
* 如果内容为字母,那么添加state=letter
* 如果内同为数字,那么添加state=number
*/
public class MyInterceptor implements Interceptor {
/**
* 初始化
*/
@Override
public void initialize() {
}
/**
* 为每个event中的header添加key-value
* channelProcessor调用拦截器时会调用该方法并将event传过来
*
* @param event
* @return
*/
@Override
public Event intercept(Event event) {
//获取body中的内容
byte[] body = event.getBody();
//判断数据中的内容 根据类型写入headers
if ((body[0] >= 'A' && body[0] <= 'Z') || (body[0] >= 'a' && body[0] <= 'z')) {
event.getHeaders().put("type", "letter");
} else if (body[0] >= '0' && body[0] <= '9') {
event.getHeaders().put("type", "number");
}
return event;
}
@Override
public List<Event> intercept(List<Event> list) {
//从集合中遍历每一个event 对其进行修改
for (Event event : list) {
intercept(event);
}
return list;
}
/**
* 拦截器结束时进行调用,关闭资源操作
*/
@Override
public void close() {
}
/**
* 静态内部类
* 返回MyInterceptor 拦截器调用的是该内部类
*/
public static class Builder implements Interceptor.Builder{
/**
* 返回Interceptor接口的实例
* @return
*/
@Override
public Interceptor build() {
return new MyInterceptor();
}
@Override
public void configure(Context context) {
}
}
}
2)编辑flume配置文件
#agent1(hadoop102)
a1.sources = r1
a1.channels = c1 c2
a1.sinks = k1 k2
a1.sources.r1.type = netcat
a1.sources.r1.bind = hadoop102
a1.sources.r1.port = 22222
#复用
a1.sources.r1.selector.type = multiplexing
a1.sources.r1.selector.header = type
a1.sources.r1.selector.mapping.letter = c1
a1.sources.r1.selector.mapping.number = c2
#自定义拦截器
a1.sources.r1.interceptors = i1
#注意:调用的是Interceptor的内部类Builder
a1.sources.r1.interceptors.i1.type = com.atguigu.demo.MyInterceptor$Builder
a1.channels.c1.type = memory
a1.channels.c2.type = memory
a1.sinks.k1.type = avro
a1.sinks.k1.hostname = hadoop103
a1.sinks.k1.port = 33333
a1.sinks.k2.type = avro
a1.sinks.k2.hostname = hadoop104
a1.sinks.k2.port = 44444
a1.sources.r1.channels = c1 c2
a1.sinks.k1.channel = c1
a1.sinks.k2.channel = c2
-----------------------------------------------------------------
#agent2(hadoop103)
a1.sources = r1
a1.channels = c1
a1.sinks = k1
a1.sources.r1.type = avro
a1.sources.r1.bind = hadoop103
a1.sources.r1.port = 33333
a1.channels.c1.type = memory
a1.channels.c1.capacity = 1000
a1.sinks.k1.type = logger
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
3)分别启动,先启动hadoop103与hadoop104,然后向hadoop102:22222发送数据(字母与数字),查看hadoop103:33333与hadoop104:44444的输出日志,可以看到实现通过拦截器分开输出。
3.6 自定义Source
3.6.1 介绍
Source是负责接收数据到Flume Agent的组件。Source组件可以处理各种类型、各种格式的日志数据,包括avro、thrift、exec、jms、spooling directory、netcat、sequence generator、syslog、http、legacy。官方提供的source类型已经很多,但是有时候并不能满足实际开发当中的需求,此时我们就需要根据实际需求自定义某些source。
官方也提供了自定义source的接口:
https://flume.apache.org/FlumeDeveloperGuide.html#source根据官方说明自定义MySource需要继承AbstractSource类并实现Configurable和PollableSource接口。
实现相应方法:
getBackOffSleepIncrement()//暂不用
getMaxBackOffSleepInterval()//暂不用
configure(Context context)//初始化context(读取配置文件内容)
process()//获取数据封装成event并写入channel,这个方法将被循环调用。
使用场景:读取MySQL数据或者其他文件系统。
3.6.2 需求
使用flume接收数据,并给每条数据添加前缀,输出到控制台。前缀可从flume配置文件中配置。
3.6.3 分析
3.6.4 实现
MySource.java
package com.atguigu.demo;
import org.apache.flume.Context;
import org.apache.flume.EventDeliveryException;
import org.apache.flume.PollableSource;
import org.apache.flume.conf.Configurable;
import org.apache.flume.event.SimpleEvent;
import org.apache.flume.source.AbstractSource;
import java.util.HashMap;
/**
* 自定义source
* 使用flume接受数据,并给每条数据添加前缀,输出到控制台,前缀可以从flume配置文件中配置
*/
public class MySource extends AbstractSource implements Configurable, PollableSource {
private String prefix;
private Long delay;
/**
* 核心方法:获取数据封装成event,将创建的event放入到channel中,该方法会被循环调用。
*
* @return
* @throws EventDeliveryException Status:一个枚举类,使用status的值来表示想Channel中添加的数据是否成功
* READY:成功 BACKOFF:失败
*/
@Override
public Status process() throws EventDeliveryException {
try {
//创建事件头信息
HashMap<String, String> headerMap = new HashMap<>();
//创建事件
SimpleEvent simpleEvent = new SimpleEvent();
for (int i = 0; i < 5; i++) {
//设置事件头信息
simpleEvent.setHeaders(headerMap);
//设置事件内容
simpleEvent.setBody((prefix + i).getBytes());
//将事件写入channel
getChannelProcessor().processEvent(simpleEvent);
Thread.sleep(delay);
}
} catch (Exception e) {
e.printStackTrace();
return Status.BACKOFF;
}
return Status.READY;
}
/**
* 当Source没有数据可封装时,可以让Source所在的线程休息一会
*
* @return
*/
@Override
public long getBackOffSleepIncrement() {
return 0;
}
/**
* 当Source没有数据可封装时,Source最大可以休息多久
*
* @return
*/
@Override
public long getMaxBackOffSleepInterval() {
return 0;
}
/**
* 读取配置文件,可以获取配置文件中的内容
*
* @param context
*/
@Override
public void configure(Context context) {
//将获取配置文件中的指定属性的内容,如果没有配那么默认值为空
prefix = context.getString("prefix", "Hello!");
delay = context.getLong("delay");
}
}
配置文件:
a1.sources = r1
a1.channels = c1
a1.sinks = k1
#自定义的source
a1.sources.r1.type = com.atguigu.demo.MySource
#自定义设置的前缀内容,如果没有设置默认值是Hello!
a1.sources.r1.prefix = helloworld
a1.channels.c1.type = memory
a1.sinks.k1.type = logger
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
启动结果:
3.7 自定义Sink
3.7.1 介绍
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或者其他文件系统。
3.7.2 需求
使用flume接收数据,并在Sink端给每条数据添加前缀和后缀,输出到控制台。前后缀可在flume任务配置文件中配置。
3.7.3 实现
Mysink.java
package com.atguigu.demo;
import org.apache.flume.*;
import org.apache.flume.conf.Configurable;
import org.apache.flume.sink.AbstractSink;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
public class MySink extends AbstractSink implements Configurable {
private String suffix;//读取到配置文件中的suffix内容
Logger logger = LoggerFactory.getLogger(MySink.class);//获取Logger对象,可以将数据以日志的方式输出
@Override
public void configure(Context context) {
suffix = context.getString("suffix", "test");
}
/**
* 将channel中的内容写出去
*
* @return
* @throws EventDeliveryException
*/
@Override
public Status process() throws EventDeliveryException {
Status status = Status.READY;//表示成功输入一个或多个event
//1.获取channel
Channel channel = getChannel();
//2.从channel中获取数据
Transaction transaction = channel.getTransaction();
try {
Event event = null;
//2.1 开启事物
transaction.begin();
//3.获取数据
while (true) {
event = channel.take();
if (event != null) {
break;
}
}
//4.将数据写出
logger.info(new String(new String(event.getBody()) + suffix));
//5.提交数据
transaction.commit();
} catch (Exception e) {
status = Status.BACKOFF;
//事务回滚
transaction.rollback();
} finally {
//关闭事务
transaction.close();
}
return status;
}
}
a1.sources = r1
a1.channels = c1
a1.sinks = k1
a1.sources.r1.type = netcat
a1.sources.r1.bind = hadoop102
a1.sources.r1.port = 44444
a1.channels.c1.type = memory
#自定义sink
a1.sinks.k1.type = com.atguigu.demo.MySink
#给数据设置一个后缀
a1.sinks.k1.suffix = HelloWorld
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
打包提交,向hadoop102:44444发送数据:
可以看到接收到的数据都加上了后缀。