SpringBoot整合Kafka

58 阅读6分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 11 天,点击查看详情

简介

Kafka当前作为Apache开源项目,最早由Linkedin公司开发,使用Scala和jAVA进行编写,它是一个分布式的,支持分区,多副本基于Zookeeper协调的分布式消息系统,可以应用在基于Hadoop的批处理系统,低延迟的实时系统,日志/消息服务等场景;

特性
高吞吐量,低延迟:即时普通硬件KafKa也可以每秒处理几十万数百万消息;
可扩展性:支持集群热扩展,无需停机即可扩展机器,支持Hadoop并行加载数据
持久可靠性:消息被持久化保存到本地磁盘,支持数据备份防止数据丢失;
容错性:是分布式、分区、复制和容错的,当集群中副本数量为n,则允许n-1个节点失败,允许集群节点失败;
高并发:单节点支持上千个客户端,并保证零停机和零数据丢失;
基础概念

Topic:主题,接受Kafka消息,不同的Topic分开存储在不同的物理区域,类似于分库分表后的数据库逻辑表;

Partition:分区,每个主题可以包含多个分区,一个分区代表一个提交日志,消息最终追加到分区,然后以先入先出的顺序进行读取,但是无法保证整体消息消费顺序,也是通过分区来保证伸缩性与数据冗余;可以设置分区为1来保证消息顺序;

Replicas:副本,分区可以有多个副本,副本保存在节点(broker)上,多个副本只有一个leader,该副本可以对外提供服务,每个节点保存多个不同主题的分区的副本信息;AR为所有副本统称,ISR为与主副本保持同步的副本,从主副本中拉取消息,可以在主副本出现问题后选举产生新的主副本,OSR具有滞后性的副本,不参与主副本选举;

Producer:消息生产者,向Kafka特定的主题中发送消息,默认情况下通过轮询来将消息均衡发布到主题的所有分区中;也可以通过自定义的分区器来根据规则分发消息;

Consumer:消息消费者,负责主动拉取Kafka中的消息,每个消费者属于配置给定的消费者组,当消费者失效时也会被其他同组消费者进行消费;消息消费的状态保存在Consumer中;

ConsumerGroup:消费者组,多个consumer可以组成消费者组,每条消息只能被组中一个消费者消费,可以被不同组的多个消费者消费,当创建消费者组时需要指定对应的消费者组id(ConsumerGroupId)

Message:在生产者与消费者之间进行传递的对象实体,存储传递的信息;

简单整合Kafka
//引入依赖
   <dependency>
        <groupId>org.springframework.kafka</groupId>
        <artifactId>spring-kafka</artifactId>
    </dependency>
    
//设置Yml文件

kafka-producer:
  #(必填)#用于建立与kafka集群的连接,这个list仅仅影响用于初始化的hosts,来发现全部的servers。
  bootstrapServers: 192.168.18.224:9092
  #(必填)#topic名称
  producerTopic: control_topic
  #(默认,可不填)#强制刷新metadata的周期,即使leader没有变化
  metadataMaxAgeMs: 300000
  #***用来设置一个batch的最大字节数byte。当设置为0时,表示完全禁用batch的功能。如果batch.size设置过大,
  #!***那么可能造成内存浪费,因为每个发送到不同partition的record都需要预先分配一块batch.size大小的内存。
  #(默认,可不填)
  batchSize: 1048576
  #   acks=0时,producer不会等待确认,直接添加到socket等待发送;
  #   acks=1时,等待leader写到local log就行;
  #   acks=all或acks=-1时,等待isr中所有副本确认
  #(默认,可不填)
  acks: 1
  #***延迟发送消息记录的时间,producer在发送消息记录record的时候,会将发送到同一个partition的records压缩在batch中。
  #***但通常这只发生在records到达速度快于records发送速度的情况下,很容易理解:如果发送速度大于record到达速度,则每来一个record都会被立即发送出去,
  #***根本不存在将多个records压缩为一个的可能。
  #(默认,可不填)
  lingerMs: 0
  #(默认,可不填)#发生错误时,重传次数。当开启重传时,需要将`max.in.flight.requests.per.connection`设置为1,否则可能导致失序
  retries: 0
  #***Producer可以用来缓存数据的内存大小。该值实际为RecordAccumulator类中的BufferPool,即Producer所管理的最大内存。
  #***如果数据产生速度大于向broker发送的速度,producer会阻塞max.block.ms,超时则抛出异常
  #(默认,可不填)
  bufferMemory: 33554432
  #(默认,可不填)#当向server发出请求时,这个字符串会发送给server,目的是能够追踪请求源
  clientId: ""
  #(默认,可不填)#TCP发送缓冲区(SO_SNDBUF)的大小,若send.buffer.bytes设为-1,则使用操作系统的默认值。
  sendBufferBytes: -1
  #(默认,可不填)#TCP接收缓冲区(SO_RCVBUF)大小,当receive.buffer.bytes设置为-1,则使用操作系统默认的大小。
  receiveBufferBytes: -1
  #(默认,可不填)#一个请求request中最大字节数,用于限制producer发送的单个请求request中,record batches的最大数量,以避免单个请求数据过于巨大。
  maxRequestSize: 1048576
  #(默认,可不填)#重连间隔时间,避免producer客户端过于紧密循环地重连kafka服务broker。该值针对的是所有client到broker的连接。
  reconnectBackoffMs: 50
  #(默认,可不填)#当一个producer到指定的partition的请求request失败时,在重连之前,需要等待的毫秒数。这是为了避免在某些失败的场景下,过于密集地重复发送请求。
  retryBackoffMs: 100
  #(默认,可不填)#Producer用于压缩数据的压缩类型,取值:none, gzip, snappy, or lz4
  compressionType: gzip
  #(默认,可不填)#关闭达到该时间的空闲连接
  connectionsMaxIdleMs: 540000
  #(默认,可不填)#client等待请求响应的最大时间,如果在这个时间内没有收到响应,客户端将重发请求,超过重试次数发送失败
  requestTimeoutMs: 30000
  #(默认,可不填)#控制block的时长,当buffer空间不够或者metadata丢失时产生block
  maxBlockMs: 60000

  #启动消费者yml文件加入kafka-consumer.enable = true.
  #   消费者属性
kafka-consumer:
  enable: true
  #(必填)#kafka指定IP端口
  kafkaServers: 192.168.18.224:9092
  #(必填)#自定义消费者Topic
  consumerTopic: topic_robot_cruise
  #(必填)#自定义消费者组
  consumerGroupId: mx_topic_20221018001
  #(默认,可不填)#是否自动提交office
  consumerEnableAutoCommit: false
  #(默认,可不填)#超时时间
  consumerSessionTimeout: 15000
  #(默认,可不填)#消费提交个数
  consumerAutoCommitInterval: 2000
  #(默认,可不填)#消费方式:earliest最前消费,latest最新消费
  consumerAutoCommitOffsetReset: latest
  #(默认,可不填)#自定义消费者个数
  consumerConcurrency: 3
消息发送与接收
/**
 * Kafka工具类
 * @author wangw
 */
@Component
@ConditionalOnClass(name = "org.springframework.kafka.core.KafkaTemplate")
public class KafkaUtil {
    private static KafkaTemplate<String, String> kafkaTemplate;
    
    @Autowired(required = true)
    public void setKafkaTemplate(KafkaTemplate<String, String> kafkaTemplate) {
        KafkaUtil.kafkaTemplate = kafkaTemplate;
    }
    //发送到指定分区
    public static void send(String topic, String data) {
        kafkaTemplate.send(topic, data);
    }
     //指定key作为hash散列,相同key可以发送到同一个分区
    public static void send(String topic, String key, String data) {
        kafkaTemplate.send(topic, key, data);
    }

}
---
//调用发送测试生产者发送
    KafkaUtil.send(KafkaTopicConst.ALARM_LAMP_CONTROL, jsonString);
---
//测试消费者消费
@Slf4j
@Service
public class BytLocationConsumer implements KafkaMessageInterface {

    @Override
    @KafkaListener(topics={KafkaTopicConst.ROBOT_CRUISE} ,containerFactory = "kafkaListenerContainerFactory")
    public void handleMessage(List<ConsumerRecord<?, ?>> records, Acknowledgment ack){
 
        for (ConsumerRecord consumerRecord : records) {
            String val = consumerRecord.value().toString();
            System.out.println(val);
             }
            ack.acknowledge();
    }
}