spring-kafka-2.8.2使用示例
前言
kafka是目前IT中常用组件,java+kafka、python+kafka、lua+kafka、go+kafka是常见组合。
kafka常用语业务层的异步数据处理。
也用于agent层服务器数据的采集。
大数据,ELK中kafka更是核心组件,flink+kafka进行海量数据的处理。
云计算心跳管理,IT七大对象自动化监管,lua+kafka也是常见场景。
kafka很牛,虽然和redis内存库不同,采用磁盘进行数据存储但是读写速度超强,因为他是顺序读写。
所以使用场景下,快速掌握kafka的使用及其重要,当然至于Kafka的saas化不需要太多考虑,只需要直接云上申请服务集群即可。
使用场景说明-springboot+kafka
在Spring Boot微服务集成Kafka客户端spring-kafka-2.8.2操作Kafka。
使用Spring封装的KafkaTemplate操作Kafka生产者Producer。
使用Spring封装的@KafkaListener操作Kafka的消费者Consumer。
JDK 1.8,
Spring Boot 2.7.11, -- springboot 3.x之后有了巨大变化,但是目前企业用的主流JDK还是8,JDK21虽然很新,但是企业追求的是稳定。
spring-kafka-2.8.2。
基础概念
Event:An event records the fact that "something happened" in the world or in your business. It is also called record or message in the documentation.
Broker:一个Kafka节点就是一个broker;多个Broker可以组成一个Kafka集群。
Topic:Kafka根据Topic对消息进行归类,发布到Kafka的每条消息都需要指定一个Topic。
Producer:消息生产者,向Broker发送消息的客户端。
Consumer:消息消费者,从Broker读取消息的客户端。
ConsumerGroup:每个Consumer属于一个特定的ConsumerGroup,一条消息可以被多个不同的ConsumerGroup消费;但是一个ConsumerGroup中只能有一个Consumer能够消费该消息。
Partition:一个topic可以分为多个partition,每个partition内部消息是有序的。
publish:发布,使用Producer向Kafka写入数据。
subscribe:订阅,使用Consumer从Kafka读取数据。
kafka使用demo
添加依赖
<!--kafka--> <dependency> <groupId>org.springframework.kafka</groupId> <artifactId>spring-kafka</artifactId> <version>2.8.2</version> </dependency>
增加kafka配置
这里使用的application.properties文件,当然目前yaml也非常流行,但是自动化部署中yaml不太好用,对缩进要求太严格
其实最完美的配置是json,但是目前看业界很少用,只有前端vue项目,或者底层go-agent类项目,采用json配置的较多。
特别说明:
网上基本千篇一律的demo都是自动装配,标准配置,但是在真实的IT生产中是不可能的。
application.properties文件也不会使用标准的K-V配置,自动装配。
就拿一个出海项目为例可能几十个profile配置文件,真实业务也往往使用多集群,多实例,账号密码还要加密认证,使用3~4级工作秘钥进行安全加固。
往往都需要手工装载,初始化bean,然后再使用。
spring.kafka.demo.bootstrap-servers=192.168.19.203:29001 spring.kafka.demo.username=demo spring.kafka.demo.password=demo#123 spring.kafka.demo.kafka.ssl.truststore.password=demo@123 spring.kafka.demo.consumer.group-id=test_group_id spring.kafka.demo.enable-auto-commit=true spring.kafka.demo.auto-offset-rest=latest spring.kafka.demo.auto-commit-interval=100 spring.kafka.demo.max-poll-records=100
装载bean
我们不使用:spring-boot-autoconfigure-2.6.3.jar,@ConfigurationProperties 自动去状态bean。
springboot提供了大量的data-template,有个大的json罗列了各种组件start的模板。
package com.wht.test.kafka; import lombok.extern.slf4j.Slf4j; import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.producer.ProducerConfig; import org.apache.kafka.common.config.SslConfigs; import org.apache.kafka.common.serialization.StringDeserializer; import org.apache.kafka.common.serialization.StringSerializer; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.system.ApplicationHome; 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.*; import org.springframework.kafka.listener.ConcurrentMessageListenerContainer; import java.io.File; import java.util.HashMap; import java.util.Map; /** * desc * * @Author 红尘过客 * @DateTime 2023-07-29 21:54:25 */ @Configuration @Slf4j public class KafkaConfig { @Value("$(spring.kafka.demo.bootstrap-servers)") private String kafkaStrapServers; @Value("$(spring.kafka.demo.username:null)") private String username; @Value("$(spring.kafka.demo.password:null)") private String password; @Value("$(spring.kafka.demo.kafka.ssl.truststore.password:null)") private String jskPassword; @Value("$(spring.kafka.demo.consumer.group-id)") private String testGroupId; @Value("$(spring.kafka.demo.enable-auto-commit)") private String enableAutoCommit; @Value("$(spring.kafka.demo.auto-offset-rest)") private String autoOffsetRest; @Value("$(spring.kafka.demo.auto-commit-interval)") private String autoCommitInterval; @Value("$(spring.kafka.demo.max-poll-records)") private String maxPollRecords; @Bean public KafkaTemplate<String, String> kafkaTemplate() { return new KafkaTemplate<>(getProducerFactory()); } @Bean public KafkaListenerContainerFactory<ConcurrentMessageListenerContainer<String, String>> kafkaListenerContainerFactory() { ConcurrentKafkaListenerContainerFactory<String, String> factory = new ConcurrentKafkaListenerContainerFactory<>(); factory.setConsumerFactory(getConsumerFactory()); factory.setConcurrency(3); factory.setBatchListener(true); factory.getContainerProperties().setPollTimeout(3000); return factory; } private ProducerFactory<String, String> getProducerFactory() { return new DefaultKafkaProducerFactory<String, String>(producerConfig()); } private ConsumerFactory<String, String> getConsumerFactory() { return new DefaultKafkaConsumerFactory<String, String>(consumerConfig()); } private Map<String, Object> producerConfig() { Map<String, Object> props = new HashMap<>(); props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaStrapServers); props.put(ProducerConfig.RETRIES_CONFIG, 0); props.put(ProducerConfig.ACKS_CONFIG, "1"); props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); setKafkaSecurity(props); return props; } private void setKafkaSecurity(Map<String, Object> props) { if (username != null) { props.put("security.protocol", "SASL.SSL"); props.put("sasl.mechanism", "PLAIN"); props.put("sasl.jaas.config", "org.apache.kafka.common.security.plain.PlainLoginModule " + "required username=\"" + username + "\" password=\"" + password + "\";"); props.put("ssl.truststore.password", jskPassword); props.put("ssl.endpoint.identification.algorithm", ""); props.put(SslConfigs.SSL_TRUSTSTORE_LOCATION_CONFIG, getConfigFilePath()); } } private Map<String, Object> consumerConfig() { Map<String, Object> props = new HashMap<>(); props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaStrapServers); props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, enableAutoCommit); props.put(ConsumerConfig.GROUP_ID_CONFIG, testGroupId); props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, autoCommitInterval); props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, autoOffsetRest); props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); setKafkaSecurity(props); return props; } private String getConfigFilePath() { ApplicationHome applicationHome = new ApplicationHome(this.getClass()); String rootPath = applicationHome.getSource().getParentFile().toString(); String configFilePath = rootPath + File.separator + "resources" + File.separator + "client.truststore.jks"; File configFile = new File(configFilePath); if (!configFile.exists()) { configFilePath = rootPath + File.separator + "config" + File.separator + "client.truststore.jks"; } log.info("jks file path = {}", configFilePath); return configFilePath; } }
推送消息示例
package com.wht.test.kafka; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.kafka.core.KafkaTemplate; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; /** * 发送消息demo * * @Author 红尘过客 * @DateTime 2023-07-29 22:39:58 */ @Component @Slf4j public class ProducerTest { @Autowired private KafkaTemplate<String, String> kafkaTemplate; private int count; @Scheduled(cron = "*/30 * * * * ?") public void run() { for (int i = 0; i < 100; i++) { kafkaTemplate.send("test_kafka_topic", "test_message_" + count++); log.info("推送消息成功。。。。。。。。。。。。。。", count); } } }
消费消息示例
package com.wht.test.kafka; import lombok.extern.slf4j.Slf4j; import org.springframework.kafka.annotation.KafkaListener; import org.springframework.stereotype.Component; import java.util.List; /** * kafka消费验证 * * @Author 红尘过客 * @DateTime 2023-07-29 22:43:04 */ @Component @Slf4j public class KafkaListenTest { @KafkaListener(topics = "", containerFactory = "test_kafka_topic", clientIdPrefix = "test_kafka_topic-", concurrency = "3") public void Listen(List<String> list) { for (int i = 0; i < list.size(); i++) { log.info("批量消费消息成功-{}", list.size()); log.info(list.get(i)); } } }
暂时记录
kafka的使用很简单,但是也会遇到各种问题。
例如2分钟推送5亿数据,消费过程中可能遇到OOM,cpu爆掉,参数配置不合理,消费组掉线。
JKS启动报错等等问题。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战