本文分享如何使用KRaft部署Kafka集群,以及Spring中如何实现Kafka响应式交互。

KRaft

我们知道,Kafka使用Zookeeper负责为kafka存储broker,Consumer Group等元数据,并使用Zookeeper完成broker选主等操作。

虽然使用Zookeeper简化了Kafka的工作,但这也使Kafka的部署和运维更复杂。

Kafka 2.8.0开始移除了Zookeeper,并使用Kafka內部的仲裁(Quorum)控制器來取代ZooKeeper,官方称这个控制器为 "Kafka Raft metadata mode",即KRaft mode。从此用户可以在不需要Zookeeper的情况下部署Kafka集群,这使Fafka更加简单,轻量级。

使用KRaft模式后,用户只需要专注于维护Kafka集群即可。

注意:由于该功能改动较大,目前Kafka2.8版本提供的KRaft模式是一个测试版本,不推荐在生产环境使用。相信Kafka后续版本很快会提供生产可用的kraft版本。

下面介绍一下如果使用Kafka部署kafka集群。

这里使用3台机器部署3个Kafka节点,使用的Kafka版本为2.8.0。

  1. 生成ClusterId以及配置文件。

    (1)使用kafka-storage.sh生成ClusterId。
$ ./bin/kafka-storage.sh random-uuid
dPqzXBF9R62RFACGSg5c-Q

(2)使用ClusterId生成配置文件

$ ./bin/kafka-storage.sh format -t <uuid> -c ./config/kraft/server.properties
Formatting /tmp/kraft-combined-logs

注意:只需要在生成一个ClusterId,并使用该ClusterId在所有机器上生成配置文件,即集群中所有节点使用的ClusterId需相同。

  1. 修改配置文件

    脚本生成的配置文件只能用于单个Kafka节点,如果在部署Kafka集群,需要对配置文件进行一下修改。

(1)修改config/kraft/server.properties(稍后使用该配置启动kafka)

process.roles=broker,controller
node.id=1
listeners=PLAINTEXT://172.17.0.2:9092,CONTROLLER://172.17.0.2:9093
advertised.listeners=PLAINTEXT://172.17.0.2:9092
controller.quorum.voters=1@172.17.0.2:9093,2@172.17.0.3:9093,3@172.17.0.4:9093

process.roles指定了该节点角色,有以下取值

  • broker: 这台机器将仅仅当作一个broker
  • controller: 作为Raft quorum的控制器节点
  • broker,controller: 包含以上两者的功能

一个集群中不同节点的node.id需要不同。

controller.quorum.voters需要配置集群中所有的controller节点,配置格式为@:。

(2)

kafka-storage.sh脚本生成的配置,默认将kafka数据存放在/tmp/kraft-combined-logs/,

我们还需要/tmp/kraft-combined-logs/meta.properties配置中的node.id,使其与server.properties配置中保持一起。

node.id=1
  1. 启动kafka

    使用kafka-server-start.sh脚本启动Kafka节点
$ ./bin/kafka-server-start.sh ./config/kraft/server.properties

下面测试一下该kafka集群

  1. 创建主题
$ ./bin/kafka-topics.sh --create --partitions 3 --replication-factor 3 --bootstrap-server 172.17.0.2:9092,172.17.0.3:9092,172.17.0.4:9092 --topic topic1
  1. 生产消息
$ ./bin/kafka-console-producer.sh --broker-list 172.17.0.2:9092,172.17.0.3:9092,172.17.0.4:9092 --topic topic1
  1. 消费消息
$ ./bin/kafka-console-consumer.sh --bootstrap-server 172.17.0.2:9092,172.17.0.3:9092,172.17.0.4:9092 --topic topic1 --from-beginning

这部分命令的使用与低版本的Kafka保持一致。

Kafka的功能暂时还不完善,这是展示一个简单的部署示例。

Kafka文档:https://github.com/apache/kafka/blob/trunk/config/kraft/README.md

Spring中可以使用Spring-Kafka、Spring-Cloud-Stream两个框架实现kafka响应式交互。

下面分别看一下这两个框架的使用。

Spring-Kafka

  1. 添加引用

    添加spring-kafka引用
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
<version>2.5.8.RELEASE</version>
</dependency>
  1. 准备配置文件,内容如下
spring.kafka.producer.bootstrap-servers=172.17.0.2:9092,172.17.0.3:9092,172.17.0.4:9092
spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.LongSerializer
spring.kafka.producer.value-serializer=org.springframework.kafka.support.serializer.JsonSerializer spring.kafka.consumer.bootstrap-servers=172.17.0.2:9092,172.17.0.3:9092,172.17.0.4:9092
spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.LongDeserializer
spring.kafka.consumer.value-deserializer=org.springframework.kafka.support.serializer.JsonDeserializer
spring.kafka.consumer.group-id=warehouse-consumers
spring.kafka.consumer.properties.spring.json.trusted.packages=*

分别是生产者和消费者对应的配置,很简单。

  1. 发送消息

    Spring-Kakfa中可以使用ReactiveKafkaProducerTemplate发送消息。

    首先,我们需要创建一个ReactiveKafkaProducerTemplate实例。(目前SpringBoot会自动创建KafkaTemplate实例,但不会创建ReactiveKafkaProducerTemplate实例)。
@Configuration
public class KafkaConfig {
@Autowired
private KafkaProperties properties; @Bean
public ReactiveKafkaProducerTemplate reactiveKafkaProducerTemplate() {
SenderOptions options = SenderOptions.create(properties.getProducer().buildProperties());
ReactiveKafkaProducerTemplate template = new ReactiveKafkaProducerTemplate(options);
return template;
}
}

KafkaProperties实例由SpringBoot自动创建,读取上面配置文件中对应的配置。

接下来,就可以使用ReactiveKafkaProducerTemplate发送消息了

    @Autowired
private ReactiveKafkaProducerTemplate template; public static final String WAREHOUSE_TOPIC = "warehouse";
public Mono<Boolean> add(Warehouse warehouse) {
Mono<SenderResult<Void>> resultMono = template.send(WAREHOUSE_TOPIC, warehouse.getId(), warehouse);
return resultMono.flatMap(rs -> {
if(rs.exception() != null) {
logger.error("send kafka error", rs.exception());
return Mono.just(false);
}
return Mono.just(true);
});
}

ReactiveKafkaProducerTemplate#send方法返回一个Mono(这是Spring Reactor中的核心对象),Mono中携带了SenderResult,SenderResult中的RecordMetadata、exception存储该记录的元数据(包括offset、timestamp等信息)以及发送操作的异常。

  1. 消费消息

    Spring-Kafka使用ReactiveKafkaConsumerTemplate消费消息。
@Service
public class WarehouseConsumer {
@Autowired
private KafkaProperties properties; @PostConstruct
public void consumer() {
ReceiverOptions<Long, Warehouse> options = ReceiverOptions.create(properties.getConsumer().buildProperties());
options = options.subscription(Collections.singleton(WarehouseService.WAREHOUSE_TOPIC));
new ReactiveKafkaConsumerTemplate(options)
.receiveAutoAck()
.subscribe(record -> {
logger.info("Warehouse Record:" + record);
});
}
}

这里与之前使用@KafkaListener注解实现的消息监听者不同,不过也非常简单,分为两个步骤:

(1)ReceiverOptions#subscription方法将ReceiverOptions关联到kafka主题

(2)创建ReactiveKafkaConsumerTemplate,并注册subscribe的回调函数消费消息。

提示:receiveAutoAck方法会自动提交消费组offset。

Spring-Cloud-Stream

Spring-Cloud-Stream是Spring提供的用于构建消息驱动微服务的框架。

它为不同的消息中间件产品提供一种灵活的,统一的编程模型,可以屏蔽底层不同消息组件的差异,目前支持RabbitMQ、Kafka、RocketMQ等消息组件。

这里简单展示Spring-Cloud-Stream中实现Kafka响应式交互的示例,不深入介绍Spring-Cloud-Stream的应用。

  1. 引入spring-cloud-starter-stream-kafka的引用
    <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-kafka</artifactId>
</dependency>
  1. 添加配置
spring.cloud.stream.kafka.binder.brokers=172.17.0.2:9092,172.17.0.3:9092,172.17.0.4:9092
spring.cloud.stream.bindings.warehouse2-out-0.contentType=application/json
spring.cloud.stream.bindings.warehouse2-out-0.destination=warehouse2
# 消息格式
spring.cloud.stream.bindings.warehouse3-in-0.contentType=application/json
# 消息目的地,可以理解为Kafka主题
spring.cloud.stream.bindings.warehouse3-in-0.destination=warehouse2
# 定义消费者消费组,可以理解为Kafka消费组
spring.cloud.stream.bindings.warehouse3-in-0.group=warehouse2-consumers
# 映射方法名
spring.cloud.function.definition=warehouse2;warehouse3

Spring-Cloud-Stream 3.1版本之后,@EnableBinding、@Output等StreamApi注解都标记为废弃,并提供了一种更简洁的函数式编程模型。

该版本后,用户不需要使用注解,只要在配置文件中指定需要绑定的方法,Spring-Cloud-Stream会为用户将这些方法与底层消息组件绑定,用户可以直接调用这些方法发送消息,或者接收到消息时Spring-Cloud-Stream会调用这些方法消费消息。

通过以下格式定义输入、输出函数的相关属性:

输出(发送消息):<functionName> + -out- + <index>

输入(消费消息):<functionName> + -in- + <index>

对于典型的单个输入/输出函数,index始终为0,因此它仅与具有多个输入和输出参数的函数相关。

Spring-Cloud-Stream支持具有多个输入(函数参数)/输出(函数返回值)的函数。

spring.cloud.function.definition配置指定需要绑定的方法名,不添加该配置,Spring-Cloud-Stream会自动尝试绑定返回类型为Supplier/Function/Consumer的方法,但是使用该配置可以避免Spring-Cloud-Stream绑定混淆。

  1. 发送消息

    用户可以编写一个返回类型为Supplier的方法,并定时发送消息
    @PollableBean
public Supplier<Flux<Warehouse>> warehouse2() {
Warehouse warehouse = new Warehouse();
warehouse.setId(333L);
warehouse.setName("天下第一仓");
warehouse.setLabel("一级仓"); logger.info("Supplier Add : {}", warehouse);
return () -> Flux.just(warehouse);
}

定义该方法后,Spring-Cloud-Stream每秒调用一次该方法,生成Warehouse实例,并发送到Kafka。

(这里方法名warehouse3已经配置在spring.cloud.function.definition中。)

通常场景下,应用并不需要定时发送消息,而是由业务场景触发发送消息操作, 如Rest接口,

这时可以使用StreamBridge接口

    @Autowired
private StreamBridge streamBridge; public boolean add2(Warehouse warehouse) {
return streamBridge.send("warehouse2-out-0", warehouse);
}

暂时未发现StreamBridge如何实现响应式交互。

  1. 消费消息

    应用要消费消息,只需要定义一个返回类型为Function/Consumer的方法即可。如下
    @Bean
public Function<Flux<Warehouse>, Mono<Void>> warehouse3() {
Logger logger = LoggerFactory.getLogger("WarehouseFunction");
return flux -> flux.doOnNext(data -> {
logger.info("Warehouse Data: {}", data);
}).then();
}

注意:方法名与<functionName> + -out- + <index>/<functionName> + -in- + <index>

spring.cloud.function.definition中的配置需要保持一致,以免出错。

SpringCloudStream文档:https://docs.spring.io/spring-cloud-stream/docs/3.1.0/reference/html/spring-cloud-stream.html

文章完整代码:https://gitee.com/binecy/bin-springreactive/tree/master/warehouse-service

如果您觉得本文不错,欢迎关注我的微信公众号,系列文章持续更新中。您的关注是我坚持的动力!

Reactive Spring实战 -- 响应式Kafka交互的更多相关文章

  1. Reactive Spring实战 -- 响应式Redis交互

    本文分享Spring中如何实现Redis响应式交互模式. 本文将模拟一个用户服务,并使用Redis作为数据存储服务器. 本文涉及两个java bean,用户与权益 public class User ...

  2. Reactive Spring实战 -- 响应式MySql交互

    本文与大家探讨Spring中如何实现MySql响应式交互. Spring Data R2DBC项目是Spring提供的数据库响应式编程框架. R2DBC是Reactive Relational Dat ...

  3. Reactive 理解 SpringBoot 响应式的核心-Reactor

    Reactive 理解 SpringBoot 响应式的核心-Reactor bestcoding 2020-02-23 17:26:43 一.前言 关于 响应式 Reactive,前面的两篇文章谈了不 ...

  4. 第二百五十一节,Bootstrap项目实战--响应式轮播图

    Bootstrap项目实战--响应式轮播图 学习要点: 1.响应式轮播图 本节课我们要在导航条的下方做一张轮播图,自动播放最新的重要动态. 一.响应式轮播图 响应式轮播图 第一步,设置轮播器区域car ...

  5. 第二百五十节,Bootstrap项目实战--响应式导航

    Bootstrap项目实战--响应式导航 学习要点: 1.响应式导航 一.响应式导航 基本导航组件+响应式 第一步,声明导航区域,设置导航默认样式,设置导航条固定在顶部navbar样式class类,写 ...

  6. Java9第四篇-Reactive Stream API响应式编程

    我计划在后续的一段时间内,写一系列关于java 9的文章,虽然java 9 不像Java 8或者Java 11那样的核心java版本,但是还是有很多的特性值得关注.期待您能关注我,我将把java 9 ...

  7. Spring 5 响应式编程

    要点 Reactor 是一个运行在 Java8 之上的响应式流框架,它提供了一组响应式风格的 API 除了个别 API 上的区别,它的原理跟 RxJava 很相似 它是第四代响应式框架,支持操作融合, ...

  8. Reactive(1) 从响应式编程到"好莱坞"

    目录 概念 面向流设计 异步化 响应式宣言 参考文档 概念 Reactive Programming(响应式编程)已经不是一个新东西了. 关于 Reactive 其实是一个泛化的概念,由于很抽象,一些 ...

  9. Reactive Spring实战 -- WebFlux使用教程

    WebFlux是Spring 5提供的响应式Web应用框架. 它是完全非阻塞的,可以在Netty,Undertow和Servlet 3.1+等非阻塞服务器上运行. 本文主要介绍WebFlux的使用. ...

随机推荐

  1. VMware Workstation中安装Hyper-V

    1:在虚拟机设置中,CPU属性中勾选"Virtualize Intel VT-x/EPT or AMD-V/RVI"来启用虚拟机的CPU支持虚拟化. 2:2.在虚拟机文件所在目录中 ...

  2. Jenkins 基础篇 - 安装部署

    Jenkins 安装 Jenkins 支持主流的 Linux 发行版系统,同时还支持 macOS.Windows.和 Docker 运行. 具体系统的 Jenkins 安装包可以去官网下载 https ...

  3. 使用PuTTY连接Azure VM

    使用PuTTY连接Azure VMhtml { -webkit-print-color-adjust: exact } * { box-sizing: border-box; -webkit-prin ...

  4. 进程Queue和线程Queue区别

    进程Queue from multiprocessing import Queue q=Queue() 线程Queue import queue q=queue.Queue()

  5. 排坑&#183;IPhone&IOS中不兼容正则中的断言匹配

    阅文时长 | 1.14分钟 字数统计 | 1834.4字符 主要内容 | 1.问题切入 2.什么是断言匹配 3.断言匹配的替换方案 4.声明与参考资料 『排坑·IPhone&IOS中不兼容正则 ...

  6. C++知识点案例 笔记-4

    1.纯虚函数 2.抽象类 3.内部类 4.运算符重载 5.类的函数重载 6.友元的函数重载 1.纯虚函数 ==纯虚函数== //有时基类中无法给出函数的具体体现,定义纯虚函数可以为派生函数保留一个函数 ...

  7. kvm虚拟机管理(3)

    一.远程管理kvm虚拟机 (1)上一节我们通过 virt-manager 在本地主机上创建并管理 KVM 虚机.其实 virt-manager 也可以管理其他宿主机上的虚机.只需要简单的将宿主机添加进 ...

  8. linux 解压总结

    tar解压 gz解压 bz2等各种解压文件使用方法 .tar 解包:tar xvf FileName.tar 打包:tar cvf FileName.tar DirName (注:tar是打包,不是压 ...

  9. Linux(CentOS 7) 安全加固之非业务端口服务关闭 postfix port 25

    目录 关闭TCP 25 端口对应的服务 1. 确认对应端口的进程 2. 查找与关闭对应服务 3. 确认结果,端口已关闭 关闭TCP 25 端口对应的服务 [0 root@Qvps /root] #ca ...

  10. shell脚本编写习惯

    前言:在公众号看一篇比较不错的shell脚本文章,自己学习同时,加一些例子分享下,哪里做得不好,请多多指教哈一.在脚本写注释 1 #脚本的参数 2 #脚本的用途 3 #脚本的注意事项 4 #脚本的写作 ...