【微服务】springboot整合kafka-stream使用详解

目录

一、前言

二、kafka stream概述

2.1 什么是kafka stream

2.2 为什么需要kafka stream

2.2.1 对接成本低

2.2.2 节省资源

2.2.3 使用简单

2.3 kafka stream特点

2.4 kafka stream中的一些概念

2.5 Kafka Stream应用场景

三、环境准备

3.1 搭建zk

3.1.1 自定义docker网络

3.1.2 拉取zk镜像

3.1.3 启动zk容器

3.2 搭建kafka

3.2.1 下载kafka并解压

3.2.2 修改配置文件

3.2.3 启动kafka服务

3.3 kafka测试

3.3.1 创建topic

3.3.2 开启kafka生产端控制台

3.3.3 开启kafka消费端控制台

3.4 java客户端集成kafka测试

四、kafka stream 使用

4.1 前置准备

4.2 kafka stream应用开发步骤

4.2.1 步骤1:创建Kafka Streams 实例

4.2.2 步骤2:指定输入与输出topic

4.2.3 步骤3:启动Kafka Streams 实例

4.3 kafka stream操作案例

4.3.1 转换单词大小写

4.3.2 将topic1数据写入到topic2中

4.3.3 统计wordcount

4.4 kafka stream窗口函数使用

4.4.1 需求一,固定时间输出统计结果到另一个topic

4.4.2 需求二,统计topic1中10秒内的wordcount写到topic2

4.5 Kafka Streams使用场景拓展

4.5.1 事件日志监控

4.5.2 事用户行为统计分析

4.5.3 数据聚合与实时计算

4.5.4 实时推荐

4.5.5 实时告警

4.5.6 应用解耦

五、kafka stream整合springboot

5.1 整合过程

5.1.1 导入springboot相关依赖

5.1.2 配置kafka相关信息

5.1.3 添加Kafka Stream配置类

5.1.4 自定义Kafka Stream业务处理监听器

5.1.5 效果测试

六、写在文末


一、前言

随着大数据技术的发展越来越成熟,大数据涉及的领域也越来越多,从以往的T+1到如今的实时处理,得益于底层技术的强大支撑,尤其是流式计算技术的发展让众多的业务场景价值得以深度挖掘,聊到流式计算,涌入入脑海中的Spark Streaming,Flink等,本文接下来将介绍另一种流式计算技术kafka stream。

二、kafka stream概述

2.1 什么是kafka stream

Kafka Stream是一款开源、分布式和水平扩展的流处理平台,其在Apache Kafka之上进行构建,借助其高性能、可伸缩性和容错性,可以实现高效的流处理应用程序。

2.2 为什么需要kafka stream

在处理流式计算的场景中,发展到今天出现了很多成熟的性能高效的技术框架,比如老牌的Apache Storm,大数据处理框架Spark Streaming,Flink等,而且像Spark 与flink都能与SQL紧密结合,集成便捷,功能也很强大,为何还需要kafka stream呢?

2.2.1 对接成本低

kafka可以说在很多互联网公司都有着广泛的使用,只要维护了kafka的环境,即可集成和使用kafka stream。

2.2.2 节省资源

相比于部署spark,storm等这样的大数据处理框架需要的计算资源,部署kafka占用的服务器资源更少,而且维护起来也相对节省人力。

2.2.3 使用简单

相比与spark和flink这样的大数据框架,kafka在日常的开发中接触和使用会更多,学习和上手成本会低很多。  

2.3 kafka stream特点

Kafka Stream是Apache Kafka从0.10版本引入的一个新Feature。它是提供了对存储于Kafka内的数据进行流式处理和分析的功能。具有如下特点:

  • Kafka Stream提供了一个非常简单而轻量的Library,可以方便的嵌入任意Java应用中,也可以任意方式打包和部署;

  • 充分利用Kafka分区机制实现水平扩展和顺序性保证;

  • 提供记录级的处理能力,从而实现毫秒级的低延迟;

  • 支持基于事件时间的窗口操作,并且可处理晚到的数据(late arrival of records),这点与spark和flink中的时间窗口处理机制很像;

  • 提供底层的处理原语Processor(类似于Storm的spout和bolt),以及高层抽象的DSL(类似于Spark的map/group/reduce);

  • 通过可容错的state store实现高效的状态操作(如windowed join和aggregation);

  • 除了Kafka外,无任何外部依赖,且支持正好一次处理语义;

2.4 kafka stream中的一些概念

在kafka stream中,KStream和KTable是理解kafka stream时非常核心的两个概念。

KStream

KStream是一个数据流,是一段顺序的、可以无限长、不断更新的数据集,可以认为所有的记录都通过Insert only的方式插入进这个数据流中。

KTable

KTable代表一个完整的数据集,可对照mysql理解为数据库中的表。每条记录都有KV键值对,key可理解为数据库中的主键,是唯一的,而value代表一条记录,记录通常是一段可序列化的字符串。可以认为KTable中的数据时通过Update only的方式进入的。如果是相同的key,会覆盖掉原来那条记录。

综上来说:

  • KStream是数据流,即不断传输过来的流式数据记录,以Insert only的方式不断插入;

  • KTable是数据集(逻辑概念),相同key的数据只保留最新的记录,也就是Update only;

2.5 Kafka Stream应用场景

Kafka Streams主要用于以下应用场景:

  • 实时数据处理,通过实时流计算,对数据进行快速分析和处理,或者处理之后转交下游应用;
  • 流式ETL,将数据从一个数据源抽取到另一个数据源,或将数据进行转换、清洗和聚合操作;
  • 流-表格Join:将一条流数据与一个表进行关联查询,实现实时查询和联合分析;
  • 行为数据统计分析与推荐,在电商场景中,通过接收用户行为日志数据进行分析计算从而为用户推荐提供数据支撑;

三、环境准备

在开始使用kafka stream之前,先快速搭建起kafka的环境,参照下面的步骤快速部署kafka的环境。

3.1 搭建zk

3.1.1 自定义docker网络

docker network create zk-kafka --driver bridge

3.1.2 拉取zk镜像

docker pull zookeeper:3.8.1

3.1.3 启动zk容器

docker run -d --name zk-server -p 2181:2181 --network zk-kafka -e ALLOW_ANONYMOUS_LOGIN=yes zookeeper:3.8.1 

 

3.2 搭建kafka

3.2.1 下载kafka并解压

下载地址:Apache Kafka,这里我使用 kafka_2.12-3.1.1.tgz

tar -zxvf  kafka_2.12-3.1.1.tgzcd kafka_2.12-3.1.1mkdir logs

3.2.2 修改配置文件

进到config目录下,找到server.properties配置文件,主要修改下面几个核心配置即可(覆盖原有的默认的配置参数)

broker.id=0

listeners=PLAINTEXT://云服务器内网IP:9092
zookeeper.connect=内外网均可,如果不对外暴露使用内网IP:2181
log.dirs=/usr/local/kafka/kafka_2.12-3.1.1/logs
advertised.listeners=PLAINTEXT://外网IP:9092

参数说明:

  • listeners=PLAINTEXT://云服务器内网ip:9092,如果是云服务器,一定要配置成内网IP;
  • advertised.listeners=PLAINTEXT://云服务器公网ip:9092,若要远程访问需配置此项为云服务器的公网ip;

3.2.3 启动kafka服务

在主目录下,使用下面的命令启动kafka服务前台启动

./bin/kafka-server-start.sh ./config/server.properties

或者使用下面的命令后台启动

./bin/kafka-server-start.sh -daemon ./config/server.properties

3.3 kafka测试

kafka服务启动之后,接下来创建一个测试用的topic并测试是否能够正常生产和消费消息

3.3.1 创建topic

使用下面的命令创建一个名为zcy的topic

bin/kafka-topics.sh --create --topic zcy --bootstrap-server 公网IP:9092

3.3.2 开启kafka生产端控制台

使用下面的命令,开启一个生产者的控制台窗口,并发送一条消息

bin/kafka-console-producer.sh --broker-list 公网IP:9092 --topic zcy

3.3.3 开启kafka消费端控制台

使用下面的命令,开启一个消费端的控制台窗口,检查是否能够正常消费消息

bin/kafka-console-consumer.sh --bootstrap-server 公网IP:9092 --topic zcy
或者
bin/kafka-console-consumer.sh --bootstrap-server 公网IP:9092 --topic zcy --from-beginning

3.4 java客户端集成kafka测试

引入kafka的客户端依赖

        <dependency><groupId>org.apache.kafka</groupId><artifactId>kafka-clients</artifactId></dependency>

编写如下的测试代码,向上述kafka的zcy这个topic中发送一条消息

public static void main(String[] args) throws Exception {// 1. 创建 kafka 生产者的配置对象Properties properties = new Properties();// 2. 给 kafka 配置对象添加配置信息:bootstrap.serversproperties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "公网IP:9092");properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");// 3. 创建 kafka 生产者对象KafkaProducer<String, String> kafkaProducer = new KafkaProducer<String, String>(properties);System.out.println("开始发送数据");// 4. 调用 send 方法,发送消息for (int i = 0; i < 5; i++) {kafkaProducer.send(new ProducerRecord<>("zcy","congge " + i));}// 5. 关闭资源kafkaProducer.close();}

运行上面的代码,运行成功后,可以看到上面的kafka的消费端的控制台正确接收到了5条消息

四、kafka stream 使用

介绍了kafka stream的相关概念之后,接下来通过一些案例感受下如何使用

4.1 前置准备

创建一个maven工程,引入如下依赖

    <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.4.RELEASE</version></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.kafka</groupId><artifactId>spring-kafka</artifactId><exclusions><exclusion><groupId>org.apache.kafka</groupId><artifactId>kafka-clients</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.apache.kafka</groupId><artifactId>kafka-clients</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId></dependency><dependency><groupId>org.apache.kafka</groupId><artifactId>kafka-streams</artifactId><exclusions><exclusion><artifactId>connect-json</artifactId><groupId>org.apache.kafka</groupId></exclusion><exclusion><groupId>org.apache.kafka</groupId><artifactId>kafka-clients</artifactId></exclusion></exclusions></dependency></dependencies>

再创建另一个topic

bin/kafka-console-consumer.sh --bootstrap-server IP:9092 --topic zcy-out

4.2 kafka stream应用开发步骤

使用kafka stream进行应用的业务开发,即相关的API使用,按照下面几步操作:

4.2.1 步骤1:创建Kafka Streams 实例

Properties props = new Properties();
props.put(StreamsConfig.APPLICATION_ID_CONFIG, "定义本次实例名称,保持全局唯一");
props.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "kafka连接IP地址:9092");
//... 更多其他的属性可以点击到StreamsConfig配置类进行查看
StreamsBuilder builder = new StreamsBuilder();
KafkaStreams streams = new KafkaStreams(builder.build(), props);

参数说明:

  • props.put(StreamsConfig.APPLICATION_ID_CONFIG, "my-stream") 指定本次流处理应用的唯一标识符;

  • props.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092") 指定连接的 Kafka 集群的地址;

  • StreamsBuilder builder = new StreamsBuilder() 创建 StreamsBuilder 实例,并用其构建 TOPOLOGY;

4.2.2 步骤2:指定输入与输出topic

final String inputTopic = "topic-input";
final String outputTopic = "topic-output";
KStream<String, String> inputStream = builder.stream(inputTopic);
//从input-topic中拿到数据进行逻辑处理
KStream<String, String> outputStream = inputStream.mapValues(value -> value.toUpperCase());
//将处理后的数据输出到其他的topic中
outputStream.to(outputTopic);

4.2.3 步骤3:启动Kafka Streams 实例

streams.start();

以上几步可以说是Kafka Streams编程的一种固定的方法模板,需重点关注。

4.3 kafka stream操作案例

4.3.1 转换单词大小写

业务场景如下,从topic1中接收到消息,将消息内容转换为大写之后,输出到topic2

完整的代码如下:

public static void main(String[] args) {Properties props = new Properties();props.put(StreamsConfig.APPLICATION_ID_CONFIG, "stream-convert-app");props.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "IP:9092");props.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass());props.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG,Serdes.String().getClass());StreamsBuilder builder = new StreamsBuilder();KStream<String, String> inputStream = builder.stream("zcy");KStream<String, String> outputStream = inputStream.mapValues(value -> value.toUpperCase());outputStream.to("zcy-out", Produced.with(Serdes.String(), Serdes.String()));KafkaStreams streams = new KafkaStreams(builder.build(), props);streams.start();}

运行代码之前,我们将zcy-out这个topic的消费端的终端打开,便于看到程序中处理之后的结果

运行上面的程序,通过观察控制台日志可以发现当前处于等待接收消息输入的状态

由于之前zcy这个topic中已经有消息了,可以看到,经过程序的处理,窗口中能够获取到之前的消息,并且已经将消息转为大写了

此时通过生产端的控制台发送一条消息,然后再在zcy-out消息控制台中就能近乎实时看到被转换后的消息了

注意:如果实际业务中想适当节省计算资源,即不需要实时计算,而是间隔计算之后提交结果,可以通过设置下面的这个参数,即3秒提交一次结果

prop.put(StreamsConfig.COMMIT_INTERVAL_MS_CONFIG,3000);  //提交时间设置为3秒

4.3.2 将topic1数据写入到topic2中

业务场景如下,topic1接收外部消息,然后转发到topic2中

实际开发中,可能需要将原始的消息经过简单的处理之后发到另一个topic中,以供后面的业务使用,可以考虑使用下面这种方式

public class StreamCopy {public static void main(String[] args) {Properties prop =new Properties();prop.put(StreamsConfig.APPLICATION_ID_CONFIG,"copy-stream");prop.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG,"IP:9092");prop.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass());prop.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG,Serdes.String().getClass());prop.put(StreamsConfig.COMMIT_INTERVAL_MS_CONFIG,3000);StreamsBuilder builder = new StreamsBuilder();KStream<String, String> inputStream = builder.stream("zcy");inputStream.to("zcy-out", Produced.with(Serdes.String(), Serdes.String()));KafkaStreams streams = new KafkaStreams(builder.build(), prop);streams.start();}}

运行代码之后,仍然采用上面的方式做测试,在zcy的生产者窗口发送一条消息,可以看到zcy-out

中接收到相同的消息

4.3.3 统计wordcount

需求场景如下,通过kafka stream将第一个topic中接收到的消息经过计算之后输出到topic2中

完整代码如下

public class KafkaStreamWordCount {public static void main(String[] args) {//kafka的配置Properties prop = new Properties();prop.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "IP:9092");prop.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass());prop.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass());prop.put(StreamsConfig.APPLICATION_ID_CONFIG, "streams-count");StreamsBuilder streamsBuilder = new StreamsBuilder();KStream<String, String> stream = streamsBuilder.stream("zcy");stream.flatMapValues(new ValueMapper<String, Iterable<String>>() {@Overridepublic Iterable<String> apply(String value) {return Arrays.asList(value.split(" "));}})//按照value进行聚合处理.groupBy((key, value) -> value)//时间窗口.windowedBy(TimeWindows.of(Duration.ofSeconds(10)))//统计单词的个数.count()//转换为kStream.toStream().map((key, value) -> {System.out.println("key:" + key + " ,vlaue:" + value);return new KeyValue<>(key.key().toString(), value.toString());})//发送消息.to("zcy-out");KafkaStreams kafkaStreams = new KafkaStreams(streamsBuilder.build(), prop);kafkaStreams.start();}}

4.4 kafka stream窗口函数使用

窗口函数在很多技术框架中都有着广泛的使用,比如spark,flink,hive,甚至在mysql8也开始支持窗口函数了,利用窗口函数可以对某个时间窗口内的数据进行统计、聚合和计算,接下来通过几个案例展示下在kafka stream中窗口函数的使用。

4.4.1 需求一,固定时间输出统计结果到另一个topic

这里每隔3秒输出一次从topic1中过去10秒的数据到topic2中

import org.apache.kafka.common.serialization.Serdes;
import org.apache.kafka.streams.*;
import org.apache.kafka.streams.kstream.*;import java.time.Duration;
import java.util.Arrays;
import java.util.Properties;public class WindowStream1 {public static void main(String[] args) {Properties prop = new Properties();prop.put(StreamsConfig.APPLICATION_ID_CONFIG, "WindowCountStream");prop.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "IP:9092");prop.put(StreamsConfig.COMMIT_INTERVAL_MS_CONFIG, 3000);prop.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass());prop.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass());StreamsBuilder builder = new StreamsBuilder();KStream<Object, Object> source = builder.stream("zcy");KTable<Windowed<String>, Long> countTable = source.flatMapValues(value -> Arrays.asList(value.toString().split("\\s+"))).map((x, y) -> {return new KeyValue<String, String>(y, "1");}).groupByKey()//加10秒窗口,按步长3秒滑动.windowedBy(TimeWindows.of(Duration.ofSeconds(10).toMillis()).advanceBy(Duration.ofSeconds(3).toMillis())).count();countTable.toStream().foreach((key, val) -> {System.out.println("key: " + key + "  val: " + val);});countTable.toStream().map((key, val) -> {return new KeyValue<String, String>(key.toString(), val.toString());}).to("zcy-out");final Topology topo = builder.build();final KafkaStreams streams = new KafkaStreams(topo, prop);streams.start();}
}

运行代码,按照上述相同的方式测试,然后再在控制台可以看到统计到的时间窗口内的单词数

4.4.2 需求二,统计topic1中10秒内的wordcount写到topic2

一个典型的场景就是,通过session会话的时间窗口统计用户访问网站的时长,对某个特定的用户来说,用户从登录开始,即该用户的窗口开始,直到发生退出或者会话超时,窗口期结束,可以统计在窗口期间发生的各种动作,比如点击某些按钮,浏览某个页面的时长等行为。

public class WindowStream2 {public static void main(String[] args) {Properties prop = new Properties();prop.put(StreamsConfig.APPLICATION_ID_CONFIG, "WindowCountStream");prop.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "IP:9092");prop.put(StreamsConfig.COMMIT_INTERVAL_MS_CONFIG, 2000);prop.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass());prop.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass());StreamsBuilder builder = new StreamsBuilder();KStream<Object, Object> source = builder.stream("zcy");KTable<Windowed<String>, Long> countTable = source.flatMapValues(value -> Arrays.asList(value.toString().split("\\s+"))).map((x, y) -> {return new KeyValue<>(y, "1");}).groupByKey().windowedBy(SessionWindows.with(Duration.ofSeconds(15).toMillis())).count();countTable.toStream().foreach((key, val) -> {System.out.println("key: " + key + "  val: " + val);});countTable.toStream().map((key, val) -> {return new KeyValue<String, String>(key.toString(), val.toString());}).to("zcy-out");KafkaStreams streams = new KafkaStreams(builder.build(), prop);streams.start();}}

4.5 Kafka Streams使用场景拓展

4.5.1 事件日志监控

假如现在需要对某系统中实时上报到topic-1的错误或告警日志进行转换,并输出到下游的topic-2中做大屏监控,如下为原始的从topic-1中获取到的日志数据格式

{"timestamp" : "2023-12-11 23:25:13","method": "GET","endpoint": "http://10.1.63.112:9098/fox/message/get","status_code": 500,"source_ip":"192.168.9.138","request_params":"type=5&status=3","operation_user":"6613"
}

假如下游的应用需要实时可视化用户请求日志,需要的数据格式如下:

{"ope_time": "2023-12-11 23:25:13","ope_user": [{"user_id": "6613", "source_ip": "192.168.9.138","endpoint":"http://10.1.63.112:9098/fox/message/get"}]
}

如果使用Kafka Stream来处理,可以考虑下面的思路

  • 根据业务需求对原始日志进行聚合和转换,重新组装结果的格式,并将结果写到下游的topic中;

  • 下游应用从topic中获取处理的结果,按照大屏的数据格式再次组装数据,最后展示到大屏;

4.5.2 事用户行为统计分析

比如某电商网站或app的后台需要统计用户某些指标的数据,从而分析用户的消费习惯为后续做促销提供数据决策依据,现在从原始的topic中可以拿到下面几类指标信息

{ "enter_type": app, "online_time": 16m, "user_type": "level_1" ,"buy_time_in_month":2,"user_id":1003
}

有了这些信息,就可以计算某种类型的用户,在过去一年内产生在app或网站来浏览的时长,购买的总次数,如果需要汇聚更多的信息,可以要求上游的topic中传入更详细的参数。

4.5.3 数据聚合与实时计算

kafka stream可以作为简单的实时计算框架,对数据进行准实时的聚合统计,快速汇总计算数据按业务维度进行数据分发,承载一部分大数据实时计算的功能。

4.5.4 实时推荐

基于现有的数据模型进行相关的指标计算,预测某些指标的行为,进一步指导业务决策,比如上面统计电商网站中用户的网站浏览动作。

4.5.5 实时告警

检测系统异常指标,通过准实时计算汇聚结果,将异常行为进行上报。

4.5.6 应用解耦

这个与消息中间件的作用类似,为了减少源系统的计算压力,可以通过kafka stream进行解耦,所有的计算动作在kafka stream中进行,然后再将计算结果推送到下游的topic进行后续的使用。

五、kafka stream整合springboot

有了上面对kafka stream的了解和使用,接下来演示下如何在springboot中整合kafka stream

5.1 整合过程

5.1.1 导入springboot相关依赖

 在上述已经导入的依赖的基础上补充下面几个依赖

        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoconfigure</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>

5.1.2 配置kafka相关信息

在配置文件中添加如下配置信息

server:port: 8088spring:application:name: kafka-sream-appkafka:bootstrap-servers: kafka连接IP:9092producer:retries: 5key-serializer: org.apache.kafka.common.serialization.StringSerializervalue-serializer: org.apache.kafka.common.serialization.StringSerializerconsumer:group-id: ${spring.application.name}-testkey-deserializer: org.apache.kafka.common.serialization.StringDeserializervalue-deserializer: org.apache.kafka.common.serialization.StringDeserializerkafka:hosts: kafka连接IP:9092group: ${spring.application.name}

5.1.3 添加Kafka Stream配置类


@Setter
@Getter
@Configuration
@EnableKafkaStreams
@ConfigurationProperties(prefix="kafka")
public class KafkaStreamConfig {private static final int MAX_MESSAGE_SIZE = 16 * 1024 * 1024;private String hosts;private String group;@Bean(name = KafkaStreamsDefaultConfiguration.DEFAULT_STREAMS_CONFIG_BEAN_NAME)public KafkaStreamsConfiguration defaultKafkaStreamsConfig() {Map<String, Object> props = new HashMap<>();props.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, hosts);props.put(StreamsConfig.APPLICATION_ID_CONFIG, this.getGroup()+"_stream_aid");props.put(StreamsConfig.CLIENT_ID_CONFIG, this.getGroup()+"_stream_id");props.put(StreamsConfig.RETRIES_CONFIG, 5);props.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass());props.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass());return new KafkaStreamsConfiguration(props);}
}

5.1.4 自定义Kafka Stream业务处理监听器

还记得在编写消息中间件客户端程序的时候添加的那些监听器吗,原理类似,这里自定义一个监听器处理类,接收上游的topic消息进行处理之后再发送到下一个topic中,相当于是把上面的代码搬过来放到spring的ioc容器中

import org.apache.kafka.streams.KeyValue;
import org.apache.kafka.streams.StreamsBuilder;
import org.apache.kafka.streams.kstream.KStream;
import org.apache.kafka.streams.kstream.TimeWindows;
import org.apache.kafka.streams.kstream.ValueMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.time.Duration;
import java.util.Arrays;@Configuration
public class StreamCountListener {@Beanpublic KStream<String, String> kStream(StreamsBuilder streamsBuilder) {KStream<String, String> stream = streamsBuilder.stream("zcy");stream.flatMapValues(new ValueMapper<String, Iterable<String>>() {@Overridepublic Iterable<String> apply(String value) {return Arrays.asList(value.split(" "));}})//按照value进行聚合处理.groupBy((key, value) -> value)//时间窗口.windowedBy(TimeWindows.of(Duration.ofSeconds(10)))//统计单词的个数.count()//转换为kStream.toStream().map((key, value) -> {System.out.println("key:" + key + " ,vlaue:" + value);return new KeyValue<>(key.key().toString(), value.toString());})//发送消息.to("zcy-out");return stream;}}

5.1.5 效果测试

运行项目,运行之后,使用下面的代码,往zcy这个topic中发送一些消息

public static void main(String[] args) throws Exception {// 1. 创建 kafka 生产者的配置对象Properties properties = new Properties();// 2. 给 kafka 配置对象添加配置信息:bootstrap.serversproperties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "IP:9092");properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");// 3. 创建 kafka 生产者对象KafkaProducer<String, String> kafkaProducer = new KafkaProducer<String, String>(properties);System.out.println("开始发送数据");// 4. 调用 send 方法,发送消息for (int i = 0; i < 5; i++) {kafkaProducer.send(new ProducerRecord<>("zcy","congge_" + i));}// 5. 关闭资源kafkaProducer.close();}

发送成功后,在控制台中可以看到经过上面的监听类处理得到的结果输出信息

六、写在文末

本篇通过较大得篇幅详细分享了kafka stream的使用,流式计算可以说是当下非常火热的技术之一,对于非大数据场景下的业务处理,kafka stream提供了一种很好的解决思路,希望对看到的同学有所帮助,本篇到此介绍,感谢观看。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/337249.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言知识总结一:C语言的基本知识汇总

点击蓝字关注我们来源于网络&#xff0c;侵删C语言是一种计算机程序设计语言。它既有高级语言的特点&#xff0c;又具有汇编语言的特点。它可以作 为系统设计语言&#xff0c;编写工作系统应用程序&#xff0c;也可以作为应用程序设计语言&#xff0c;编写不依赖计算机 硬件的应…

jboss8日志级别设置_罐中研讨会:设置JBoss BPM Suite全日研讨会

jboss8日志级别设置是否在寻找一种简单的方法来宣传&#xff0c;展示或演示JBoss业务流程管理套件&#xff08;BPM Suite&#xff09;产品的入门难度&#xff1f; 别无所求&#xff0c;因为我们已经召集了这个研讨会&#xff0c;因此您可以围绕JBoss BPM Suite构建一个晚上&a…

yapi 接口文档_1分钟docker部署顶尖 API 文档管理系统

YApi 是高效、易用、功能强大的 api 管理平台&#xff0c;旨在为开发、产品、测试人员提供更优雅的接口管理服务。可以帮助开发者轻松创建、发布、维护 API&#xff0c;YApi 还为用户提供了优秀的交互体验&#xff0c;开发人员只需利用平台提供的接口数据写入工具以及简单的点击…

微软CTO建议业界弃用C/C++采用Rust,遭C++之父回怼

点击蓝字关注我们来源于网络&#xff0c;侵删近日&#xff0c;Microsoft Azure CTO、Sysinternals 的主要开发者 Mark Russinovich 在其社交账号上发布动态称&#xff0c;开发人员是时候停止使用 C/C 来启动新项目&#xff0c;并建议可在需要使用 non-GC 语言的场景中使用 Rust…

红帽 jboss_红帽峰会2015所需的JBoss BPM内容指南

红帽 jboss明年再见&#xff1f; 今年在Red Hat Summit上&#xff0c;我们在JBoss BRMS和JBoss BPM Suite演讲中获得了很多乐趣。 在DevNation周围也有一些社区会议&#xff0c;重点介绍了使我们的产品成为可能的项目。 您可以在他们的博客上找到此演讲的概述&#xff0c;并…

Python、C、Java 和 C++ 四足鼎立,其他已无胜算? | TIOBE 10 月编程语言排行榜

点击蓝字关注我们来源于网络&#xff0c;侵删技术的千变万化&#xff0c;都是有迹可循的&#xff0c;最新的 TIOBE 十月编程语言榜单重磅发布&#xff0c;快来看看有哪些值得关注的变化吧&#xff01;四大编程语言不断增强其主导地位曾几何时&#xff0c;编程语言界中 Java、C、…

C语言 #define 和 typedef 区别

点击蓝字关注我们来源于网络&#xff0c;侵删在C语言编程中&#xff0c;typedef 和 #define是最常用语句&#xff0c;可能很多工作过几年的工程师都没有去深究过它们的一些用法和区别。typedef的用法在C/C语言中&#xff0c;typedef常用来定义一个标识符及关键字的别名&#xf…

Spring Batch –用JavaConfig替换XML作业配置

最近&#xff0c;我协助一个客户启动并运行了Spring Batch实现。 该团队决定继续使用针对批处理作业的基于JavaConfig的配置&#xff0c;而不是传统的基于XML的配置。 随着这越来越成为配置Java应用程序的一种常用方法&#xff0c;我觉得是时候更新Keyhole的Spring Batch系列了…

sql limit 子句_Java 8流中的常见SQL子句及其等效项

sql limit 子句功能编程允许使用通用语言进行准声明性编程 。 通过使用功能强大的流畅API&#xff08;例如Java 8的Stream API &#xff09;或jOOλ的顺序Stream扩展Seq或更复杂的库&#xff08;例如javaslang或functionaljava&#xff09; &#xff0c;我们可以以一种非常简洁…

C++编程中的核心知识点!

点击蓝字关注我们来源于网络&#xff0c;侵删尊重函数接口&#xff0c;尽量不作内部改动C代码语句分为&#xff1a;内置类型&#xff0c;名字&#xff0c;变量&#xff0c;操作符&#xff0c;标量&#xff0c;字符串&#xff0c;预处理指示&#xff08;如#include&#xff09;等…

C++ 语言的单元测试与代码覆盖率

点击蓝字关注我们来源于网络&#xff0c;侵删前言测试是软件开发过程中一个必须的环节&#xff0c;测试确保软件的质量符合预期。对于工程师自己来说&#xff0c;单元测试也是提升自信心的一种方式。直接交付没有经过测试的代码是不太好的&#xff0c;因为这很可能会浪费整个团…

C++ 模板(Template)总结,长点小知识

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删前言&#xff1a;大家好&#xff0c;今天给大家分享一篇关于 C 模板总结概述.模板&#xff08;Template&#xff09;指 C 程序设计设计语言中采用…

归并排序 java_归并排序(Java实现)

package primary_01;/** 归并排序* 1.算法实现* 2.验证算法的正确性* 3.分析算法的复杂度*/public class Merge_sort {public static void main(String[] args) {int arr [] {2,1,6,5,9,8,2020,199};sort(arr, 0, arr.length-1);for (int i : arr) {System.out.print(i" &…

C++17 常用新特性:带初始化的 if 和 switch 语句

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删早在2016-03-14&#xff0c;Thomas Kppe 在https://wg21.link/p0305r0中就提出了在if和switch使用初始化语句&#xff0c;使用时的代码示例如下&…

四大语言加强统治地位,Rust威胁C/C++

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删TIOBE 公布了 2022 年 10 月的编程语言排行榜。截至今日&#xff0c;Python、Java、C 和 C 语言已经在 TIOBE 榜单的前 4 位盘踞了相当长的一段时…

sql计算留存_SQL无所不能:DBA宝妈宝爸系列分享

原文链接&#xff1a;https://www.modb.pro/db/22315 目前程序从功能上其实已经完全满足客户(当然我这里的客户都是指媳妇儿)需求&#xff0c;具体可参考&#xff1a;使用SQL计算宝宝每次吃奶的时间间隔 - Part1&#xff1a;分析函数的使用使用SQL计算宝宝每次吃奶的时间间隔 -…

对 int 变量赋值的操作是原子的吗?为什么?

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删这个是在面试的时候遇到的问题&#xff0c;当时没有答出来。回到家以后查了查&#xff0c;整理记录下来。原问题&#xff1a;什么指令集支持原子…

C语言知识总结:if-else判断语句和switch-case选择语句

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删1、if-else判断语句一个基本的if语句由一个关键字if开头&#xff0c;跟上在括号&#xff08;&#xff09;里边的是表示逻辑条件的表达式&#xf…

hibernate性能_改善Hibernate应用程序性能的7种方法

hibernate性能Hibernate是将Java Web应用程序与SQL数据库集成的好工具&#xff0c;但是当应用程序的响应时间开始显着增加时&#xff0c;我们应该怎么做&#xff1f; 当我们怀疑应用程序是否会随着客户群的增长而扩展时&#xff0c;我们该怎么办&#xff1f; 在花大价钱托管或…

为什么永远不会有语言取代C/C++?

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删数以百计的 C/C 替代品将会出现&#xff0c;但 C/C 将永远与我们同在&#xff01;每个 CPU 都带有一种称为 ISA&#xff08;指令集架构&#xff…