日志采集项目——整合Flume+Kafka案例
日志采集项目——整合Flume+Kafka案例
创建模拟日志数据源项目
打开IDEA创建一个新的Maven项目(File -> New -> Project)


打开项目的pom.xml文件,在文件的指定位置添加如下内容
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>Vfrti</groupId>
<artifactId>artifactdata</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>artifactdata Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
<build>
<finalName>artifactdata</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.14.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.my.logs.LoggerProducer</mainClass>
</transformer>
</transformers>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
在项目中创建OrdersInfo和LoggerProducer两个Java类,以及log4j.properties
# 1. 定义日志输出器:控制台 + 文件(每天生成一个新文件,避免单个文件过大)
log4j.rootLogger=INFO, CONSOLE, FILE
# 2. 控制台输出配置(保留原来的,方便调试)
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Target=System.out
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
# 3. 文件输出配置(核心:写入 orderLogs 目录)
# 3.1 每天生成一个新日志文件(例如:order-2025-11-18.log)
log4j.appender.FILE=org.apache.log4j.DailyRollingFileAppender
# 3.2 日志文件路径:指向 orderLogs 目录(注意:Linux 路径用 /,不是 \)
# ${user.dir} 表示“当前程序运行的目录”,即 /usr/test/flume-test/flume-kafka
log4j.appender.FILE.File=${user.dir}/orderLogs/order.log
# 3.3 日志滚动规则:每天凌晨生成新文件
log4j.appender.FILE.DatePattern='.'yyyy-MM-dd
# 3.4 日志格式(和控制台一致,方便查看)
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
# 3.5 日志文件编码(避免中文乱码)
log4j.appender.FILE.Encoding=UTF-8
package org.my.logs;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import java.util.UUID;
import com.alibaba.fastjson.JSONObject;
// 创建订单实体类
public class OrdersInfo {
// 店铺名组
private final String[] ShopNames = new String[] { "Moda", "ZARA", "乐町", "蒂妮佳", "太平鸟", "三彩", "茵曼", "伊芙丽", "秋水伊人", "Lily", "Mongyi", "玖姿", "红袖", "老思", "衣香丽影", "森马", "蜜仙娜" };
// 商品名组
private final String[] GoodsNames = new String[] { "连衣裙", "半身裙", "旗袍", "T恤", "衬衫", "毛衣", "雪纺衫", "卫衣", "马甲", "牛仔裤", "短外套", "西装", "风衣", "毛呢大衣", "羽绒服", "皮衣", "棉衣" };
// 商品价格组
private final float[] GoodsPrices = new float[] { 500.0f, 280.0f, 570.0f, 120.0f, 310.0f, 600.0f, 420.0f, 180.0f, 240.0f, 380.0f, 480.0f, 1300.0f, 1100.0f, 2600.0f, 1500.0f, 1800.0f, 580.0f };
// 价格折扣组
private final float[] PromotionRates = new float[] { 0.8f, 0.7f, 0.65f, 0.95f, 0.85f, 0.5f, 0.75f, 0.9f, 0.75f, 0.55f, 0.9f, 0.8f, 0.85f, 0.65f, 0.7f, 0.75f, 0.6f };
private String orderId; // 订单ID
private long createOrderTime; // 下单时间
private String paymentId; // 支付编号
private Date paymentTime; // 支付时间
private String goodsId; // 商品ID
private String goodsName; // 商品名
private float goodsPrice; // 价格
private float promotionRate; // 价格折扣
private String shopId; // 店铺ID
private String shopName; // 店铺名
private String shopMobile; // 店铺电话
private float payPrice; // 单笔订单支付总价
private int num; // 单价/单数量
private String province; // 省
private String city; // 市
private String county; // 县
private String categorys; // 类别
// 定义类属性orderId的get方法和set方法
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
// 其他类属性的get方法和set方法可通过菜单自动生成,限于篇幅,省略了其他类属性的get方法和set方法的描述,请读者自行补充完整
// ...
@Override
public String toString() {
return "PaymentInfo{" + "orderId=" + this.orderId + "\", \"createOrderTime=" + this.createOrderTime + ", \"paymentId=" + this.paymentId + "\", \"paymentTime=" + this.paymentTime + ", \"goodsId=" + this.goodsId + "\", \"goodsName=" + this.goodsName + "\", \"goodsPrice=" + this.goodsPrice + ", \"promotionRate=" + this.promotionRate + ", \"shopId=" + this.shopId + "\", \"shopName=" + this.shopName + "\", \"shopMobile=" + this.shopMobile + "\", \"payPrice=" + this.payPrice + ", \"num=" + this.num + "}";
}
// 生产订单模拟数据
public String random() throws ParseException {
this.orderId = UUID.randomUUID().toString().replaceAll("-", "");
this.paymentId = UUID.randomUUID().toString().replaceAll("-", "");
this.shopId = new Random().nextInt(200) * 1000 + "";
this.num = new Random().nextInt(10) + 1;
int index = new Random().nextInt(ShopNames.length);
this.shopName = ShopNames[index];
this.goodsName = GoodsNames[index];
this.goodsPrice = GoodsPrices[index];
this.promotionRate = PromotionRates[index];
this.payPrice = this.goodsPrice * this.promotionRate * this.num;
this.categorys = new Random().nextInt(100) * 100 + "," + new Random().nextInt(100) * 100 + "," + new Random().nextInt(100) * 100;
this.province = new Random().nextInt(23) + "";
this.city = new Random().nextInt(265) + "";
this.county = new Random().nextInt(1489) + "";
String s_basedate = "2020-12-12 00:00:00";
SimpleDateFormat sFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
Date base_date = sFormat.parse(s_basedate);
this.createOrderTime = base_date.getTime() + new Random().nextInt(100) * 1000;
} catch (ParseException e) {
e.printStackTrace();
}
return JSONObject.toJSONString(this);
}
}
# 1. 定义日志输出器:控制台 + 文件(每天生成一个新文件,避免单个文件过大)
log4j.rootLogger=INFO, CONSOLE, FILE
# 2. 控制台输出配置(保留原来的,方便调试)
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Target=System.out
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
# 3. 文件输出配置(核心:写入 orderLogs 目录)
# 3.1 每天生成一个新日志文件(例如:order-2025-11-18.log)
log4j.appender.FILE=org.apache.log4j.DailyRollingFileAppender
# 3.2 日志文件路径:指向 orderLogs 目录(注意:Linux 路径用 /,不是 \)
# ${user.dir} 表示“当前程序运行的目录”,即 /usr/test/flume-test/flume-kafka
log4j.appender.FILE.File=${user.dir}/orderLogs/order.log
# 3.3 日志滚动规则:每天凌晨生成新文件
log4j.appender.FILE.DatePattern='.'yyyy-MM-dd
# 3.4 日志格式(和控制台一致,方便查看)
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
# 3.5 日志文件编码(避免中文乱码)
log4j.appender.FILE.Encoding=UTF-8
双击pakage运行,生成.jar文件

创建两级新目录
mkdir -p /usr/test/flume-test/flume-kafka/orderLogs
将.jar文件上传到主节点上
cd /usr/test/flume-test/flume-kafka/orderLogs
执行 rz 后,会自动弹出一个 Windows 文件选择窗口。在窗口中,找到你本地电脑上的 JAR 文件然后点击 “打开” 或 “确定”。
rz
运行jar文件
java -jar artifacdata-1.0-SNAPSHOT-shaded.jar

查看模拟日志生成情况
ll orderLogs/

创建Kafka的消息topic
在每个节点开启zookeeper、kafka
zkServer.sh start
start-kafka.sh
在主节点hadoop01上为Kafka集群创建一个名称为kafka_test用于接收数据的topic
kafka-topics.sh --create --zookeeper hadoop01:2181,hadoop02:2181,hadoop03:2181 --replication-factor 2 --partitions 2 --topic kafka_test
执行成功后会反馈如下信息

编写采集方案,实现Flume+Kafka整合
在flume_kafa目录下,执行以下命令创建Flume采集文案
vi flume-kafka.conf
在文件中按以下格式写入代码
# 设置 source channel sink
a1.sources = r1
a1.channels = c1
a1.sinks = k1
a1.sources.r1.channels = c1 # 指定 source 的管道
a1.sources.r1.type = TAILDIR # 指定 source 类型
a1.sources.r1.positionFile = /usr/test/flume-test/flume-kafka/record/taildir_position.json
a1.sources.r1.filegroups = f1
# 指定被监视的文件
a1.sources.r1.filegroups.f1 = /usr/test/flume-test/flume-kafka/orderLogs/order.log
a1.channels.c1.type = memory # 指定 channel 为 memory 类型
a1.sinks.k1.channel = c1 # 指定 sink 的管道
# 指定 sink 类型为 kafka,注意 KafkaSink 字母的大小,第一个“K”大写,第一个“S”大写
a1.sinks.k1.type = org.apache.flume.sink.kafka.KafkaSink
# 将 topic 的值指定为前面创建好的 kafka 中的 topic 名称
a1.sinks.k1.kafka.topic = kafka_test
a1.sinks.k1.kafka.bootstrap.servers = node1 主机名:9092,node2 主机名:9092,node3 主机名:9092
a1.sinks.k1.kafka.flumeBatchSize = 20
a1.sinks.k1.kafka.producer.acks = 1
测试整合效果
在主节点hadoop01的flume-kafka目录下启动flume采集方案
flume-ng agent -c conf/ -f flume-kafka.conf -n a1 -Dflume.root.logger=INFO.console
开启另一个主节点hadoop01的终端窗口,在flume-kafka目录下执行以下命令,启动模拟日志生成程序
java -jar artifactdata-1.0-SNAPSHOT-shaded.jar

在集群中的任意一个从节点上,以节点hadoop02为例,执行以下命令为前面已创建好的kafka-testTopic启动Condumer
kafka-console-consumer.sh --bootstrap-server hadoop02:9092,hadoop03:9092 --topic kafka_test --from-beginning


浙公网安备 33010602011771号