前言

Kafka 是一个消息队列产品,基于Topic partitions的设计,能达到非常高的消息发送处理性能。本文主是基于Spirng Boot封装了Apache 的Kafka-client,用于在Spring Boot 项目里快速集成kafka。


一、Kafka 是什么?

Apache Kafka是分布式发布-订阅消息系统。

它最初由LinkedIn公司开发,之后成为Apache项目的一部分。

Kafka是一种快速、可扩展的、设计内在就是分布式的,分区的和可复制的提交日志服务。

二、消息者

1.引入库

引入需要依赖的jar包,引入POM文件

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

2.配置文件

spring:
custom:
kafka:
username: admin
password: admin-secret
partitions: 1
enable-auto-commit: false
topics: CHANNEL-BodyBusiness-dataDev,CHANNEL-BodyBusiness-pushDev
groupId: consumer-group-lms-dev
batch-listener: false
bootstrap-servers:
- 192.168.1.95:9092

3.端启动类

启动类名 EnableAutoKafkaClient
package com.cdkjframework.kafka.consumer.annotation;

import com.cdkjframework.kafka.consumer.config.KafkaClientMarkerConfiguration;
import org.springframework.context.annotation.Import; import java.lang.annotation.*; /**
* @ProjectName: cdkj-framework
* @Package: com.cdkjframework.kafka.consumer.annotation
* @ClassName: EnableAutoKafkaClient
* @Description: Kafka客户端自动启动类
* @Author: xiaLin
* @Date: 2023/7/18 9:20
* @Version: 1.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({KafkaClientMarkerConfiguration.class})
public @interface EnableAutoKafkaClient {
}

4.spring.factories配置文件

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.cdkjframework.kafka.consumer.config.KafkaClientAutoConfiguration

5.配置类

5.1 Kafka客户端配置

package com.cdkjframework.kafka.consumer.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration; import java.util.List; /**
* @ProjectName: cdkj-framework
* @Package: com.cdkjframework.kafka.consumer.config
* @ClassName: KafkaClientConfig
* @Description: Kafka客户端配置
* @Author: xiaLin
* @Version: 1.0
*/
@Data
@Configuration
@ConfigurationProperties(prefix = "spring.custom.kafka")
public class KafkaClientConfig { /**
* 服务列表
*/
private List<String> bootstrapServers; /**
* 主题
*/
private List<String> topics; /**
* 账号
*/
private String username; /**
* 密码
*/
private String password; /**
* 延迟为1毫秒
*/
private Integer linger = 1; /**
* 批量大小
*/
private Integer batchSize = 16384; /**
* 重试次数,0为不启用重试机制
*/
private Integer retries = 0; /**
* 人锁
*/
private Integer maxBlock = 6000; /**
* acks
*/
private String acks = "1"; /**
* security.providers
*/
private String securityProviders; /**
* 启用自动提交
*/
private boolean enableAutoCommit = true; /**
* 会话超时
*/
private String sessionTimeout = "5000"; /**
* 会话超时
*/
private Integer maxPollInterval = 10000; /**
* 组ID
*/
private String groupId = "defaultGroup"; /**
* 最大投票记录
*/
private Integer maxPollRecords = 1; /**
* 并发性
*/
private Integer concurrency = 3; /**
* 拉取超时时间
*/
private Integer pollTimeout = 60000; /**
* 批量监听
*/
private boolean batchListener = false; /**
* 副本数量
*/
private String sort = "1"; /**
* 分区数
*/
private Integer partitions = 3; /**
* 消费者默认支持解压
*/
private String compressionType = "none"; /**
* offset偏移量规则设置
*/
private String autoOffsetReset = "earliest"; /**
* 自动提交的频率
*/
private Integer autoCommitInterval = 100; /**
* 生产者可以使用的总内存字节来缓冲等待发送到服务器的记录
*/
private Integer bufferMemory = 33554432; /**
* 消息的最大大小限制
*/
private Integer maxRequestSize = 1048576;
}

5.2 Kafka客户端自动配置

package com.cdkjframework.kafka.consumer.config;

import com.cdkjframework.kafka.consumer.ConsumerConfiguration;
import com.cdkjframework.kafka.consumer.service.ConsumerService;
import com.cdkjframework.kafka.consumer.listener.ConsumerListener;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy; /**
* @ProjectName: cdkj-framework
* @Package: com.cdkjframework.kafka.consumer.config
* @ClassName: KafkaClientAutoConfiguration
* @Description: Kafka客户端自动配置
* @Author: xiaLin
* @Date: 2023/7/18 9:21
* @Version: 1.0
*/
@Lazy(false)
@RequiredArgsConstructor
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(KafkaClientConfig.class)
@AutoConfigureAfter({WebClientAutoConfiguration.class})
@ImportAutoConfiguration(ConsumerConfiguration.class)
@ConditionalOnBean(KafkaClientMarkerConfiguration.Marker.class)
public class KafkaClientAutoConfiguration { /**
* 消费者服务接口
*/
private final ConsumerService consumerService; /**
* kafka topic 启动触发器
*
* @return 返回结果
*/
@Bean
@ConditionalOnMissingBean
public ConsumerListener kafkaConsumer() {
return new ConsumerListener(consumerService);
}
}

5.3 Kafka客户端标记配置

package com.cdkjframework.kafka.consumer.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.annotation.EnableKafka; /**
* @ProjectName: cdkj-framework
* @Package: com.cdkjframework.kafka.consumer.config
* @ClassName: KafkaClientMarkerConfiguration
* @Description: Kafka客户端标记配置
* @Author: xiaLin
* @Date: 2023/12/6 13:11
* @Version: 1.0
*/
@EnableKafka
@Configuration(proxyBeanMethods = false)
public class KafkaClientMarkerConfiguration { @Bean
public Marker kafkaMarker() {
return new Marker();
} public static class Marker { }
}

6.消费者配置

package com.cdkjframework.kafka.consumer;

import com.cdkjframework.kafka.consumer.config.KafkaClientConfig;
import com.cdkjframework.util.log.LogUtils;
import com.cdkjframework.util.tool.JsonUtils;
import com.cdkjframework.util.tool.StringUtils;
import lombok.RequiredArgsConstructor;
import org.apache.kafka.clients.CommonClientConfigs;
import org.apache.kafka.clients.consumer.Consumer;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.common.TopicPartition;
import org.apache.kafka.common.config.SaslConfigs;
import org.apache.kafka.common.security.auth.SecurityProtocol;
import org.apache.kafka.common.serialization.StringDeserializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
import org.springframework.kafka.config.KafkaListenerContainerFactory;
import org.springframework.kafka.core.ConsumerFactory;
import org.springframework.kafka.core.DefaultKafkaConsumerFactory;
import org.springframework.kafka.listener.BatchErrorHandler;
import org.springframework.kafka.listener.ConcurrentMessageListenerContainer;
import org.springframework.kafka.listener.ConsumerAwareErrorHandler; import java.util.HashMap;
import java.util.Iterator;
import java.util.Map; /**
* @ProjectName: cdkj-framework
* @Package: com.cdkjframework.kafka.consumer
* @ClassName: ProducerConfiguration
* @Description: 设置@Configuration、@EnableKafka两个注解,声明Config并且打开KafkaTemplate能力。
* @Author: xiaLin
* @Version: 1.0
*/
@Configuration
@RequiredArgsConstructor
public class ConsumerConfiguration { /**
* 日志
*/
private final LogUtils logUtils = LogUtils.getLogger(ConsumerConfiguration.class); /**
* JAAS配置
*/
private String JAAS_CONFIG = "org.apache.kafka.common.security.plain.PlainLoginModule required username=%s password=%s;"; /**
* 配置
*/
private final KafkaClientConfig kafkaClientConfig; /**
* 监听容器工厂
*
* @return 返回结果
*/
@Bean(name = "kafkaListenerContainerFactory")
public KafkaListenerContainerFactory<ConcurrentMessageListenerContainer<String, String>> kafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory<String, String>
factory = new ConcurrentKafkaListenerContainerFactory<>();
// 设置消费者工厂
factory.setConsumerFactory(consumerFactory());
// 消费者组中线程数量
factory.setConcurrency(kafkaClientConfig.getConcurrency());
// 拉取超时时间
factory.getContainerProperties().setPollTimeout(kafkaClientConfig.getPollTimeout());
// 当使用批量监听器时需要设置为true
factory.setBatchListener(kafkaClientConfig.isBatchListener());
// 将单条消息异常处理器添加到参数中
factory.setErrorHandler(new ConsumerAwareErrorHandler() {
@Override
public void handle(Exception thrownException, ConsumerRecord<?, ?> data, Consumer<?, ?> consumer) {
logUtils.error("// 将单条消息异常:" + thrownException.getMessage());
logUtils.error("// 将单条消息:" + data.toString() + "," + consumer.toString());
Iterator<TopicPartition> iterator = consumer.assignment().iterator();
if (iterator.hasNext()) {
// 提交重新消费
consumer.seek(iterator.next(), data.offset());
}
}
});
if (kafkaClientConfig.isBatchListener()) {
// 将批量消息异常处理器添加到参数中
factory.setBatchErrorHandler(new BatchErrorHandler() {
@Override
public void handle(Exception thrownException, ConsumerRecords<?, ?> data) {
logUtils.error("// 将批量消息异常:" + thrownException.getMessage());
logUtils.error(thrownException);
logUtils.error(JsonUtils.objectToJsonString(data));
}
});
}
// factory.setContainerCustomizer(); return factory;
} /**
* 消费者工厂
*
* @return 返回消费工厂
*/
@Bean
public ConsumerFactory<String, String> consumerFactory() {
return new DefaultKafkaConsumerFactory<>(consumerConfig());
} /**
* 消费者配置
*
* @return 返回结果
*/
@Bean
public Map<String, Object> consumerConfig() {
Map<String, Object> propsMap = new HashMap<>();
// Kafka地址
propsMap.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaClientConfig.getBootstrapServers());
//配置默认分组,这里没有配置+在监听的地方没有设置groupId,多个服务会出现收到相同消息情况
propsMap.put(ConsumerConfig.GROUP_ID_CONFIG, kafkaClientConfig.getGroupId());
// 是否自动提交offset偏移量(默认true)
propsMap.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, kafkaClientConfig.isEnableAutoCommit());
// 心跳机制
propsMap.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, kafkaClientConfig.getMaxPollInterval());
// 每次读取最大记录
propsMap.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, kafkaClientConfig.getMaxPollRecords());
if (kafkaClientConfig.isEnableAutoCommit()) {
// 自动提交的频率(ms)
propsMap.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, kafkaClientConfig.getAutoCommitInterval());
}
// 键的反序列化方式
propsMap.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
// 值的反序列化方式
propsMap.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
// offset偏移量规则设置:
// (1)、earliest:当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,从头开始消费
// (2)、latest:当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,消费新产生的该分区下的数据
// (3)、none:topic各分区都存在已提交的offset时,从offset后开始消费;只要有一个分区不存在已提交的offset,则抛出异常
propsMap.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, kafkaClientConfig.getAutoOffsetReset()); // 安全认证 账号密码
if (StringUtils.isNotNullAndEmpty(kafkaClientConfig.getUsername()) &&
StringUtils.isNotNullAndEmpty(kafkaClientConfig.getPassword())) {
propsMap.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, SecurityProtocol.SASL_PLAINTEXT.name);
String SASL_MECHANISM = "PLAIN";
propsMap.put(SaslConfigs.SASL_MECHANISM, SASL_MECHANISM);
propsMap.put(SaslConfigs.SASL_JAAS_CONFIG, String.format(JAAS_CONFIG, kafkaClientConfig.getUsername(), kafkaClientConfig.getPassword()));
} return propsMap;
}
}

7.消费者服务

package com.cdkjframework.kafka.consumer.service;

import com.cdkjframework.exceptions.GlobalException;

/**
* @ProjectName: cdkj-framework
* @Package: com.cdkjframework.kafka.consumer
* @ClassName: com.cdkjframework.kafka.consumer.service.ConsumerService
* @Description: 消费者服务
* @Author: xiaLin
* @Version: 1.0
*/
public interface ConsumerService { /**
* 消息内容
*
* @param topics 主题
* @param message 内容
* @throws GlobalException 异常信息
*/
void onMessage(String topics, String message) throws GlobalException;
}

8.消费者监听器

package com.cdkjframework.kafka.consumer.listener;

import com.cdkjframework.kafka.consumer.service.ConsumerService;
import com.cdkjframework.util.log.LogUtils;
import org.apache.kafka.clients.consumer.Consumer;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.springframework.kafka.annotation.KafkaListener; /**
* @ProjectName: cdkj-kafka-client
* @Package: com.cdkjframework.kafka.consumer
* @ClassName: com.cdkjframework.kafka.consumer.listener.ConsumerListener
* @Description: 消费者监听器
* @Author: xiaLin
* @Version: 1.0
*/
public class ConsumerListener { /**
* 日志
*/
private final LogUtils logUtils = LogUtils.getLogger(ConsumerListener.class); /**
* 消费者服务接口
*/
private final ConsumerService consumerService; /**
* 构造函数
*
* @param consumerService 消费者服务接口
*/
public ConsumerListener(ConsumerService consumerService) {
this.consumerService = consumerService;
} /**
* 单条监听MQ消息
*
* @param data 消息内容
* @param consumer 消息者
*/
@KafkaListener(topics = "#{'${spring.custom.kafka.topics}'.split(',')}", groupId = "${spring.custom.kafka.groupId}")
public void listener(ConsumerRecord<String, String> data, Consumer consumer) {
try {
consumerService.onMessage(data.topic(), data.value());
consumer.commitAsync();
} catch (Exception e) {
logUtils.error(e);
// 抛出异常,以重试消费
throw new RuntimeException(e.getMessage());
}
}
}


总结

例如:以上就是今天要讲的内容,本文仅仅简单介绍了 Spring Boot 集成消息消费者的封装。

相对应的开源项目欢迎访问:维基框架

第三章 Spring Boot 整合 Kafka消息队列 消息者的更多相关文章

  1. Spring Boot 整合 Kafka

    Kafka 环境搭建 kafka 安装.配置.启动.测试说明: 1. 安装:直接官网下载安装包,解压到指定位置即可(kafka 依赖的 Zookeeper 在文件中已包含) 下载地址:https:// ...

  2. spring boot整合kafka

    最近项目需求用到了kafka信息中间件,在此做一次简单的记录,方便以后其它项目用到. 引入依赖 <dependency> <groupId>org.springframewor ...

  3. Spring Boot2 系列教程(三十)Spring Boot 整合 Ehcache

    用惯了 Redis ,很多人已经忘记了还有另一个缓存方案 Ehcache ,是的,在 Redis 一统江湖的时代,Ehcache 渐渐有点没落了,不过,我们还是有必要了解下 Ehcache ,在有的场 ...

  4. spring boot 2.x 系列 —— spring boot 整合 kafka

    文章目录 一.kafka的相关概念: 1.主题和分区 2.分区复制 3. 生产者 4. 消费者 5.broker和集群 二.项目说明 1.1 项目结构说明 1.2 主要依赖 二. 整合 kafka 2 ...

  5. kafka学习(五)Spring Boot 整合 Kafka

    文章更新时间:2020/06/08 一.创建Spring boot 工程 创建过程不再描述,创建后的工程结构如下: POM文件中要加入几个依赖: <?xml version="1.0& ...

  6. Netty学习第四章 spring boot整合netty的使用

    现在大多数项目都是基于spring boot进行开发,所以我们以spring boot作为开发框架来使用netty.使用spring boot的一个好处就是能给将netty的业务拆分出来,并通过spr ...

  7. 上手spring boot项目(三)之spring boot整合mybatis进行增删改查的三种方式。

    1.引入依赖. <!--springboot的web起步依赖--><dependency> <groupId>org.springframework.boot< ...

  8. 上手spring boot项目(三)之spring boot整合mybatis进行增删改查

    使用mybatis框架进行增删改查大致有两种基础方式,一种扩展方式.两种基础方式分别是使用xml映射文件和使用方法注解.扩展方式是使用mybatis-plus的方式,其用法类似于spring-data ...

  9. spring boot 整合kafka 报错 Exception thrown when sending a message with key='null' and payload=JSON to topic proccess_trading_end: TimeoutException: Failed to update metadata after 60000 ms.

    org.springframework.kafka.support.LoggingProducerListener- Exception thrown when sending a message w ...

  10. Spring Boot 整合JDBC 实现后端项目开发

    一.前言 二.新建Spring Boot 项目 三.Spring Boot 整合JDBC 与MySQL 交互 3.1 新建数据表skr_user 3.2 Jdbcproject 项目结构如下 3.3 ...

随机推荐

  1. Luogu P9180 [COCI2022-2023#5] Slastičarnica 题解 [ 蓝 ] [ 区间 dp ] [ dp 状态优化 ] [ 前缀和优化 ]

    Slastičarnica:非常好的区间 dp 题. 暴力 不难设计出暴力状态:\(dp_{q,i,j}\) 表示进行到第 \(q\) 次操作,剩下区间 \([i,j]\) 是否可行. 直到全部状态都 ...

  2. 本地AI搭建

    搭建本地博客AI 目录 搭建本地博客AI 环境 下载ollama 选择模型 选择embedding模型 查看性能测试 选择合适的嵌入模型(Embedder) 估算内存 选择模型 量化类型介绍 Q5_0 ...

  3. 快速集成和使用 solon-flow 规则与流引擎(用 yaml 编写业务规则)

    本文参考自:https://www.cnblogs.com/studyjobs/p/18125096 规则引擎技术的主要思想是将应用程序中的业务规则分离出来,业务规则不再以程序代码的形式驻留在系统中, ...

  4. 推荐几款开源且免费的 .NET MAUI 组件库

    前言 今天大姚给大家推荐 3 款开源且免费的 .NET MAUI 组件库. .NET MAUI介绍 .NET 多平台应用 UI (.NET MAUI) 是一个跨平台框架,用于使用 C# 和 XAML ...

  5. camunda工作流实战项目(表单设计器+流程编辑器,零代码创建流程)

    该项目的plus版本已制作完成,文章链接 [plus版]camunda工作流实战项目 一.整体情况介绍 基于ruoyi平台和camunda工作流开发而成,结合bpmn.js流程编辑器和vform表单设 ...

  6. Set Cover问题的贪心近似算法分析

    问题描述 全集 \(U = \{ e_1, e_2, ... , e_n \}\) 被划分为一系列的子集 \(S = \{ S_1, S_2, ... , S_k \}\).且存在一个cost函数\( ...

  7. SpringSecurity学习笔记-前后端分离

    1. 简介 Spring Security是Spring家族中的一个安全管理框架.相比于另外一个安全框架Shiro,它提供了更丰富的功能,社区资源也比Shiro丰富. 一般来说中大型的项目都是使用Sp ...

  8. kali安装pdtm工具

    kali安装pdtm工具 前言 今天想安装一下pdtm工具集的,但过程中一直出现各种错误,找了几篇文章之后并没有找到解决方法,后解决之后写了这样一篇文章希望可以解决大家在安装过程中碰到的部分问题 介绍 ...

  9. 【QT】使用Qxlsx读取Excel单元格中函数表达式的结果值

    [QT]使用Qxlsx读取Excel单元格中函数表达式的结果值 零.起因 是这样的,目前朋友托我写一款模板生成软件,任务是先把他写的程序文件复制一份出来,然后再根据Excel中对应位置的单元格的值,修 ...

  10. Go初入武林之乘法表

    为统一管理源码, 请到gitee中查看. GoTimesTable