本文分享如何使用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. Spring cloud 基础框架集成

    Spring cloud 基础框架集成 1. 注册中心 -eurekar 1. pom依赖 <?xml version="1.0" encoding="UTF-8& ...

  2. 【技术博客】基于vue的前端快速开发(工具篇)

    一.Vue教程 vue.js是一套构建用户界面的渐进式框架.vue采用自底向上增量开发的设计.vue的核心库只关心视图层,非常容易学习,非常容易与其它库和已有项目整合.vue完全有能力驱动采用单文件组 ...

  3. CRM数据分析的重要作用

    优秀的管理者都知道企业想要实现业务大幅增长不是一件容易的事情.这往往需要通过明智的决策和正确的时机才能够实现.所以,您需要有洞察正确的时间和制定正确决策的能力,这样才能确保您做出正确的决定. CRM系 ...

  4. 关于flask的模板注入的学习

    flask模板注入的学习 关于flask模版注入,之前不太理解,看了很多文章才弄懂,主要原理就是渲染函数的参数用户可控就造成了模板注入 就会使用户构造恶意的代码进行逃逸从而进行攻击 flask模板渲染 ...

  5. DNS和BIND

    https://www.jianshu.com/p/296b2c7ea76f DNS和BIND 毛利卷卷发关注 0.482018.07.25 10:33:44字数 4,919阅读 4,909 DNS ...

  6. IIS 10 设置FTP Passive Mode 固定端口

    1. Run [Start] - [Server Manager] and Click [Tools] - [Internet Information Services (IIS) Manager]. ...

  7. Linux进阶之软件管理

    本节内容 一.rpm:管理linux软件程序的 特点:安装方便 不能解决依赖关系 1.安装软件: -i: -v: -h: rpm -ivh 包名 2.卸载软件 -e: 清楚 rpm -e 程序名 3. ...

  8. Java 中布尔(boolean)类型占用多少个字节

    为什么要问这个问题,首先在Java中定义的八种基本数据类型中,除了其它七种类型都有明确的内存占用字节数外,就 boolean 类型没有给出具体的占用字节数,因为对虚拟机来说根本就不存在 boolean ...

  9. 对狂神说的MybatisPlus的学习总结

    1.什么是MybatisPlus? 需要的基础:spring,spring mvc,mybatis 作用:可以节省大量的工作时间,所有的CRUD代码都可以自动完成,简化Mybatis MyBatis- ...

  10. DM8_Linux详细安装步骤

    (从虚拟机配置讲起,有基础的可以直接看二,谢谢) 一.虚拟机的安装和配置 软件:virtualbox 系统:centos7 工具:Xshell 官网下载centos7,使用virtualbox安装ce ...