Apache Flink是新一代的分布式流式数据处理框架,它统一的处理引擎既可以处理批数据(batch data)也可以处理流式数据(streaming data)。在实际场景中,Flink利用Apache Kafka作为上下游的输入输出十分常见,本文将给出一个可运行的实际例子来集成两者。

1. 目标

本例模拟中将集成Kafka与Flink:Flink实时从Kafka中获取消息,每隔10秒去统计机器当前可用的内存数并将结果写入到本地文件中。

2. 环境准备

  • Apache Kafka 0.11.0.0
  • Apache Flink 1.3.1
  • Gradle 3.5 (版本号不是强要求)

本例运行在Windows环境,但可以很容易地移植到其他平台上。

3. 创建Flink Streaming工程

本例使用Intellij IDEA作为项目开发的IDE。首先创建Gradle project,group为'huxihx.flink.demo',artifact id为‘flink-kafka-demo’,version为‘1.0-SNAPSHOT’。整个项目结构如图所示:

4. 增加kafka和kafka-connector依赖

增加下列gradle依赖:

    compile group: 'org.apache.flink', name: 'flink-connector-kafka-0.10_2.11', version: '1.3.1'
compile group: 'org.apache.flink', name: 'flink-streaming-java_2.11', version: '1.3.1'
compile group: 'org.apache.kafka', name: 'kafka-clients', version: '0.11.0.0'

设置gradle打包依赖

jar {
manifest {
attributes(
"Manifest-Version": 1.0,
"Main-Class": "huxihx.KafkaMessageStreaming")
}
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
into('assets') {
from 'assets'
}
}

5. 启动Flink环境(本例使用local测试环境)

F:\SourceCode\flink-1.3.
> bin\start-local.bat
Starting Flink job manager. Webinterface by default on http://localhost:8081/.
Don't close this batch window. Stop job manager by pressing Ctrl+C.

6. 启动Kafka单节点集群

启动Zookeeper:

cd F:\SourceCode\zookeeper
> bin\zkServer.cmd

启动Kafka broker:

> cd F:\SourceCode\kafka_1
> set JMX_PORT=
> bin\windows\kafka-server-start.bat F:\\SourceCode\\configs\\server.properties

7. 代码开发

代码主要由两部分组成:

  • MessageSplitter类、MessageWaterEmitter类和KafkaMessageStreaming类:Flink streaming实时处理Kafka消息类
  • KafkaProducerTest类和MemoryUsageExtrator类:构建Kafka测试消息

本例中,Kafka消息格式固定为:时间戳,主机名,当前可用内存数。其中主机名固定设置为machine-1,而时间戳和当前可用内存数都是动态获取。由于本例只会启动一个Kafka producer来模拟单台机器发来的消息,因此在最终的统计结果中只会统计machine-1这一台机器的内存。下面我们先来看flink部分的代码实现。

MessageSplitter类(将获取到的每条Kafka消息根据“,”分割取出其中的主机名和内存数信息)

public class MessageSplitter implements FlatMapFunction<String, Tuple2<String, Long>> {

    @Override
public void flatMap(String value, Collector<Tuple2<String, Long>> out) throws Exception {
if (value != null && value.contains(",")) {
String[] parts = value.split(",");
out.collect(new Tuple2<>(parts[1], Long.parseLong(parts[2])));
}
}
}

MessageWaterEmitter类(根据Kafka消息确定Flink的水位)

public class MessageWaterEmitter implements AssignerWithPunctuatedWatermarks<String> {
@Nullable
@Override
public Watermark checkAndGetNextWatermark(String lastElement, long extractedTimestamp) {
if (lastElement != null && lastElement.contains(",")) {
String[] parts = lastElement.split(",");
return new Watermark(Long.parseLong(parts[0]));
}
return null;
} @Override
public long extractTimestamp(String element, long previousElementTimestamp) {
if (element != null && element.contains(",")) {
String[] parts = element.split(",");
return Long.parseLong(parts[0]);
}
return 0L;
}
}

KafkaMessageStreaming类(Flink入口类,封装了对于Kafka消息的处理逻辑。本例每10秒统计一次结果并写入到本地文件)

public class KafkaMessageStreaming {

    public static void main(String[] args) throws Exception {
final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.enableCheckpointing(5000); // 非常关键,一定要设置启动检查点!!
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime); Properties props = new Properties();
props.setProperty("bootstrap.servers", "localhost:9092");
props.setProperty("group.id", "flink-group"); FlinkKafkaConsumer010<String> consumer =
new FlinkKafkaConsumer010<>(args[0], new SimpleStringSchema(), props);
consumer.assignTimestampsAndWatermarks(new MessageWaterEmitter()); DataStream<Tuple2<String, Long>> keyedStream = env
.addSource(consumer)
.flatMap(new MessageSplitter())
.keyBy(0)
.timeWindow(Time.seconds(10)) .apply(new WindowFunction<Tuple2<String, Long>, Tuple2<String, Long>, Tuple, TimeWindow>() {
@Override
public void apply(Tuple tuple, TimeWindow window, Iterable<Tuple2<String, Long>> input, Collector<Tuple2<String, Long>> out) throws Exception {
long sum = 0L;
int count = 0;
for (Tuple2<String, Long> record: input) {
sum += record.f1;
count++;
}
Tuple2<String, Long> result = input.iterator().next();
result.f1 = sum / count;
out.collect(result);
}
}); keyedStream.writeAsText(args[1]);
env.execute("Flink-Kafka demo");
}
}

实现了这些代码之后我们已然可以打包进行部署了,不过在其之前我们先看下Kafka producer测试类的实现——该类每1秒发送一条符合上面格式的Kafka消息供下游Flink集群消费。

MemoryUsageExtrator类(很简单的工具类,提取当前可用内存字节数)

public class MemoryUsageExtrator {

    private static OperatingSystemMXBean mxBean =
(OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); /**
* Get current free memory size in bytes
* @return free RAM size
*/
public static long currentFreeMemorySizeInBytes() {
return mxBean.getFreePhysicalMemorySize();
}
}

KafkaProducerTest类(发送Kafka消息)

public class KafkaProducerTest {

    public static void main(String[] args) throws Exception {
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("acks", "all");
props.put("retries", 0);
props.put("batch.size", 16384);
props.put("linger.ms", 1);
props.put("buffer.memory", 33554432);
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer"); Producer<String, String> producer = new KafkaProducer<>(props);
int totalMessageCount = 10000;
for (int i = 0; i < totalMessageCount; i++) {
String value = String.format("%d,%s,%d", System.currentTimeMillis(), "machine-1", currentMemSize());
producer.send(new ProducerRecord<>("test", value), new Callback() {
@Override
public void onCompletion(RecordMetadata metadata, Exception exception) {
if (exception != null) {
System.out.println("Failed to send message with exception " + exception);
}
}
});
Thread.sleep(1000L);
}
producer.close();
} private static long currentMemSize() {
return MemoryUsageExtrator.currentFreeMemorySizeInBytes();
}
}

8. 部署Flink jar包

8.1 打包Flink jar包

> cd flink-kafka-demo
> gradle clean build

生成的jar包在项目目录下的build/libs/下,本例中是flink-kafka-demo-1.0-SNAPSHOT.jar

8.2 部署jar包

> bin\flink.bat run -c huxihx.KafkaMessageStreaming  F:\\Projects\\flink-kafka-demo\\build\\libs\\flink-kafka-demo-1.0-SNAPSHOT.jar test F:\\temp\result.txt  

KafkaMessageStreaming类接收两个命令行参数,第一个是Kafka topic名字,第二个是输出文件路径

部署成功之后,可以在Flink控制台(本例中是http://localhost:8081/)中看到job已成功部署,如下图所示:

8. 运行KafkaProducerTest

运行Kafka producer,给Flink job创建输入数据,然后启动一个终端,监控输出文件的变化,

> cd F:\temp
> tail -f result.txt
(machine-,)
(machine-,)
(machine-,)
(machine-,)
(machine-,)
(machine-,)
(machine-,)
......

可以看到,Flink每隔10s就会保存一条新的统计记录到result.txt文件中,该记录会统计主机名为machine-1的机器在过去10s的平均可用内存字节数。

9. 总结

本文给出了一个可运行的Flink + Kafka的项目配置及代码实现。值得注意的是,上面例子中用到的Flink Kafka connector使用了Kafka新版本consumer的API,因此不再需要连接Zookeeper信息。

Kafka与Flink集成的更多相关文章

  1. Kafka设计解析(十八)Kafka与Flink集成

    转载自 huxihx,原文链接 Kafka与Flink集成 Apache Flink是新一代的分布式流式数据处理框架,它统一的处理引擎既可以处理批数据(batch data)也可以处理流式数据(str ...

  2. Apache Hudi与Apache Flink集成

    感谢王祥虎@wangxianghu 投稿 Apache Hudi是由Uber开发并开源的数据湖框架,它于2019年1月进入Apache孵化器孵化,次年5月份顺利毕业晋升为Apache顶级项目.是当前最 ...

  3. 简单测试flume+kafka+storm的集成

    集成 Flume/kafka/storm 是为了收集日志文件而引入的方法,最终将日志转到storm中进行分析.storm的分析方法见后面文章,这里只讨论集成方法. 以下为具体步骤及测试方法: 1.分别 ...

  4. 058 kafka与log4j集成

    1.首先在resources下面写log4j.properties 主要是因为kafka.producer.KafkaLog4jAppender类的存在. log4j.rootLogger=INFO, ...

  5. Flink KAFKA

    https://data-artisans.com/blog/kafka-flink-a-practical-how-to https://github.com/dataArtisans/kafka- ...

  6. Kafka设计解析(二十)Apache Flink Kafka consumer

    转载自 huxihx,原文链接 Apache Flink Kafka consumer Flink提供了Kafka connector用于消费/生产Apache Kafka topic的数据.Flin ...

  7. 【译】Apache Flink Kafka consumer

    Flink提供了Kafka connector用于消费/生产Apache Kafka topic的数据.Flink的Kafka consumer集成了checkpoint机制以提供精确一次的处理语义. ...

  8. Flink Kafka Connector 与 Exactly Once 剖析

    Flink Kafka Connector 是 Flink 内置的 Kafka 连接器,它包含了从 Kafka Topic 读入数据的 Flink Kafka Consumer 以及向 Kafka T ...

  9. kafka与Spring的集成

    准备工作 kafka版本:kafka_2.10-0.10.1.0 spring版本:spring4.3 配置文件 pom文件配置(也可以直接下载jar包) Kafka和spring集成的支持类库,sp ...

随机推荐

  1. c++ const 用法总结

    最近第二次学习c++ , 却总是忘记const的一些用法, 所以记录一下笔记 忒困! A: const指针位于 * 的左边 A : const 修饰指针指向的内容, 则内容为不可变量但指针可变: 称其 ...

  2. 【转】【Python】装饰器

    1.闭包 >>> def outer(): ... x = 1 ... def inner(): ... ... return inner >>> foo = ou ...

  3. 转载:推荐给每个“数据分析师”看的PPT——关于开会的那点事

    推荐给每个“数据分析师”看的PPT——关于开会的那点事 经常对开会“深恶痛绝”,大概的原因有两个,其一,开会之前的准备,各种指标.各种分析.各种PPT,其二,开会中的板凳.废话,尤其是走形式的会议,战 ...

  4. SpagoBI 论坛

    http://www.spagoworld.org/jforum/forums/list.page

  5. 第三百七十四节,Django+Xadmin打造上线标准的在线教育平台—创建课程app,在models.py文件生成4张表,课程表、课程章节表、课程视频表、课程资源表

    第三百七十四节,Django+Xadmin打造上线标准的在线教育平台—创建课程app,在models.py文件生成4张表,课程表.课程章节表.课程视频表.课程资源表 创建名称为app_courses的 ...

  6. JDBC删除数据实例

    在本教程将演示如何在JDBC应用程序中,删除数据库表中数据记录. 在执行以下示例之前,请确保您已经准备好以下操作: 具有数据库管理员权限,以在给定模式的数据库表中删除数据记录. 要执行以下示例,需要用 ...

  7. (转)st(state-threads) coroutine和stack分析

     目录(?)[-] STACK分配 THREAD初始化栈 Thread启动和切换 Thread退出 Thread初始线程 Thread生命周期 st(state-threads) https://gi ...

  8. CentOS7修改设置静态IP和DNS

    当前位置: 主页 > CentOS入门 > 系统配置 > CentOS7修改设置静态IP和DNS 时间:2016-02-22 00:55来源:blog.csdn.net 作者:get ...

  9. 新建maven指定jdk版本-eclipse新建maven项目报错The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path的解决方案

    具体表现为: 使用Eclipse+Maven建立了一个Javaweb工程,JSP页面顶端出现“红色”的报错信息:The superclass "javax.servlet.http.Http ...

  10. koa中上传文件到阿里云oss实现点击在线预览和下载

    比较好的在线预览的方法: 跳转一个新的页面,里面放一个iframe标签,或者object标签 <iframe src="xxx"></iframe> < ...