08-Flume

一、Flume基础部分

参考网址:Flume 1.9用户手册中文版 — 可能是目前翻译最完整的版本了

在线作图工具:ProcessOn思维导图流程图-在线画思维导图流程图_在线作图实时协作

 Apache Flume 是一个分布式、高可靠、高可用的用来收集、聚合、转移不同来源的大量日志数据到中央数据仓库的工具。

1、Flume的简介 

flume 其实就是水管  数据起始就是水,将水引入到不同的容器中

image.png

Sqoop -- 一般用于采集数据库中的数据
Flume -- 一般采集我们的日志数据   user.log

Flume 其实可以看做一个水管。一头接着水源,一头接着桶,从水源地抽取数据到桶里去。

image.png

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

image.png

修改一下flume的配置文件:

cp flume-env.sh.template flume-env.sh

image.png

3、flume的数据模型

- 单一数据模型 只有一个Agent

- 多数据流模型

image.png

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 传递一个时间,要么使用本地时间。

image.png

假如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键值对

image.png

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地址。

image.png

举例:

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

image.png

此时,说明我们打包的时候没有将这个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

image.png

如何自定义时间戳拦截器(只需要在方法中编写如下代码即可) 

 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随机的处理请求的数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YuPangZa

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值