事件处理:SpringBoot+Kafka

文章目录

  • 关于Kafka
  • 安装kafka服务
  • 实战


关于Kafka

  1. kafka官网:http://kafka.apache.org/
  2. 是一个分布式的、分区的、多副本的、多订阅者,基于zookeeper协调的分布式日志系统(也可以当做MQ系统),常用于web/nginx日志、访问日志、消息服务等等。
  3. 高吞吐量、低延迟、可扩展性、持久性、可靠性、容错性、高并发。
    事件处理:SpringBoot+Kafka_第1张图片
    【图源来自网络】每个broker是kafka服务器一个节点,每条消息是发布到topic上【topic可以理解为类别,生产者将消息发到kafka上会指定一个类别】,每个topic上有一个或多个partition分区,发送到topic上的消息会根据分区策略追加到分区的末尾【顺序写磁盘,效率高,在创建topic时可指定parition数量】。

其他属性:
offset: partition中的每条消息都被标记了一个序号,这个序号表示消息在partition中的偏移量,称为offset,每一条消息在partition都有唯一的offset,消息者通过指定offset来指定要消费的消息。【消费者在消费完一条消息后会递增offset,准备去消费下一条消息,但也可以将offset设成一个较小的值,重新消费一些消费过的消息。

producer: 生产者,生产者发送消息到指定的topic下,消息再根据分配规则append到某个partition的末尾。

consumer: 消费者,消费者从topic中消费数据。

consumer group: 消费者组,每个consumer属于一个特定的consumer group,可为每个consumer指定consumer group,若不指定则属于默认的group。

同一topic的一条消息只能被同一个consumer group内的一个consumer消费,但多个consumer group可同时消费这一消息。这也是kafka用来实现一个topic消息的广播和单播的手段,如果需要实现广播,一个consumer group内只放一个消费者即可,要实现单播,将所有的消费者放到同一个consumer group即可。
用consumer group还可以将consumer进行自由的分组而不需要多次发送消息到不同的topic。

leader: 每个partition有多个副本,其中有且仅有一个作为leader,leader会负责所有的客户端读写操作。

follower: follower不对外提供服务,只与leader保持数据同步,如果leader失效,则选举一个follower来充当新的leader。当follower与leader挂掉、卡住或者同步太慢,leader会把这个follower从ISR列表中删除,重新创建一个follower。

rebalance: 同一个consumer group下的多个消费者互相协调消费工作,我们这样想,一个topic分为多个分区,一个consumer group里面的所有消费者合作,一起去消费所订阅的某个topic下的所有分区(每个消费者消费部分分区),kafka会将该topic下的所有分区均匀的分配给consumer group下的每个消费者。
rebalance表示"重平衡",consumer group内某个消费者挂掉后,其他消费者自动重新分配订阅主题分区的过程,是 Kafka 消费者端实现高可用的重要手段。

上述参考文章:
https://mp.weixin.qq.com/s?__biz=MzU1NDA0MDQ3MA==&mid=2247483958&idx=1&sn=dffaad318b50f875eea615bc3bdcc80c&chksm=fbe8efcfcc9f66d9ff096fbae1c2a3671f60ca4dc3e7412ebb511252e7193a46dcd4eb11aadc&scene=21


安装kafka服务

  1. 安装环境:VMware虚拟机CentOS7
  2. 安装方式:docker下安装
  3. 关于Linux安装docker可参考文章:虚拟机Linux环境下安装docker以及docker下mysql、redis的安装
  4. 进入虚拟机,启动docker:systemctl start docker
  5. 安装zookeeper和kafka:
su #进入root用户
systemctl start docker #启动docker
docker pull wurstmeister/zookeeper #安装zookeeper
docker pull wurstmeister/kafka #安装kafka
docker run -d --name zookeeper -p 2181:2181  -t wurstmeister/zookeeper #启动zk

# 起动kafka 服务器地址换成自己虚拟机的
docker run -d --name kafka --publish 9092:9092 --link zookeeper:zookeeper -e KAFKA_BROKER_ID=1  -e HOST_IP=192.168.33.114  -e KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181 -e KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://192.168.33.114:9092  -e KAFKA_ADVERTISED_HOST_NAME=192.168.33.114 -e KAFKA_ADVERTISED_PORT=9082  --restart=always  -t  wurstmeister/kafka

docker ps查看启动的服务:
事件处理:SpringBoot+Kafka_第2张图片
启动kafka后,进入kafka容器,进入bin目录:
事件处理:SpringBoot+Kafka_第3张图片
到bin下后,开始创建topic:
创建两个topic:topic1、topic2,其分区卫和副本数为1。【记得将服务器地址修改成自己虚拟机的】

 ./kafka-topics.sh --create --zookeeper 192.168.33.114:2181 --replication-factor 1 --partitions 2 --topic topic1

./kafka-topics.sh --create --zookeeper 192.168.33.114:2181 --replication-factor 1 --partitions 2 --topic topic2

创建topic可由代码进行创建,不输入命令也行。在发送消息时,kafka会帮我们自动完成topic的创建工作,但这种情况下创建的topic默认只有一个分区,分区也没有副本。如果需要设置多个分区和副本需要进行配置。


实战

事件处理:SpringBoot+Kafka_第4张图片

  1. pom.xml【spring boot依赖和kafka依赖】
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.6.10</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.kafka</groupId>
            <artifactId>spring-kafka</artifactId>
            <version>2.8.5</version>
        </dependency>
    </dependencies>

这里一定要注意kafka与springboot版本,一定要一一对应。参考网址:https://spring.io/projects/spring-kafka
事件处理:SpringBoot+Kafka_第5张图片
不然会启动不起来,报下面错误:
事件处理:SpringBoot+Kafka_第6张图片
2. kafka配置:【application.properties】

spring.application.name=kafka-demo
server.port=8081
###########【Kafka集群】###########
spring.kafka.bootstrap-servers=192.168.33.114:9092
###########【初始化生产者配置】###########
# 重试次数
spring.kafka.producer.retries=0
# 应答级别:多少个分区副本备份完成时向生产者发送ack确认(可选0、1、all/-1)
spring.kafka.producer.acks=1
# 批量大小
spring.kafka.producer.batch-size=16384
# 提交延时
spring.kafka.producer.properties.linger.ms=0
# 当生产端积累的消息达到batch-size或接收到消息linger.ms后,生产者就会将消息提交给kafka
# linger.ms为0表示每接收到一条消息就提交给kafka,这时候batch-size其实就没用了
​
# 生产端缓冲区大小
spring.kafka.producer.buffer-memory = 33554432
# Kafka提供的序列化和反序列化类
spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer
spring.kafka.producer.value-serializer=org.apache.kafka.common.serialization.StringSerializer
# 自定义分区器
# spring.kafka.producer.properties.partitioner.class=com.felix.kafka.producer.CustomizePartitioner
​
###########【初始化消费者配置】###########
# 默认的消费组ID
spring.kafka.consumer.properties.group.id=defaultConsumerGroup
# 是否自动提交offset
spring.kafka.consumer.enable-auto-commit=true
# 提交offset延时(接收到消息后多久提交offset)
spring.kafka.consumer.auto.commit.interval.ms=1000
# 当kafka中没有初始offset或offset超出范围时将自动重置offset
# earliest:重置为分区中最小的offset;
# latest:重置为分区中最新的offset(消费分区中新产生的数据);
# none:只要有一个分区不存在已提交的offset,就抛出异常;
spring.kafka.consumer.auto-offset-reset=latest
# 消费会话超时时间(超过这个时间consumer没有发送心跳,就会触发rebalance操作)
spring.kafka.consumer.properties.session.timeout.ms=120000
# 消费请求超时时间
spring.kafka.consumer.properties.request.timeout.ms=180000
# Kafka提供的序列化和反序列化类
spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer
spring.kafka.consumer.value-deserializer=org.apache.kafka.common.serialization.StringDeserializer
# 消费端监听的topic不存在时,项目启动会报错(关掉)
spring.kafka.listener.missing-topics-fatal=false
# 设置批量消费
# spring.kafka.listener.type=batch
# 批量消费每次最多消费多少条消息
# spring.kafka.consumer.max-poll-records=50
  1. kafka其他配置KafkaTopicConfiguration【创建topic】
/**
 * 

KafkaTopicConfiguration

*

kafka配置类

* * @author : he zhe * @date : 2022-08-01 17:18 **/
@Configuration public class KafkaTopicConfiguration { /** * 创建 KafkaAmin,可以自动检测集群中是否存在topic,不存在则创建 * @return */ @Bean public KafkaAdmin kafkaAdmin() { Map<String, Object> props = new HashMap<>(); props.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.33.114:9092"); return new KafkaAdmin(props); } @Bean public NewTopic newTopic() { // 创建 topic,指定 名称、分区数、副本数 return new NewTopic("hello-kafka-test-topic", 2, (short) 1); } }
  1. 生产者【通过接口控制】
/**
 * 

KafkaProducer

*

生产者

* * @author : he zhe * @date : 2022-08-01 16:49 **/
@RestController public class KafkaProducer { @Autowired private KafkaTemplate<String, Object> kafkaTemplate; /** * 发生简单信息 * @param normalMessage */ @RequestMapping(value = "/kafka/normal/{message}", method = RequestMethod.GET) public void sendMessage1(@PathVariable("message") String normalMessage) { kafkaTemplate.send("topic1", normalMessage); } /** * 异步发送:带回调的发生消息 * @param callbackMessage */ @GetMapping("/kafka/callbackOne/{message}") public void sendMessage2(@PathVariable("message") String callbackMessage) { kafkaTemplate.send("topic1", callbackMessage).addCallback(success -> { // 消息发送到的topic String topic = success.getRecordMetadata().topic(); // 消息发送到的分区 int partition = success.getRecordMetadata().partition(); // 消息在分区内的offset long offset = success.getRecordMetadata().offset(); System.out.println("发送消息成功:" + topic + "-" + partition + "-" + offset); }, failure -> { System.out.println("发送消息失败:" + failure.getMessage()); }); } /** * 事务处理 * @param message */ @GetMapping("/kafka/transaction/{message}") public void sendMessage3(@PathVariable("message") String message) { // 声明事务:operations函数报错,消息就不会发出去。 kafkaTemplate.executeInTransaction(operations -> { //数据发往kafka operations.send("topic1",message); //模拟后续业务处理发生了异常 throw new RuntimeException("fail"); }); } }
  1. 消费者KafkaConsumer【对生产进行监听】
/**
 * 

KafkaConsumer

*

消费者

* * @author : he zhe * @date : 2022-08-01 16:51 **/ @Service public class KafkaConsumer { // 消费监听 @KafkaListener(topics = {"topic1"}) public void onMessage1(ConsumerRecord record){ // 消费的哪个topic、partition的消息,打印出消息内容 System.out.println("简单消费:"+record.topic()+"-"+record.partition()+"-"+record.value()); } }
  1. 启动类
/**
 * 

KafkaAppliction

*

启动类

* * @author : he zhe * @date : 2022-08-01 16:43 **/ @SpringBootApplication public class KafkaApplication { public static void main(String[] args) { SpringApplication.run(KafkaApplication.class, args); System.out.println("KafkaApplication启动成功"); } }
  1. 启动项目:
    (1)访问:GET http://localhost:8081/kafka/normal/Hello【像kafka的topic1发送一条消息】
    (2)消费者监听到topic1产生了一条消息,进行消费,打印在控制台
    事件处理:SpringBoot+Kafka_第7张图片
    (3)访问GET http://localhost:8081/kafka/callbackOne/Suceess 效果同上
    (4)事务处理,访问GET http://localhost:8081/kafka/transaction/transaction,会报错,需要修改下面的配置
# 重试次数
spring.kafka.producer.retries=3
# 应答级别:多少个分区副本备份完成时向生产者发送ack确认(可选0、1、all/-1)
spring.kafka.producer.acks=-1
#增加配置
spring.kafka.producer.transaction-id-prefix=tx_

再访问,我们可以在控制台看到事务处理失败,消息并没有发生成功:
事件处理:SpringBoot+Kafka_第8张图片

你可能感兴趣的:(SpringBoot,kafka,spring,boot,java)