总体步骤:
- springboot 项目中导入 kafka Maven依赖;
- 编写配置文件;
- 创建消息生产者;
- 创建消息消费者;
- 通过调用生产者往kafka内发送消息,并在消费者中消费打印消息;
以下代码实现内容借鉴 简书博客:
springboot 之集成kafka
导入maven 依赖
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
编写配置文件
server:
port: 7007
spring:
kafka:
bootstrap-servers: 127.0.0.1:9092
producer:
# 发生错误后,消息重发的次数。
retries: 0
#当有多个消息需要被发送到同一个分区时,生产者会把它们放在同一个批次里。该参数指定了一个批次可以使用的内存大小,按照字节数计算。
batch-size: 16384
# 设置生产者内存缓冲区的大小。
buffer-memory: 33554432
# 键的序列化方式
key-serializer: org.apache.kafka.common.serialization.StringSerializer
# 值的序列化方式
value-serializer: org.apache.kafka.common.serialization.StringSerializer
# acks=0 : 生产者在成功写入消息之前不会等待任何来自服务器的响应。
# acks=1 : 只要集群的首领节点收到消息,生产者就会收到一个来自服务器成功响应。
# acks=all :只有当所有参与复制的节点全部收到消息时,生产者才会收到一个来自服务器的成功响应。
acks: 1
consumer:
# 自动提交的时间间隔 在spring boot 2.X 版本中这里采用的是值的类型为Duration 需要符合特定的格式,如1S,1M,2H,5D
auto-commit-interval: 1S
# 该属性指定了消费者在读取一个没有偏移量的分区或者偏移量无效的情况下该作何处理:
# latest(默认值)在偏移量无效的情况下,消费者将从最新的记录开始读取数据(在消费者启动之后生成的记录)
# earliest :在偏移量无效的情况下,消费者将从起始位置读取分区的记录
auto-offset-reset: earliest
# 是否自动提交偏移量,默认值是true,为了避免出现重复数据和数据丢失,可以把它设置为false,然后手动提交偏移量
enable-auto-commit: false
# 键的反序列化方式
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
# 值的反序列化方式
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
listener:
# 在侦听器容器中运行的线程数。
concurrency: 5
#listner负责ack,每调用一次,就立即commit
ack-mode: manual_immediate
missing-topics-fatal: false
编写消息生产者
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.support.SendResult;
import org.springframework.stereotype.Component;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureCallback;
/**
* 生产者
*/
@Component
@Slf4j
public class KfkProductService {
@Autowired
private KafkaTemplate<String, Object> kafkaTemplate;
//发送消息主题
public static final String TOPIC_TEST = "topic.test";
public void send(Object obj) {
String obj2String = JSONObject.toJSONString(obj);
log.info("准备发送消息为:{}", obj2String);
//发送消息
ListenableFuture<SendResult<String, Object>> future = kafkaTemplate.send(TOPIC_TEST, obj);
future.addCallback(new ListenableFutureCallback<SendResult<String, Object>>() {
public void onFailure(Throwable throwable) {
//发送失败的处理
log.info(TOPIC_TEST + " - 生产者 发送消息失败:" + throwable.getMessage());
//todo ...
}
public void onSuccess(SendResult<String, Object> stringObjectSendResult) {
//成功的处理
log.info(TOPIC_TEST + " - 生产者 发送消息成功:" + stringObjectSendResult.toString());
//todo ...
}
});
}
}
编写消息消费者
import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.kafka.support.Acknowledgment;
import org.springframework.kafka.support.KafkaHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;
import java.util.Optional;
/**
* 消费者
*/
@Component
@Slf4j
public class KfkConsumerService {
//消费的消息主题
public static final String TOPIC_TEST = "topic.test";
//消费组1
public static final String TOPIC_GROUP1 = "topic.group1";
//消费组2
public static final String TOPIC_GROUP2 = "topic.group2";
/**
* 消费者1
* @param record
* @param ack
* @param topic
*/
@KafkaListener(topics = TOPIC_TEST, groupId =TOPIC_GROUP1)
public void topic_test(ConsumerRecord<?, ?> record, Acknowledgment ack, @Header(KafkaHeaders.RECEIVED_TOPIC) String topic) {
Optional message = Optional.ofNullable(record.value());
if (message.isPresent()) {
Object msg = message.get();
log.info("消费者1 消费了: Topic:" + topic + ",Message:" + msg);
ack.acknowledge();
}
}
/**
* 消费者2
* @param record
* @param ack
* @param topic
*/
@KafkaListener(topics = TOPIC_TEST, groupId = TOPIC_GROUP2)
public void topic_test1(ConsumerRecord<?, ?> record, Acknowledgment ack, @Header(KafkaHeaders.RECEIVED_TOPIC) String topic) {
Optional message = Optional.ofNullable(record.value());
if (message.isPresent()) {
Object msg = message.get();
log.info("消费者2 消费了: Topic:" + topic + ",Message:" + msg);
ack.acknowledge();
}
}
}
编写controller调用生产者推送消息
import com.codeLead.kfk.service.KfkProductService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/product")
@Slf4j
public class ProductController {
@Autowired
private KfkProductService kfkProductService;
@RequestMapping("/sendKfkMsg")
public void test(@RequestParam("msg") String msg){
kfkProductService.send(msg);
}
}
调用推送消息,并观察消费者消费情况
浏览器get带参调用
日志打印消费情况
windows 内下载安装kafka(单机版)
kafka 官方下载地址:https://kafka.apache.org/downloads.html
1,下载安装 安装包;
我本地下载的是2.1.2-2.6.0 版本
下载到本地后解压缩即可
2,启动 kafka (windows 环境下启动脚本在 bin目录中的windows目录下)
-
启动zookeeper服务;
(下载的kafka包中内置有zookeeper服务,使用命令启动即可。注意,启动时需要指定配置文件。)进入windows 目录打开黑窗口执行以下命令(所有的配置文件在 bin目录下的 config 目录下,启动时指定下配置文件,内容可以不用修改)
zookeeper-server-start.bat E:\App\kafka\kafka_2.12-2.6.0\config\zookeeper.properties
- 启动 kafka 服务;
进入windows 目录再打开一个黑窗口执行以下命令(启动时需要指定配置文件,配置文件内容可以不用修改)
kafka-server-start.bat E:\App\kafka\kafka_2.12-2.6.0\config\server.properties
3,创建一个topic ;
此处创建一个 主题名 为 wwftopic,分区数量为 1 的主题;(相关命令及对应含义可参阅官方文档)
进入windows 目录再打开一个黑窗口执行以下命令
kafka-topics.bat --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic wwftopic
创建成功
查看已创建的主题
kafka-topics.bat --list --zookeeper 127.0.0.1:2181
4,启动一个生产者;
启动一个生产者,往刚刚新建的主题内发送消息;
进入windows 目录再打开一个黑窗口执行以下命令
kafka-console-producer.bat --broker-list 127.0.0.1:9092 --topic wwftopic
往 wwftopic 主题发了一句话 this is my send
5,启动一个消费者
启动一个消费者,来接受消费指定主题内的消息
进入windows 目录再打开一个黑窗口执行以下命令
kafka-console-consumer.bat --bootstrap-server 127.0.0.1:9092 --topic wwftopic
执行命令后发现并没有收到刚刚发送的那句话??
这个命令只能收到实时发送过来的消息。要想拿到历史的消息,可以在命令行加上参数,让它从头开始读取消息即可;
kafka-console-consumer.bat --bootstrap-server 127.0.0.1:9092 --topic wwftopic --from-beginning
over…
来个面试题;
1,zookeeper 在 kafka 中的作用?
用来注册记录kafka 集群信息,Kafka通过Zookeeper管理集群配置,选举leader;