一、Flume基础部分
参考网址:Flume 1.9用户手册中文版 — 可能是目前翻译最完整的版本了
在线作图工具:ProcessOn思维导图流程图-在线画思维导图流程图_在线作图实时协作
Apache Flume 是一个分布式、高可靠、高可用的用来收集、聚合、转移不同来源的大量日志数据到中央数据仓库的工具。
1、Flume的简介
flume 其实就是水管 数据起始就是水,将水引入到不同的容器中

Sqoop -- 一般用于采集数据库中的数据
Flume -- 一般采集我们的日志数据 user.log
Flume 其实可以看做一个水管。一头接着水源,一头接着桶,从水源地抽取数据到桶里去。

Agent : 是Flume中的基本单位,一个Flume配置文件,可以有多个Agent.
每一个Agent中有三个组件组成,缺一不可:
1、Source 来源,数据过来的地方
2、channel 通道 传递数据用的通道,一般比较的长,一个Channel中可以存储多个数据
3、Sink 数据下沉的地方
4、event 一个event就是一个数据单元(一条数据)
2、安装
解压,重命名,修改配置文件:
tar -zxvf apache-flume-1.9.0-bin.tar.gz -C /opt/installs/
mv apache-flume-1.9.0-bin/ flume
在企业中安装软件起始有两个地方比较常见:
/usr/local/
也可以安装在 /opt/installs
export FLUME_HOME=/opt/installs/flume
export PATH=$PATH:$FLUME_HOME/bin:
source /etc/profile

修改一下flume的配置文件:
cp flume-env.sh.template flume-env.sh

3、flume的数据模型
- 单一数据模型 只有一个Agent

- 多数据流模型




flume抽取数据的速度快吗?最大的速度有多快?最大速度也不能超过磁盘的读写速度,磁盘一秒钟最多写多少? 100M/S
4、关于flume的使用
flume 的使用是编写 conf文件的,运行的时候指定该文件
# 定义组件的名字
<Agent>.sources = <Source>
a1.sources=s1
<Agent>.channels = <Channel1> <Channel2>
a1.channels=c1
<Agent>.sinks = <Sink>
a1.sinks=sink1
# 设置source 和 channel 之间的关系
<Agent>.sources.<Source>.channels = <Channel1> <Channel2> ...
a1.sources.s1.channels=c1
# 设置sink 和 channel 之间的关系
<Agent>.sinks.<Sink>.channel = <Channel1>
a1.sinks.sink1.channel=c1
先定义agent的名字,再定义agent中三大组件的名字
接着定义各个组件之间的关联关系
# list the sources, sinks and channels for the agent
agent_foo.sources = avro-appserver-src-1
agent_foo.channels = mem-channel-1
agent_foo.sinks = hdfs-sink-1
# set channel for source
agent_foo.sources.avro-appserver-src-1.channels = mem-channel-1
# set channel for sink
agent_foo.sinks.hdfs-sink-1.channel = mem-channel-1
常见的source和channel 以及sink有哪些?
常见的Source :

常见的channel:

常见的sink:

总结:Kafka 可以充当flume中的各个组件。三个组件可以两两组合在一起,所以使用场景非常的多。
5、案例展示
1)Avro+Memory+Logger【主要用于演示,没有实战意义】
avro: 是监听某个端口是否有信息的工具
memory: 内存
logger: 控制台
即将演示一个场景:给服务器上的一个端口发送消息,消息经过内存,打印到控制台上。
先找source 中的avro看需要设置什么参数

#编写s1的类型是什么
a1.sources.s1.type = avro
a1.sources.s1.bind = 192.168.32.128
a1.sources.s1.port = 4141
a1.sources.s1.channels = c1
找到channel中的memory类型,再设置一下
a1.channels.c1.type = memory
a1.channels.c1.capacity = 10000
#source 或者 sink 每个事务中存取 Event 的操作数量
a1.channels.c1.transactionCapacity = 10000
接着查找sink,sink的类型是logger
a1.sinks.s2.channel = c1
a1.sinks.s2.type = logger
最终合并起来的文件就是:
a1.sources = r1
a1.channels = c1
a1.sources.r1.type = avro
a1.sources.r1.channels = c1
a1.sources.r1.bind = bigdata01
a1.sources.r1.port = 4141
a1.channels.c1.type = memory
a1.sinks = k1
a1.sinks.k1.type = logger
a1.sinks.k1.channel = c1
在flume文件夹下创建一个文件夹flumeconf ,用于存放我们写好的文件
a1.sources = r1
a1.channels = c1
a1.sources.r1.type = avro
a1.sources.r1.channels = c1
a1.sources.r1.bind = bigdata01
a1.sources.r1.port = 4141
a1.channels.c1.type = memory
a1.sinks = k1
a1.sinks.k1.type = logger
a1.sinks.k1.channel = c1
在flume文件夹下创建一个文件夹flumeconf ,用于存放我们写好的文件
进入后创建 avro-memory-logger.conf
将配置文件的内容拷贝进去
先启动flume-ng
flume-ng agent -c ./ -f avro-memory-logger.conf -n a1 -Dflume.root.logger=INFO,console
-c 后面跟上 配置文件的路径
-f 跟上自己编写的conf文件
-n agent的名字
-Dflume.root.logger=INFO,console INFO 日志输出级别 Debug,INFO,warn,error 等
接着向端口中发送数据:
flume-ng avro-client -c /opt/installs/flume/conf/ -H bigdata01 -p 4141 -F /home/hivedata/arr1.txt
给avro发消息,使用avro-client
flume是没有运行结束时间的,它一直监听某个Ip的端口,有消息就处理,没消息,就等着,反正不可能运行结束。

如果想停止,可以使用ctrl + c 终止flume。
2) Exec + Memory + HDFS
案例我写死了,告诉你了,到企业中怎么知道用什么呢?
取决于公司的业务,理论将 sources channel sink 可以任意组合
经过一个执行的命令,将数据通过内存,上传至hdfs
exec 执行命令
tail -f tomcat.log 查看这个文件,而且是不断的查看
linux中查看文件 cat more less tail
tail -f 文件名 不断查看一个文件的内容,长用于查看日志文件
a1.sources = r1
a1.channels = c1
a1.sources.r1.type = exec
a1.sources.r1.command = tail -F /opt/data/c.txt
a1.sources.r1.channels = c1
a1.channels.c1.type = memory
a1.channels.c1.capacity = 10000
a1.channels.c1.transactionCapacity = 10000
a1.sinks = k1
a1.sinks.k1.type = hdfs
a1.sinks.k1.channel = c1
a1.sinks.k1.hdfs.path = /flume/events/%y-%m-%d/%H%M/
# round 用于控制含有时间转义符的文件夹的生成规则
a1.sinks.k1.hdfs.round = true
a1.sinks.k1.hdfs.roundValue = 10
a1.sinks.k1.hdfs.roundUnit = minute
# 使用本地时间,否则会报时间戳是null的异常
a1.sinks.k1.hdfs.useLocalTimeStamp=true
假如hdfs中有时间转义字符,必须给定时间,给定时间有两种方式,要么通过header 传递一个时间,要么使用本地时间。

假如hdfs中使用了时间转义字符,配置文件中必须二选一:
1)useLocalTimeStamp=true
2)使用时间戳拦截器
为啥呀:
时间需要转义,没有时间无法翻译为具体的值 %d 就无法翻译为 日期



如何实现不断的向a.txt中存入数据的效果呢?
echo "Hello World" >> a.txt
运行我们的脚本:
flume-ng agent -c ./ -f exec-memory-hdfs.conf -n a1 -Dflume.root.logger=INFO,console
3)Spool +File + HDFS
Spooling Directory
spool 这个效果是抽取一个文件夹的效果,文件夹中不断的产生新的文件,我将这些新的文件上传至hdfs。
a1.channels = ch-1
a1.sources = src-1
a1.sources.src-1.type = spooldir
a1.sources.src-1.channels = ch-1
a1.sources.src-1.spoolDir = /home/scripts/
a1.sources.src-1.fileHeader = true
a1.channels.ch-1.type = file
a1.sinks = k1
a1.sinks.k1.type = hdfs
a1.sinks.k1.channel = ch-1
a1.sinks.k1.hdfs.path = /flume/


数据采集的意思:不管是一个文件还是一个文件夹,数据都是不断产生的,特别是生产环境下更是如此。
以上的采集只能采集到文件夹中是否有新的文件产生,不能采集变化的文件。
抽取一个文件夹中的所有文件,子文件夹中的文件是不抽取的,抽取过的文件,数据发生了变化,也不会再抽取一次。
假如你的channel是 file类型,必定会有临时文件产生,产生的文件在哪里?



总结:
channel 一般常用的只要两个,一个是memory,一个是file ,最高效的是memory ,也是最常用的。

4) tailDir + Memory + HDFS
tailDir 是用来监控多个文件夹下的多个文件的,只要文件内容发生变化,就会再次的进行数据的抽取
a1.sources = r1
a1.channels = c1
a1.sources.r1.type = TAILDIR
a1.sources.r1.channels = c1
a1.sources.r1.filegroups = f1
# . 代表的意思是一个任意字符 * 代表前面的字符出现0到多次
a1.sources.r1.filegroups.f1 = /home/scripts/datas/.*txt.*
a1.channels.c1.type = memory
a1.channels.c1.capacity = 10000
a1.channels.c1.transactionCapacity = 10000
a1.sinks = k1
a1.sinks.k1.type = hdfs
a1.sinks.k1.channel = c1
a1.sinks.k1.hdfs.path = /flume3/logs
tailDir 可以采集一个不断变化的文件。 a.log 不断的有数据产生,就可以不断的上传至hdfs了。
flume-ng agent -c ./ -f taildir_memory_hdfs.conf -n a1 -Dflume.root.logger=INFO,console
数据不断的发生变化,每发生一次变化,就传递一次,源源不断的抽取出来。
刚才为什么抽取了那么多个文件还没有抽取完?
由于我们刚才的一些数据非常的大,根据如下参数可以疯狂创建文件:
hdfs.rollInterval 30 当前文件写入达到该值时间后触发滚动创建新文件(0表示不按照时间来分割文件),单位:秒
hdfs.rollSize 1024 当前文件写入达到该大小后触发滚动创建新文件(0表示不根据文件大小来分割文件),单位:字节
hdfs.rollCount 10 当前文件写入Event达到该数量后触发滚动创建新文件(0表示不根据 Event 数量来分割文件)
再次抽取,发现不抽了,原因是有一个文件记录了以前抽取过的内容,将其删除:
rm -rf /root/.flume/taildir_position.json
修改1.txt 里面的内容,flume会再次抽取数据
echo "hello" >> 1.txt
二、Flume中的拦截器
Flume支持在运行时对Event进行修改或丢弃,可以通过拦截器来。
Event 就是数据,拦截住数据之后,可以修改它,也可以删除它。
Event中的拦截器起始就是拦住以后 ,向Event中的header属性中添加KV键值对

1、时间戳拦截器
这个拦截器会向每个Event的header中添加一个时间戳属性进去,key默认是“timestamp ”(也可以通过headerName参数来自定义修改),value就是当前的毫秒值(其实就是用System.currentTimeMillis()方法得到的)。 如果Event已经存在同名的属性,可以选择是否保留原始的值。


拦截器怎么用:
a1.sources.r1.interceptors = i1 i1 是拦截器的名字,自己定义的
a1.sources.r1.interceptors.i1.type = timestamp 指定i1 这个拦截器的类型
hdfs中,如果文件夹使用了时间相关的转义字符,比如:
a1.channels = c1
a1.sinks = k1
a1.sinks.k1.type = hdfs
a1.sinks.k1.channel = c1
a1.sinks.k1.hdfs.path = /flume/events/%y-%m-%d/%H%M/%S
a1.sinks.k1.hdfs.filePrefix = events-
a1.sinks.k1.hdfs.round = true
a1.sinks.k1.hdfs.roundValue = 10
a1.sinks.k1.hdfs.roundUnit = minute
a1.sinks.k1.hdfs.useLocalTimeStamp =true
也可以使用时间戳拦截器:
a1.sources = r1
a1.channels = c1
a1.sources.r1.type = exec
a1.sources.r1.command = tail -F /opt/data/c.txt
a1.sources.r1.channels = c1
a1.channels.c1.type = memory
a1.channels.c1.capacity = 10000
a1.channels.c1.transactionCapacity = 10000
a1.sinks = k1
a1.sinks.k1.type = hdfs
a1.sinks.k1.channel = c1
a1.sinks.k1.hdfs.path = /flume/events/%y-%m-%d/%H%M/
# round 用于控制含有时间转义符的文件夹的生成规则
a1.sinks.k1.hdfs.round = true
a1.sinks.k1.hdfs.roundValue = 10
a1.sinks.k1.hdfs.roundUnit = minute
# i1 是拦截器的名字,自己定义的
a1.sources.r1.interceptors = i1
a1.sources.r1.interceptors.i1.type = timestamp
因为必须保证header中有时间戳timestamp这个KV键值对。
2、Host添加拦截器
如果event被这个拦截器拦住后,就会在heder中添加 主机名或者IP地址。

举例:
a1.sources = r1
a1.channels = c1
a1.sinks = s1
a1.sources.r1.type= http
a1.sources.r1.bind = bigdata01
a1.sources.r1.port = 6666
a1.sources.r1.interceptors=i2
a1.sources.r1.interceptors.i2.type=host
a1.sources.r1.interceptors.i2.preserveExisting=false
a1.sources.r1.interceptors.i2.useIP=true
a1.sources.r1.interceptors.i2.hostHeader=hostname
# hostname=192.168.233.128
a1.channels.c1.type=memory
a1.channels.c1.capacity=1000
a1.channels.c1.transactionCapacity=100
a1.sinks.s1.type=hdfs
a1.sinks.s1.hdfs.path=/flume/%Y/%m/%d/%H%M
a1.sinks.s1.hdfs.filePrefix=%{hostname}
a1.sinks.s1.hdfs.fileSuffix=.log
a1.sinks.s1.hdfs.rollInterval=60
a1.sinks.s1.hdfs.round=true
a1.sinks.s1.hdfs.roundValue=10
a1.sinks.s1.hdfs.roundUnit=second
a1.sinks.s1.hdfs.useLocalTimeStamp=true
a1.sources.r1.channels=c1
a1.sinks.s1.channel=c1
这个例子可以完美的说明host拦截器是干嘛的,拦截住之后 在Header中添加一个 hostname=192.168.32.128
在hdfs想使用hostname的时候,就可以通过%{hostname}
9870 hdfs的web访问端口
9820 是hdfs底层进行通信的端口
jps -ml 查看java进行,带详细信息
测试:
curl 起始就是通过命令模拟浏览器
curl http://www.baidu.com
curl http://bigdata01:9870/dfshealth.html#tab-overview
curl -X POST -d '[{"headers":{"state":"USER"},"body":"this my multiplex to c1"}]' http://bigdata01:6666
3、静态拦截器
Header中的数据是可以被其他地方使用的,时间戳和Host拦截器只能指定固定的KV键值对。
假如我现在想指定 name=zhangsan , 也就是说如果想在Header中指定自定义的KV键值对,可以使用静态拦截器。

三、自定义拦截器(重要)
需求:
处理数据样例:
log='{
"host":"www.baidu.com",
"user_id":"13755569427",
"items":[
{
"item_type":"eat",
"active_time":156234
},
{
"item_type":"car",
"active_time":156233
}
]
}'
结果样例:
[{"active_time":156234,"user_id":"13755569427","item_type":"eat","host":"www.baidu.com"},
{"active_time":156233,"user_id":"13755569427","item_type":"car","host":"www.baidu.com"}]
1、创建一个maven项目,导入jar包

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bigdata</groupId>
<artifactId>MyInterceptor</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.apache.flume/flume-ng-core -->
<dependency>
<groupId>org.apache.flume</groupId>
<artifactId>flume-ng-core</artifactId>
<version>1.9.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.48</version>
</dependency>
</dependencies>
<!--可以使用maven中的某些打包插件,不仅可以帮助我们打包代码还可以打包所依赖的jar包-->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<!-- 禁止生成 dependency-reduced-pom.xml-->
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
<executions>
<!-- Run shade goal on package phase -->
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<relocations>
<relocation>
<!-- 解决包冲突 进行转换-->
<pattern>com.google.protobuf</pattern>
<shadedPattern>shaded.com.google.protobuf</shadedPattern>
</relocation>
</relocations>
<artifactSet>
<excludes>
<exclude>log4j:*</exclude>
</excludes>
</artifactSet>
<filters>
<filter>
<!-- Do not copy the signatures in the META-INF folder.
Otherwise, this might cause SecurityExceptions when using the JAR. -->
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
</excludes>
</filter>
</filters>
<transformers>
<!-- 某些jar包含具有相同文件名的其他资源(例如属性文件)。 为避免覆盖,您可以选择通过将它们的内容附加到一个文件中来合并它们-->
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>reference.conf</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>mainclass</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
打包插件:
![]()
代码:
package com.bigdata;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.flume.Context;
import org.apache.flume.Event;
import org.apache.flume.interceptor.Interceptor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class DemoInterceptor implements Interceptor {
@Override
public void initialize() {
}
// 只需要关注 这个方法的写法
/**
* 需求:
* log='{
* "host":"www.baidu.com",
* "user_id":"13755569427",
* "items":[
* {
* "item_type":"eat",
* "active_time":156234
* },
* {
* "item_type":"car",
* "active_time":156233
* }
* ]
* }'
*
* 需要转化为:
* [{"active_time":156234,"user_id":"13755569427","item_type":"eat","host":"www.baidu.com"},
* {"active_time":156233,"user_id":"13755569427","item_type":"car","host":"www.baidu.com"}]
*/
@Override
public Event intercept(Event event) {
// 解析 文本数据变为另一种格式
byte[] body = event.getBody();
String content = new String(body);
/**
* {
* "host":"www.baidu.com",
* "user_id":"13755569427",
* "items":[
* {
* "item_type":"eat",
* "active_time":156234
* },
* {
* "item_type":"car",
* "active_time":156233
* }
* ]
* }
*/
// 将一个json字符串变为 json 对象
JSONObject jsonObject = JSON.parseObject(content);
// 通过对象 获取 json 中的值
String host = jsonObject.getString("host");
String user_id = jsonObject.getString("user_id");
// 通过对象获取json 数组
JSONArray items = jsonObject.getJSONArray("items");
// 定义一个集合,集合中是map
ArrayList<HashMap<String, String>> list = new ArrayList<>();
for (Object object: items) {
String obj = object.toString();
JSONObject jobj = JSON.parseObject(obj);
String item_type = jobj.getString("item_type");
String active_time = jobj.getString("active_time");
HashMap<String, String> map = new HashMap<>();
map.put("active_time",active_time);
map.put("item_type",item_type);
map.put("host",host);
map.put("user_id",user_id);
list.add(map);
}
// 将对象变为字符串
String s = JSON.toJSONString(list);
event.setBody(s.getBytes());
return event;
}
// 这个方法可以调取 上面这个方法
@Override
public List<Event> intercept(List<Event> list) {
for (int i=0;i<list.size();i++) {
Event oldEvent = list.get(i);
Event newEvent = intercept(oldEvent);
list.set(i,newEvent);
}
return list;
}
@Override
public void close() {
}
// 作用只有一个,就是new 一个自定义拦截器的类
public static class BuilderEvent implements Builder{
@Override
public Interceptor build() {
return new DemoInterceptor();
}
@Override
public void configure(Context context) {
}
}
}
打包,上传至 flume 下的lib 下。
测试:
编写一个flume脚本文件 testInter.conf
a1.sources = s1
a1.channels = c1
a1.sinks = r1
a1.sources.s1.type = TAILDIR
#以空格分隔的文件组列表。每个文件组表示要跟踪的一组文件
a1.sources.s1.filegroups = f1
#文件组的绝对路径
a1.sources.s1.filegroups.f1=/home/b.log
#使用自定义拦截器
a1.sources.s1.interceptors = i1
a1.sources.s1.interceptors.i1.type = com.bigdata.DemoInterceptor$BuilderEvent
a1.channels.c1.type = file
a1.channels.c1.capacity = 1000
a1.channels.c1.transactionCapacity = 100
a1.sinks.r1.type = hdfs
a1.sinks.r1.hdfs.path = /flume/202309
a1.sinks.r1.hdfs.fileSuffix= .log
# 将上传的数据格式使用text类型,便于查看
a1.sinks.r1.hdfs.fileType=DataStream
a1.sinks.r1.hdfs.writeFormat=Text
a1.sources.s1.channels = c1
a1.sinks.r1.channel = c1
运行该脚本:
flume-ng agent -c ./ -f testInterceptor.conf -n a1 -Dflume.root.logger=INFO,console

此时,说明我们打包的时候没有将这个jar包打包到自定义的jar包,可以通过手动的提交的方式解决这个问题。
将fast-json.jar 放入到 flume/lib
接着开始进行测试,必须先启动flume
编写一个脚本,模拟 b.log 中不断的产生json数据的场景。
#!/bin/bash
log='{
"host":"www.baidu.com",
"user_id":"13755569427",
"items":[
{
"item_type":"eat",
"active_time":156234
},
{
"item_type":"car",
"active_time":156233
}
]
}'
echo $log >> /home/b.log
保存,并且赋予权限:
chmod 777 createJson.sh
执行这个脚本,就可以模拟不断的向 b.log中传输数据了
./createJson.sh

如何自定义时间戳拦截器(只需要在方法中编写如下代码即可)
HashMap<String, String> hashMap = new HashMap<>();
hashMap.put("timestamp",System.currentTimeMillis()+"");
event.setHeaders(hashMap);
四、选择器

一个Source对应多个channel的情况下,多个Channel中的数据是否相同,取决于我们使用了什么选择器,默认是复制选择器。也可以手动的使用多路选择器。
1、演示复制选择器

编写flume脚本,需要一个source,两个channel,以及两个sink
a1.sources = r1
a1.channels = c1 c2
a1.sinks = s1 s2
# avro http syslogtcp
# avro avro-client
# http curl
# syslogtcp nc
a1.sources.r1.type = syslogtcp
a1.sources.r1.host = bigdata01
a1.sources.r1.port = 7777
#执行选择器类型为复制选择器
a1.sources.r1.selector.type=replicating
a1.channels.c1.type=memory
a1.channels.c2.type=memory
a1.sinks.s1.type=hdfs
a1.sinks.s1.hdfs.path=hdfs://bigdata01:9820/flume/%Y-%m-%d/rep
a1.sinks.s1.hdfs.filePrefix=s1sink
a1.sinks.s1.hdfs.fileSuffix=.log
a1.sinks.s1.hdfs.fileType=DataStream
a1.sinks.s1.hdfs.writeFormat=Text
a1.sinks.s1.hdfs.useLocalTimeStamp=true
a1.sinks.s2.type=hdfs
a1.sinks.s2.hdfs.path=hdfs://bigdata01:9820/flume/%Y-%m-%d/rep
a1.sinks.s2.hdfs.filePrefix=s2sink
a1.sinks.s2.hdfs.fileSuffix=.log
a1.sinks.s2.hdfs.fileType=DataStream
a1.sinks.s2.hdfs.writeFormat=Text
a1.sinks.s2.hdfs.useLocalTimeStamp=true
a1.sources.r1.channels=c1 c2
a1.sinks.s1.channel=c1
a1.sinks.s2.channel=c2
启动这个flume脚本:
flume-ng agent -c ./ -f syslogtcp-memory-hdfs.conf -n a1 -Dflume.root.logger=INFO,console
向bigdata01 中的6666端口发送消息:
echo "hello world" | nc bigdata01 7777
如果nc命令无法识别,需要安装一下 yum install -y nc

查看里面的数据发现都一样,说明使用的是复制选择器。
2、多路选择器

就是每次发送消息的时候,可以指定发送消息走哪条channel,只有这条channel对应的sink才有数据,其他sink没数据。
举例:
a1.sources = r1
a1.channels = c1 c2 c3 c4
a1.sources.r1.selector.type = multiplexing
a1.sources.r1.selector.header = state #以每个Event的header中的state这个属性的值作为选择channel的依据
a1.sources.r1.selector.mapping.CZ = c1 #如果state=CZ,则选择c1这个channel
a1.sources.r1.selector.mapping.US = c2 c3 #如果state=US,则选择c2 和 c3 这两个channel
a1.sources.r1.selector.default = c4 #默认使用c4这个channel
说明一个小区别:
avro
syslogtcp
http
可以指定一个hostname和端口号
不同的source,我们使用的发送数据的方式是不一样的:
avro-client
nc
curl
curl 是可以模拟发送get 或者 post 请求的。
比如: curl www.baidu.com
编写脚本:
mul.conf
a1.sources = r1
a1.channels = c1 c2
a1.sinks = s1 s2
a1.sources.r1.type= http
a1.sources.r1.bind = bigdata01
a1.sources.r1.port = 8888
a1.sources.r1.selector.type=multiplexing
# header 跟 mapping 结合在一起,用于发送消息时,指定发送的方向
a1.sources.r1.selector.header = state
a1.sources.r1.selector.mapping.USER = c1
a1.sources.r1.selector.mapping.ORDER = c2
# 发送的消息找不到具体的channel,就走默认的c1
a1.sources.r1.selector.default = c1
a1.channels.c1.type=memory
a1.channels.c2.type=memory
a1.sinks.s1.type=hdfs
a1.sinks.s1.hdfs.path=hdfs://bigdata01:9820/flume/%Y-%m-%d/mul
a1.sinks.s1.hdfs.filePrefix=s1sink
a1.sinks.s1.hdfs.fileSuffix=.log
a1.sinks.s1.hdfs.fileType=DataStream
a1.sinks.s1.hdfs.writeFormat=Text
a1.sinks.s1.hdfs.useLocalTimeStamp=true
a1.sinks.s2.type=hdfs
a1.sinks.s2.hdfs.path=hdfs://bigdata01:9820/flume/%Y-%m-%d/mul
a1.sinks.s2.hdfs.filePrefix=s2sink
a1.sinks.s2.hdfs.fileSuffix=.log
a1.sinks.s2.hdfs.fileType=DataStream
a1.sinks.s2.hdfs.writeFormat=Text
a1.sinks.s2.hdfs.useLocalTimeStamp=true
a1.sources.r1.channels=c1 c2
a1.sinks.s1.channel=c1
a1.sinks.s2.channel=c2
启动该脚本:
flume-ng agent -c ./ -f mul.conf -n a1 -Dflume.root.logger=INFO,console
模拟http请求:
curl -X POST -d '[{"headers":{"state":"USER"},"body":"this my multiplex to c1"}]' http://bigdata01:8888
curl -X POST -d '[{"headers":{"state":"ORDER"},"body":"this my multiplex to c2"}]' http://bigdata01:8888
效果就是,当我发送一条指令的时候,走state=USER的路径,只生成一个文件,走另一条路才会生成另一个不同的文件。

五、自动容灾(故障转移)

多个sink组成一个组,这个组内的sink只有一台工作,假如这一台坏了,另一台自动的工作。
为了演示这个效果,我使用了三个Agent.模型如下:

在bigdata02和bigdata03上安装flume
在集群中可以使用脚本
myscp.sh /opt/installs/flume1.9.0/
myscp.sh /etc/profile
xcall.sh source /etc/profile
也可以使用长拷贝命令,例如:
scp -r /opt/installs/flume1.9.0/ root@hadoop11:/opt/installs/
# 因为 /etc/hosts 文件中没有配置映射,所以使用ip代替了
scp -r /opt/installs/flume1.9.0/ root@192.168.52.12:/opt/installs/
scp -r /etc/profile root@hadoop11:/etc
scp -r /etc/profile root@192.168.52.12:/etc
两个虚拟机需要刷新配置文件
source /etc/profile
在bigdata01上,编写flume脚本:
failover.conf
#list names
a1.sources = r1
a1.channels = c1
a1.sinks = k1 k2
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
a1.sinks.k2.channel = c1
# source
a1.sources.r1.type = syslogtcp
a1.sources.r1.host = bigdata01
a1.sources.r1.port = 10086
# channel
a1.channels.c1.type = memory
# sink
a1.sinks.k1.type = avro
a1.sinks.k1.hostname = bigdata02
a1.sinks.k1.port = 10087
a1.sinks.k2.type = avro
a1.sinks.k2.hostname = bigdata03
a1.sinks.k2.port = 10088
#设置sink组
a1.sinkgroups = g1
a1.sinkgroups.g1.sinks = k1 k2
a1.sinkgroups.g1.processor.type = failover
# 此处是设置的权重,权重越多,就工作
a1.sinkgroups.g1.processor.priority.k1 = 10
a1.sinkgroups.g1.processor.priority.k2 = 5
a1.sinkgroups.g1.processor.maxpenalty = 10000
flume-ng agent -c ../conf -f ./failover.conf -n a1 -Dflume.root.logger=INFO,console
修改第二台电脑的脚本:
bigdata02
failover2.conf
a1.sources = r1
a1.channels = c1
a1.sinks = k1
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
# source
a1.sources.r1.type = avro
a1.sources.r1.bind = bigdata02
a1.sources.r1.port = 10087
# channel
a1.channels.c1.type = memory
# sink
a1.sinks.k1.type = logger
启动flume脚本:
flume-ng agent -c ../conf -f ./failover2.conf -n a1 -Dflume.root.logger=INFO,console
在bigdata03上,编写failover3.conf
a1.sources = r1
a1.channels = c1
a1.sinks = k1
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
# source
a1.sources.r1.type = avro
a1.sources.r1.bind = bigdata03
a1.sources.r1.port = 10088
# channel
a1.channels.c1.type = memory
# sink
a1.sinks.k1.type = logger
flume-ng agent -c ../conf -f ./failover3.conf -n a1 -Dflume.root.logger=INFO,console
bigdata02和03启动无异常,再启动01上的脚本:
flume-ng agent -c ../conf -f ./failover.conf -n a1 -Dflume.root.logger=INFO,console
在hadoop10上,发送消息:
echo "wei,wei,wei" | nc bigdata01 10086
发现bigdata02有反应,出现了消息,而bigdata03无反应。因为bigdata02权重大,需要工作

测试故障转移,将bigdata02停掉,再在bigdata01上发消息,就发现bigdata03收到消息了,故障转移了。

假如bigdata02又好了,怎么办? 发现bigdata02继续工作,劳苦的命。
六、负载均衡
发送一个Event的时候到底是哪个sink来处理这个工作,有两种方式:轮询 和 随机

演示一下:
bigdata01中创建balance.conf
#list names
a1.sources = r1
a1.channels = c1
a1.sinks = k1 k2
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
a1.sinks.k2.channel = c1
# source
a1.sources.r1.type = syslogtcp
a1.sources.r1.host = bigdata01
a1.sources.r1.port = 10086
# channel
a1.channels.c1.type = memory
# sink
a1.sinks.k1.type = avro
a1.sinks.k1.hostname = bigdata02
a1.sinks.k1.port = 10087
a1.sinks.k2.type = avro
a1.sinks.k2.hostname = bigdata03
a1.sinks.k2.port = 10088
#设置sink组
a1.sinkgroups = g1
a1.sinkgroups.g1.sinks = k1 k2
a1.sinkgroups.g1.processor.type = load_balance
processor.selector = random
bigdata02,bigdata03 不变,服务必须是启动的。
使用我们的nc命令发送请求,发现bigdata02和03随机的处理请求的数据。
2110

被折叠的 条评论
为什么被折叠?



