Flume概述
Flume定义
Flume是Cloudera提供的一个高可用的,高可靠的,分布式的海量日志采集、聚合和传输的系统。
Flume基于流式架构,灵活简单
graph LR
a1[Java后台日志数据] --> B[服务器本地文件夹]
a2[其他日志数据]-->B
B-->C[Flume]
D[网络端口数据]-->C
C-->E[HDFS]
C-->F[Kafka]
Flume基础架构
Flume组成架构如下图(官网图)所示:
Agent
Agent是一个JVM进程,它以事件(Event)的形式将数据从源头送至目的地
Agent主要有3个部分组成:Source、Channel、Sink
Source
Source是负责接收数据到Flume Agent的组件
Source组件可以处理各种类型、各种格式的日志数据
Sink
Sink不断地轮询Channel中的事件且批量地移除它们,并将这些事件批量写入到存储或索引系统、或者被发送到另外的Channel
Channel
Channel是位于Source和Sink之间的缓冲区。因此,Channel允许Source和Sink运作在不同的速率上。Channel是线程安全的,可以同时处理几个Source的写入操作和几个Sink的读取操作。
Flume自带两种Channel:Memory Channel和File Channel
Memory Channel:是内存中的队列。Memory Channel在不需要关心数据丢失的情景下适用。程序死亡、机器宕机或者重启都会导致数据丢失。
File Channel:将所有事件写到磁盘。因此在程序关闭或机器宕机的情况下不会丢失数据。
Event
传输单元,Flume数据传输的基本单元,以Event的形式将数据从源头送至目的地。
Event由Header和Body两部分组成
Header用来存放该event的一些属性,为K-V结构
Body用来存放该条数据,形式为字节数组。
Flume入门
Flume官网地址:flume.apache.org/
文档查看地址:flume.apache.org/FlumeUserGu…
下载地址:archive.apache.org/dist/flume/
Flume安装部署
- 将
apache-flume-1.9.0-bin.tar.gz上传到linux的/opt/software目录下 - 解压
apache-flume-1.9.0-bin.tar.gz到/opt/module/目录下
tar -zxvf /opt/software/apache-flume-1.9.0-bin.tar.gz -C /opt/module/
- 修改
apache-flume-1.9.0-bin的名称为flume
mv /opt/module/apache-flume-1.9.0-bin /opt/module/flume
- 将
lib文件夹下的guava-11.0.2.jar删除以兼容Hadoop 3.1.3
rm /opt/module/flume/lib/guava-11.0.2.jar
- 修改
conf下的log4j.properties确定日志打印的位置
#console表示同时将日志输出到控制台
flume.root.logger=INFO,LOGFILE,console
#固定日志输出的位置
flume.log.dir=/opt/module/flume/logs
#日志文件的名称
flume.log.file=flume.log
Flume入门案例
官方案例
-
案例需求: 使用
Flume监听一个端口,收集该端口数据,并打印到控制台。 -
实现步骤: (1)安装
netcat工具sudo yum install -y nc(2)判断44444端口是否被占用
sudo netstat -nlp | grep 44444(3)在
flume目录下创建job文件夹 (4)在job文件夹下创建Flume Agent配置文件nc_to_logger.confvim nc_to_logger.conf(5)在
nc_to_logger.conf文件中添加如下内容,加内容如下:
# Name the components on this agent
# 设置agent各个组件名称,包括sources,channels,sinks
a1.sources = r1
a1.sinks = k1
a1.channels = c1
# Describe/configure the source
# 设置source为netcat,地址为localhost,端口为44444
a1.sources.r1.type = netcat
a1.sources.r1.bind = localhost
a1.sources.r1.port = 44444
# Describe the sink
# 设置sink类型为logger控制台输出
a1.sinks.k1.type = logger
# Use a channel which buffers events in memory
#设置channel类型为内存类型,大小为1000M
a1.channels.c1.type = memory
a1.channels.c1.capacity = 1000
a1.channels.c1.transactionCapacity = 100
# Bind the source and sink to the channel
# 绑定sources,channel,sink的对应关系
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
注:配置文件来源于官方手册flume.apache.org/FlumeUserGu…
所有组件的其他类型的配置方式均可参考官网所给出的配置方法
(6)先开启flume监听端口
- 第一种写法:
bin/flume-ng agent --conf conf/ --name a1 --conf-file job/nc_to_logger.conf
- 第二种写法:
bin/flume-ng agent -c conf/ -n a1 -f job/nc_to_logger.conf
参数说明:
--name/-n:表示给agent起名为a1
--conf/-c:表示配置文件存储在conf/目录
--conf-file/-f:flume本次启动读取的配置文件是在job文件夹下的nc_to_logger.conf文件。
(6)使用netcat工具向本机的44444端口发送内容
[user@hadoop102 ~]$ nc localhost 44444
10
OK
(7)在Flume监听页面观察接收数据情况
[INFO -org.apache.flume.sink.LoggerSink.process(LoggerSink.java:95)] Event: { headers:{} body: 31 30 10 }
解析:由于我们没有对事务进行头部的添加,因此headers为空,传输的10被转换为字节数组存储在body中,1的16进制ASCII码为31,0的16进制ASCII码为30,因此body数组中存储的为31 30
(8)event打印的源码介绍
LoggerSink的process方法:
if (event != null) {
if (logger.isInfoEnabled()) {
logger.info("Event: " + EventHelper.dumpEvent(event, maxBytesToLog));
}
}
dumpEvent方法返回值:buffer是固定长度的字符串,前端是16进制表示的字符的阿斯卡码值。
return "{ headers:" + event.getHeaders() + " body:" + buffer + " }";
实时监控目录下的多个追加文件
Taildir Source适合用于监听多个实时追加的文件,并且能够实现断点续传
(1) 案例需求
使用Flume监听整个目录的实时追加文件,并上传至HDFS
(2) 实现步骤
- 在
job目录下创建配置文件file_to_hdfs.conf,添加如下内容
a1.sources = r1
a1.sinks = k1
a1.channels = c1
# Describe/configure the source
a1.sources.r1.type = TAILDIR
a1.sources.r1.filegroups = f1 f2
# 必须精确到文件,可以写匹配表达式匹配多个文件
a1.sources.r1.filegroups.f1 = /opt/module/flume/files1/.*file.*
a1.sources.r1.filegroups.f2 = /opt/module/flume/files2/.*log.*
# 实现断点续传的文件存放位置 不改有默认位置也能实现断点续传
a1.sources.r1.positionFile = /opt/module/flume/taildir_position.json
# Describe the sink
a1.sinks.k1.type = hdfs
# 地址值可以填写hdfs://hadoop102:8020也可以省略,flume会自动读取hadoop配置文件信息获取地址
a1.sinks.k1.hdfs.path = hdfs://hadoop102:8020/flume/%Y%m%d/%H
#上传文件的前缀
a1.sinks.k1.hdfs.filePrefix = log-
#是否使用本地时间戳
a1.sinks.k1.hdfs.useLocalTimeStamp = true
#设置文件类型 分为二进制文件SequenceFile和文本文件DataStream(不能压缩) 和CompressedStream(可以压缩)
a1.sinks.k1.hdfs.fileType = DataStream
#多久生成一个新的文件
a1.sinks.k1.hdfs.rollInterval = 30
#设置每个文件的滚动大小大概是128M
a1.sinks.k1.hdfs.rollSize = 134217700
#文件的滚动与Event数量无关
a1.sinks.k1.hdfs.rollCount = 0
# Use a channel which buffers events in memory
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
-
在/opt/module/flume目录下创建files1,files2文件夹 -
启动监控文件夹命令
bin/flume-ng agent --conf conf/ --name a1 --conf-file job/file_to_hdfs.conf
- 向文件夹中添加文件
echo hello >> file1.txt
echo world >> file2.txt
- 查看HDFS上的数据
Taildir说明: Taildir Source维护了一个json格式的position File,其会定期的往position File中更新每个文件读取到的最新的位置,因此能够实现断点续传。
Linux中储存文件元数据的区域就叫做inode,每个inode都有一个号码,操作系统用inode号码来识别不同的文件,Unix/Linux系统内部不使用文件名,而使用inode号码来识别文件。TailDir source使用inode和文件的全路径一起识别同一个文件,所以修改文件名之后如果表达式也能够匹配上,会再重新读取一份文件的数据
Flume进阶
Flume事务
Put事务流程:
doPut:将批数据先写入临时缓冲区putListdoCommit:检查channel内存队列是否足够合并doRollback:channel内存队列空间不足,回滚数据
Take事务:
doTake:将数据取到临时缓冲区takeList,并将数据发送到HDFSdoCommit:如果数据全部发送成功,则清除临时缓冲区takeListdoRollback:数据发送过程中如果出现异常,rollback将临时缓冲区takeList中的数据归还给channel内存队列
Flume Agent内部原理
graph TD
Data --1.接收数据-->Source
Source--2.处理事件-->Channel_Processor
Channel_Processor--3.将事件传递给拦截器链-->Interceptor1
Interceptor1-->Interceptor2
Interceptor2-->Interceptor3
Interceptor3-->Channel_Processor
Channel_Processor--4.将事件交给Chnnel选择器-->Channel_Selector
Channel_Selector--5.返回写入事件的Channel列表-->Channel_Processor
Channel_Processor--6.将事件写入相应Channel-->Channel1
Channel_Processor-->Channel2
Channel_Processor-->Channel3
Channel1-->SinkProcessor
Channel2-->SinkProcessor
Channel3-->SinkProcessor
SinkProcessor-->Sink1
SinkProcessor-->Sink2
SinkProcessor-->Sink3
ChannelSelector
ChannelSelector的作用就是选出~将要被发往哪个Channel
其共有两种类型,分别是Replicating(复制)和Multiplexing(多路复用)
-
ReplicatingSelector会将同一个Event发往所有的Channel -
Multiplexing会根据相应的原则,将不同的Event发往不同的Channel
SinkProcessor
SinkProcessor共有三种类型,分别是
-
DefaultSinkProcessor(默认1对1) -
LoadBalancingSinkProcessor(负载均衡) -
FailoverSinkProcessor(故障转移)
DefaultSinkProcessor对应的是单个的Sink
LoadBalancingSinkProcessor和FailoverSinkProcessor对应的是Sink Group
LoadBalancingSinkProcessor可以实现负载均衡的功能
FailoverSinkProcessor可以错误恢复的功能
自定义拦截器
在实际的开发中,一台服务器产生的日志类型可能有很多种,不同类型的日志可能需要发送到不同的分析系统。此时会用到Flume的channel selecter中的Multiplexing结构,Multiplexing的原理是,根据event中Header的某个key的值,将不同的event发送到不同的Channel中,所以我们需要自定义一个Interceptor,为不同类型的event的Header中的key赋予不同的值。
我们以端口数据模拟日志,以数字(单个)和字母(单个)模拟不同类型的日志,我们需要自定义interceptor区分数字和字母,将其分别发往不同的分析系统(Channel)
实现步骤
- 创建一个
maven项目,并引入以下依赖
<dependencies>
<dependency>
<groupId>org.apache.flume</groupId>
<artifactId>flume-ng-core</artifactId>
<version>1.9.0</version>
</dependency>
</dependencies>
- 定义
MyInterceptor类并实现Interceptor接口
import org.apache.flume.Context;
import org.apache.flume.Event;
import org.apache.flume.interceptor.Interceptor;
import java.util.List;
import java.util.Map;
/**
* 1. 实现interceptor接口
* 2. 实现接口的4个方法
* 3. 实现一个静态内部类创建拦截器
*/
public class MyInterceptor implements Interceptor {
//初始化方法,不需要实现
@Override
public void initialize() {}
//处理单条event
@Override
public Event intercept(Event event) {
// 需要配合channel选择器使用 向headers当中put对应的参数
// 根据传入的数据 首位是数字还是字母 判断他是不同类型的日志
byte[] body = event.getBody();
byte b = body[0];
Map<String, String> headers = event.getHeaders();
if (b >= '0' && b <= '9'){
// b为数字
headers.put("type","number");
}else if((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z')){
// b 为字母
headers.put("type","letter");
}
// 可以不需要在写放回headers(可写可不写)
event.setHeaders(headers);
return event;
}
//处理多条event,调用上述的处理单挑event的方法即可
@Override
public List<Event> intercept(List<Event> events) {
for (Event event : events) {
intercept(event);
}
return events;
}
//close方法,不需要实现
@Override
public void close() {}
// 静态内部类
public static class MyBuilder implements Builder{
//以建造者模式为设计模式,build方法返回该拦截器对象
@Override
public Interceptor build() {
return new MyInterceptor();
}
// 配置方法,不需要实现
@Override
public void configure(Context context) {}
}
}
将代码打包,然后将jar包放到flume根目录的lib目录下,方便flume去调用
flume配置文件配置自定义的拦截器
# 拦截器配置
a1.sources.r1.interceptors = i1
a1.sources.r1.interceptors.i1.type = com.atguigu.flume.MyInterceptor$MyBuilder
注意:在拦截器的type参数中为自定义拦截器的全类名$静态内部类名
Flume数据流监控
Ganglia的安装与部署
(1) 安装Ganglia
- 在
hadoop102、hadoop103、hadoop104分别安装epel-release依赖
sudo yum -y install epel-release
- 在
hadoop102安装gmetad,web,gmond
sudo yum -y install ganglia-gmetad
sudo yum -y install ganglia-web
sudo yum -y install ganglia-gmond
- 在
hadoop103、hadoop104安装gmond
sudo yum -y install ganglia-gmond
(2) 在hadoop102修改配置文件/etc/httpd/conf.d/ganglia.conf
sudo vim /etc/httpd/conf.d/ganglia.conf
#
# Ganglia monitoring system php web frontend
#
Alias /ganglia /usr/share/ganglia
<Location /ganglia>
#Order deny,allow
#Deny from all
#Allow from 127.0.0.1
#Allow from ::1
# Allow from .example.com
Require all granted
</Location>
(3) 在hadoop102修改配置文件/etc/ganglia/gmetad.conf
sudo vim /etc/ganglia/gmetad.conf
修改为:
data_source "hadoop102" hadoop102
(4) 在hadoop102、hadoop103、hadoop104修改配置文件/etc/ganglia/gmond.conf
sudo vim /etc/ganglia/gmond.conf
cluster {
name = "hadoop102"
owner = "unspecified"
latlong = "unspecified"
url = "unspecified"
}
udp_send_channel {
#bind_hostname = yes # Highly recommended, soon to be default.
# This option tells gmond to use a source address
# that resolves to the machine's hostname. Without
# this, the metrics may appear to come from any
# interface and the DNS names associated with
# those IPs will be used to create the RRDs.
# mcast_join = 239.2.11.71
# 数据发送给hadoop102
host = hadoop102
port = 8649
ttl = 1
}
udp_recv_channel {
# mcast_join = 239.2.11.71
port = 8649
# 接收来自任意连接的数据
bind = 0.0.0.0
retry_bind = true
# Size of the UDP buffer. If you are handling lots of metrics you really
# should bump it up to e.g. 10MB or even higher.
# buffer = 10485760
(5) 在hadoop102修改配置文件/etc/selinux/config
sudo vim /etc/selinux/config
# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
# enforcing - SELinux security policy is enforced.
# permissive - SELinux prints warnings instead of enforcing.
# disabled - No SELinux policy is loaded.
SELINUX=disabled
# SELINUXTYPE= can take one of these two values:
# targeted - Targeted processes are protected,
# mls - Multi Level Security protection.
SELINUXTYPE=targeted
注意:selinux本次生效关闭必须重启,如果此时不想重启,可以临时生效
sudo setenforce 0
(6) 启动ganglia
- 在
hadoop102、hadoop103、hadoop104启动gmond
sudo systemctl start gmond
- 在
hadoop102启动httpd、gmetad
sudo systemctl start httpd
sudo systemctl start gmetad
(7) 打开网页浏览ganglia页面