Kafka学习笔记

本文仅仅为自用kafka学习笔记,如果对你没有帮助,请不要喷谢谢。如果有帮助,那本人感到万分的荣幸

Kafka的两种生产消费模式

点对点:

在这里插入图片描述
发布订阅模式:
在这里插入图片描述

Kafka基础架构

在这里插入图片描述
解释:
Producer:生产者
Comsumer:消费者
Comsumer Group(CG):消费者组。每一个消费者组中每一个消费者对应着不同的Partition。也就是下面说的在Topic中的分组。
Broker:服务器,可能有多台Kafka服务器,组成一个集群。
Topic:这个是一个概念,这个概念独立于服务器Broker。但是其存储是依赖具体的服务器Broker的。一个Topic分布在多个服务器上。
Partition:分组,一个Topic对应多个Partition。
Replication:副本,一个Partition有多个副本。
Leader:一个分组有一个Leader。
Follower:一个分组有多个Follower,对应一个Leader。

Kafka 的安装

安装地址
Kafka 官网

安装流程
将下载好的安装包上传到 Linux 服务器。(我这里使用的是 kafka_2.11-0.11.0.0.tgz)
解压安装包到指定目录。

tar -zxvf kafka_2.11-0.11.0.0.tgz -C /opt/module/

修改解压后的文件名称。

mv kafka_2.11-0.11.0.0/ kafka

在 /opt/module/kafka 目录下创建 logs 文件夹。

mkdir logs

修改 config 目录下的配置文件 server.properties。
输入以下内容:

#broker 的全局唯一编号,不能重复
broker.id=0
#删除 topic 功能使能
delete.topic.enable=true
#处理网络请求的线程数量
num.network.threads=3
#用来处理磁盘 IO 的现成数量
num.io.threads=8
#发送套接字的缓冲区大小
socket.send.buffer.bytes=102400
#接收套接字的缓冲区大小
socket.receive.buffer.bytes=102400
#请求套接字的缓冲区大小
socket.request.max.bytes=104857600
#kafka 运行日志存放的路径
log.dirs=/opt/module/kafka/data
#topic 在当前 broker 上的分区个数
num.partitions=1
#用来恢复和清理 data 下数据的线程数量
num.recovery.threads.per.data.dir=1
#segment 文件保留的最长时间,超时将被删除
log.retention.hours=168
#配置连接 Zookeeper 集群地址
zookeeper.connect=master:2181,slave1:2181,slave2:2181

将 kafka 目录分发到另外两台机器上。

scp kafka/ master:/opt/module/
scp kafka/ slave2:/opt/module/

在另外两台机器上修改配置文件 /opt/module/kafka/config/server.properties 中的 broker.id=1、broker.id=2(broker.id 不得重复)

配置环境变量。

vim /etc/profile

添加以下内容:

#KAFKA_HOME
export KAFKA_HOME=/opt/module/kafka
export PATH=$PATH:$KAFKA_HOME/bin

让配置文件生效:

 source /etc/profile

在另外两台机器做以上操作。

启动集群。

依次在 master、slave1、slave2 节点上启动 Kafka。

kafka-server-start.sh -daemon /opt/module/kafka/config/server.properties

关闭集群。

依次在 master、slave1、slave2 节点上关闭 Kafka。

kafka-server-stop.sh stop

在 /opt/module/kafka/bin 目录下编写群起群关脚本 kk.sh,方便以后使用。

vim kk.sh
#!/bin/bash
case $1 in
"start"){
  for i in master slave1 slave2
    do
      echo "****************** $i *********************"
      ssh $i "source /etc/profile && /opt/module/kafka/bin/kafka-server-start.sh -daemon /opt/module/kafka/config/server.properties"
    done
};;

"stop"){
  for i in master slave1 slave2
    do
      echo "****************** $i *********************"
      ssh $i "/opt/module/kafka/bin/kafka-server-stop.sh"
    done
};;

esac

chmod 777 kk.sh

演示截图:
在这里插入图片描述

说明: Kafka 集群关闭时会需要一些时间。

Kafka 命令行操作
创建 topic。

kafka-topics.sh --zookeeper slave1:2181 --create --replication-factor 3 --partitions 2 --topic demo

查看当前服务器中所有的 topic。

kafka-topics.sh --zookeeper slave1:2181 --list

查看某个 topic 的详情。

kafka-topics.sh --zookeeper slave1:2181 --describe --topic demo

删除 topic。

kafka-topics.sh --zookeeper slave1:2181 --delete --topic first

发送消息。

kafka-console-producer.sh --broker-list slave1:9092 --topic demo

消费消息。
(1)方法一

 kafka-console-consumer.sh --zookeeper slave1:2181 --topic demo

注意: 该方法已经过时。

(2)方法二

kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic demo

Kafka工作流程

原理
Kafka 中消息是以 topic 进行分类的,生产者生产消息,消费者消费消息,都是面向 topic 的。topic 是逻辑上的概念。而 partition 是物理上的概念,每个 partition 对应一个 log 文件,该 log 文件中存储的就是 producer 生产的数据。producer 生产的数据会不断追加到该 log 文件末端,且每条数据都有自己的 offset(偏移量)。消费者组中每个消费者,都会实时记录自己消费到了哪个 offset,以便出错恢复时,从上次的位置继续消费。
在这里插入图片描述

kafka文件存储机制
在这里插入图片描述
由于生产者生产的消息不断追加到 log 文件末尾,为防止 log 文件过大导致数据定位效率低下,Kafka 采取了分片和索引机制,将每个 partition 分为多个 segment。每个 segment 对应两个文件,“.index” 文件和 “.log 文件”。这些文件位于一个文件夹下,该文件夹命名规则为:topic 名称 + 分区序号。例如,demo 这个 topic 有两个分区,则其对应的文件夹为 demo-0,demo-1。

示意:
在这里插入图片描述
在这里插入图片描述
index 和 log 文件以当前 segment 的第一条消息 offset 命名。
在这里插入图片描述
“.index” 文件存储大量的索引信息,“.log” 文件存储大量的数据,索引文件中的元数据指向对应数据文件中 message 的物理偏移地址。

Kafka 生产者

分区策略
1.指定具体partition
2.没有指定partition,但是有key。我们可以利用key的hash值 / partition数对总数进行取余。
3.既没有key,又没有指定partition的话,我们利用第一次调用时随机生成一个整数(后面每次调用在这个整数上自增),将这个值与 topic 可用的 partition 总数取余得到 partition 值,也就是常说的 round-robin (轮询)算法。

为保证 producer 发送的数据,能可靠的发送到指定的 topic,topic 中的每个 partition 收到 producer 发送的数据后,都需要向 producer 发送 ack (acknowledgement 确认收到),如果 producer 收到 ack,就会进行下一轮的发送,否则重新发送数据。

在这里插入图片描述
关于同步策略:Kafka 采用的是第二种方案。因为第一种方案会造成大量数据的冗余,虽然第二种方案的网络延迟会比较高,但网络延迟对 Kafka 的影响较小。

为了解决这个问题,leader 维护了一个动态的 in-sync replica(ISA),意味着和 leader 保持同步的 follower 集合。当 ISA 中的 follower 完成数据同步之后,leader 就会给 follower 发送 ack。如果 follower 长时间未向 leader 同步数据,则该 follower 将被踢出 ISR,该时间阈值由 replica.lag.time.max.ms 参数设定。leader 发生故障后,就会从 ISR 中选取新的 leader。

关于ack的参数配置
在这里插入图片描述
故障处理
在这里插入图片描述
LEO: 指的是每个副本最大的 offset。
HW: 指的是消费者能见到的最大的 offset,ISR 队列中最小的 LEO。

follower 发生故障之后会被临时踢出 ISR,待该 follower 恢复之后,follower 会读取本地磁盘记录的上次 HW,并将 log 文件高于 HW 的部分裁掉,从 HW 开始向 leader 进行同步。等待 follower 的 LEO 大于等于该 partition 的 HW ,即 follower 追上 leader 之后,就可以重新加入 ISR了。

leader 发生故障之后,会从 ISR 中选出一个新的 leader,之后为保证多个副本间的数据一致性,其余的 follower 会先将各自的 log 文件高出 HW 的部分裁掉,然后从新的 leader 同步数据。

在 0.11 版本前的 kafka ,只能保证数据不丢失,再在下游消费者对数据进行全局去重。0.11 版本的 kafka 引入了 幂等性。幂等性是指 producer 不论向 server 发送多少次重复数据,server 端只会持久化一条。At Least Once + 幂等性 = Exactly Once。

要启动幂等性,只需要将 producer 中的参数 enable.idompotence 设置为 true 即可。kafka 的幂等性实现就是将原来下游需要做的去重放在了数据上游。开启幂等性的 producer 在初始化的时候会被分配一个 PID。发往同一个 partition 的消息会附带 Sequence Number。而 broker 端会对 <PID, Partition, SeqNumber> 做缓存,当具有相同主键提交时, broker 只会持久化一条。但是 PID 重启就会发生变化,同时不同的 partition 也具有不同的主键,所以幂等性无法保证跨分区会话的 Exactly Once。

Kafka消费者

消费方式
consumer 采用 pull(拉)模式从 broker 中读取数据。

push(推)模式很难适应消费速率不同的消费者,因为消息发送速率是由 broker 决定的。它的目标是尽可能以最快速度传递消息,但是这样很容易造成 consumer 来不及处理消息。典型的表现是拒绝服务以及网络拥塞。而 pull 模式则可以根据 consumer 的消费能力以适当的速率消费消息。

pull 模式不足之处是, 如果 kafka 没有数据,消费者可能陷入循环中,一直返回空数据。针对这一点,kafka 的消费者在消费数据时会传入一个时长参数 timeout,如果当前没有数据提供消费,consumer 会等待一段时间之后再返回,这段时长即 timeout。

分配策略
round robin
在这里插入图片描述
range
在这里插入图片描述
offset的维护
由于断电后可能会宕机,因此我们系统需要维护offset。以便于记录自己断电之前消费到哪个offset。

kafka 0.9 版本以前,consumer 默认将 offset 保存在 zookeeper 中,从 0.9 版本开始,consumer 默认将 offset 保存在 Kafka 一个内置 topic 中,该 topic 为 _consumer_offsets 。

步骤:
1.修改配置文件:

exclude.internal.topics=false

2.启动生产者与消费者

kafka-console-producer.sh --broker-list salve1:9092 --topic first
kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic first

读取offset

kafka-console-consumer.sh --topic __consumer_offsets --zookeeper slave1:2181 \
--formatter "kafka.coordinator.group.GroupMetadataManager\$OffsetsMessageFormatter" \
--consumer.config /opt/module/kafka/config/consumer.properties \
--from-beginning

注意:同一时刻只有一个消费者接收到消息。

Kafka事物

Kafka 从 0.11 版本开始引入了事务支持。事务可以保证 Kafka 在 Exactly Once 语义 基础上,生产和消费跨分区和会话,要么全部成功,要么全部失败。

为了实现跨分区跨会话的事务,需要引入一个全局唯一的 TransactionID,并将 Producer 获得的 PID 和 TransactionID 绑定。这样当 Producer 重启后就可以通过正在进行的 TransactionID 获取原来的 PID。

为了管理 Transaction,Kafka 引入了一个新的组件 Transaction Coordinator。Producer 就是通过和 Transaction Coordinator 交互获得 TransactionID 对应的任务状态。Transaction Coordinator 还负责将事务写入 Kafka 的一个内部 topic,这样即使整个服务重启,由于事务状态得到保存,进行中的事务状态可以得到恢复,从而继续进行。

对于 Consumer 而言,事务的保证就会相对较弱,尤其是无法保证 Commit 的信息被精确消费。这是由于 Consumer 可以通过 offset 访问任何信息,而不同的 Segment File 生命周期不同,同一事务的消息可能出现重启后被删除的情况。

Kafka API

假设机器上已经部署好了kafka和zookeeper
不会的参考博客配置好zookeeper和kafka。

maven:

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

创建2个topic测试:


[root@iZ2zegzlkedbo3e64vkbefZ ~]#  cd /usr/local/kafka-cluster/kafka1/bin/
[root@iZ2zegzlkedbo3e64vkbefZ bin]# ./kafka-topics.sh --create --zookeeper 172.17.80.219:2181 --replication-factor 2 --partitions 2 --topic topic1
Created topic topic1.
[root@iZ2zegzlkedbo3e64vkbefZ bin]# ./kafka-topics.sh --create --zookeeper 172.17.80.219:2181 --replication-factor 2 --partitions 2 --topic topic2
Created topic topic2.

或者代码创建:


@Configuration
public class KafkaInitialConfiguration {
    // 创建一个名为testtopic的Topic并设置分区数为8,分区副本数为2
    @Bean
    public NewTopic initialTopic() {
        return new NewTopic("testtopic",8, (short) 2 );
    }// 如果要修改分区数,只需修改配置值重启项目即可
    // 修改分区数并不会导致数据的丢失,但是分区数只能增大不能减小
    @Bean
    public NewTopic updateTopic() {
        return new NewTopic("testtopic",10, (short) 2 );
    }
}

配置properties

###########【Kafka集群】###########
spring.kafka.bootstrap-servers=112.126.74.249:9092,112.126.74.249:9093
###########【初始化生产者配置】###########
# 重试次数
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

简单生产和消费:

@RestController
public class KafkaProducer {
    @Autowired
    private KafkaTemplate<String, Object> kafkaTemplate;// 发送消息
    @GetMapping("/kafka/normal/{message}")
    public void sendMessage1(@PathVariable("message") String normalMessage) {
        kafkaTemplate.send("topic1", normalMessage);
    }
}
@Component
public class KafkaConsumer {
    // 消费监听
    @KafkaListener(topics = {"topic1"})
    public void onMessage1(ConsumerRecord<?, ?> record){
        // 消费的哪个topic、partition的消息,打印出消息内容
        System.out.println("简单消费:"+record.topic()+"-"+record.partition()+"-"+record.value());
    }
}

上面示例创建了一个生产者,发送消息到topic1,消费者监听topic1消费消息。监听器用@KafkaListener注解,topics表示监听的topic,支持同时监听多个,用英文逗号分隔。启动项目,postman调接口触发生产者发送消息。

生产者
1、带回调的生产者

//第一种写法
@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());
    });
}

//第二种写法
@GetMapping("/kafka/callbackTwo/{message}")
public void sendMessage3(@PathVariable("message") String callbackMessage) {
    kafkaTemplate.send("topic1", callbackMessage).addCallback(new ListenableFutureCallback<SendResult<String, Object>>() {
        @Override
        public void onFailure(Throwable ex) {
            System.out.println("发送消息失败:"+ex.getMessage());
        }
 
        @Override
        public void onSuccess(SendResult<String, Object> result) {
            System.out.println("发送消息成功:" + result.getRecordMetadata().topic() + "-"
                    + result.getRecordMetadata().partition() + "-" + result.getRecordMetadata().offset());
        }
    });
}

2.自定义分区
我们来自定义一个分区策略,将消息发送到我们指定的partition,首先新建一个分区器类实现Partitioner接口,重写方法,其中partition方法的返回值就表示将消息发送到几号分区

public class CustomizePartitioner implements Partitioner {
    @Override
    public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {
        // 自定义分区规则(这里假设全部发到0号分区)
        // ......
        return 0;
    }@Override
    public void close() {}@Override
    public void configure(Map<String, ?> configs) {}
}

properties中也要配置


# 自定义分区器
spring.kafka.producer.properties.partitioner.class=com.felix.kafka.producer.CustomizePartitioner

3.事务提交
使用KafkaTemplate 的 executeInTransaction 方法来声明事务。

@GetMapping("/kafka/transaction")
public void sendMessage7(){
    // 声明事务:后面报错消息不会发出去
    kafkaTemplate.executeInTransaction(operations -> {
        operations.send("topic1","test executeInTransaction");
        throw new RuntimeException("fail");
    });// 不声明事务:后面报错但前面消息已经发送成功了
   kafkaTemplate.send("topic1","test executeInTransaction");
   throw new RuntimeException("fail");
}

消费者
1、指定topic、partition、offset消费

前面我们在监听消费topic1的时候,监听的是topic1上所有的消息,如果我们想指定topic、指定partition、指定offset来消费呢?也很简单,@KafkaListener注解已全部为我们提供。

api:

/**
 * @Description 同时监听topic1和topic2,监听topic1的0号分区、topic2的 "0号和1号" 分区,指向1号分区的offset初始值为8
 **/
@KafkaListener(id = "consumer1",groupId = "felix-group",topicPartitions = {
        @TopicPartition(topic = "topic1", partitions = { "0" }),
        @TopicPartition(topic = "topic2", partitions = "0", partitionOffsets = @PartitionOffset(partition = "1", initialOffset = "8"))
})
public void onMessage2(ConsumerRecord<?, ?> record) {
    System.out.println("topic:"+record.topic()+"|partition:"+record.partition()+"|offset:"+record.offset()+"|value:"+record.value());
}

属性解释:
① id:消费者ID;
② groupId:消费组ID;
③ topics:监听的topic,可监听多个;
④ topicPartitions:可配置更加详细的监听信息,可指定topic、parition、offset监听。
上面onMessage2监听的含义:监听topic1的0号分区,同时监听topic2的0号分区和topic2的1号分区里面offset从8开始的消息。
注意:topics和topicPartitions不能同时使用

2.批量消费

# 设置批量消费
spring.kafka.listener.type=batch
# 批量消费每次最多消费多少条消息
spring.kafka.consumer.max-poll-records=50

接收消息,用List<>集合。

@KafkaListener(id = "consumer2",groupId = "felix-group", topics = "topic1")
public void onMessage3(List<ConsumerRecord<?, ?>> records) {
    System.out.println(">>>批量消费一次,records.size()="+records.size());
    for (ConsumerRecord<?, ?> record : records) {
        System.out.println(record.value());
    }
}

3、ConsumerAwareListenerErrorHandler 异常处理器
该处理器能处理Kafka使用时发生的异常。因此我们可以用其来进行异常处理。


// 新建一个异常处理器,用@Bean注入
@Bean
public ConsumerAwareListenerErrorHandler consumerAwareErrorHandler() {
    return (message, exception, consumer) -> {
        System.out.println("消费异常:"+message.getPayload());
        return null;
    };
}// 将这个异常处理器的BeanName放到@KafkaListener注解的errorHandler属性里面
@KafkaListener(topics = {"topic1"},errorHandler = "consumerAwareErrorHandler")
public void onMessage4(ConsumerRecord<?, ?> record) throws Exception {
    throw new Exception("简单消费-模拟异常");
}// 批量消费也一样,异常处理器的message.getPayload()也可以拿到各条消息的信息
@KafkaListener(topics = "topic1",errorHandler="consumerAwareErrorHandler")
public void onMessage5(List<ConsumerRecord<?, ?>> records) throws Exception {
    System.out.println("批量消费一次...");
    throw new Exception("批量消费-模拟异常");
}

4、消息过滤器

配置消息过滤只需要为 监听器工厂 配置一个RecordFilterStrategy(消息过滤策略),返回true的时候消息将会被抛弃,返回false时,消息能正常抵达监听容器

@Component
public class KafkaConsumer {
    @Autowired
    ConsumerFactory consumerFactory;// 消息过滤器
    @Bean
    public ConcurrentKafkaListenerContainerFactory filterContainerFactory() {
        ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory();
        factory.setConsumerFactory(consumerFactory);
        // 被过滤的消息将被丢弃
        factory.setAckDiscarded(true);
        // 消息过滤策略
        factory.setRecordFilterStrategy(consumerRecord -> {
            if (Integer.parseInt(consumerRecord.value().toString()) % 2 == 0) {
                return false;
            }
            //返回true消息则被过滤
            return true;
        });
        return factory;
    }// 消息过滤监听
    @KafkaListener(topics = {"topic1"},containerFactory = "filterContainerFactory")
    public void onMessage6(ConsumerRecord<?, ?> record) {
        System.out.println(record.value());
    }
}

5.消息转发
只需要通过一个@SendTo注解

/**
 * @Description 从topic1接收到的消息经过处理后转发到topic2
 **/
@KafkaListener(topics = {"topic1"})
@SendTo("topic2")
public String onMessage7(ConsumerRecord<?, ?> record) {
    return record.value()+"-forward message";
}

6、定时启动、停止监听器
指定的时间点开始或者停止监听器,利用KafkaListenerEndpointRegistry

① 禁止监听器自启动;

② 创建两个定时任务,一个用来在指定时间点启动定时器,另一个在指定时间点停止定时器;

新建一个定时任务类,用注解@EnableScheduling声明,KafkaListenerEndpointRegistry 在SpringIO中已经被注册为Bean,直接注入,设置禁止KafkaListener自启动

@EnableScheduling
@Component
public class CronTimer {/**
     * @KafkaListener注解所标注的方法并不会在IOC容器中被注册为Bean,
     * 而是会被注册在KafkaListenerEndpointRegistry中,
     * 而KafkaListenerEndpointRegistry在SpringIOC中已经被注册为Bean
     **/
    @Autowired
    private KafkaListenerEndpointRegistry registry;@Autowired
    private ConsumerFactory consumerFactory;// 监听器容器工厂(设置禁止KafkaListener自启动)
    @Bean
    public ConcurrentKafkaListenerContainerFactory delayContainerFactory() {
        ConcurrentKafkaListenerContainerFactory container = new ConcurrentKafkaListenerContainerFactory();
        container.setConsumerFactory(consumerFactory);
        //禁止KafkaListener自启动
        container.setAutoStartup(false);
        return container;
    }// 监听器
    @KafkaListener(id="timingConsumer",topics = "topic1",containerFactory = "delayContainerFactory")
    public void onMessage1(ConsumerRecord<?, ?> record){
        System.out.println("消费成功:"+record.topic()+"-"+record.partition()+"-"+record.value());
    }// 定时启动监听器
    @Scheduled(cron = "0 42 11 * * ? ")
    public void startListener() {
        System.out.println("启动监听器...");
        // "timingConsumer"是@KafkaListener注解后面设置的监听器ID,标识这个监听器
        if (!registry.getListenerContainer("timingConsumer").isRunning()) {
            registry.getListenerContainer("timingConsumer").start();
        }
        //registry.getListenerContainer("timingConsumer").resume();
    }// 定时停止监听器
    @Scheduled(cron = "0 45 11 * * ? ")
    public void shutDownListener() {
        System.out.println("关闭监听器...");
        registry.getListenerContainer("timingConsumer").pause();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值