当前位置: 首页 > news >正文

Kafka基本原理

总述

简介

Kafka是最初由Linkedin公司开发,是一个分布式、支持分区的(partition)、多副本的(replica),基于zookeeper协调的分布式消息系统,它的最大的特性就是可以实时的处理大量数据以满足各种需求场景:比如基于hadoop的批处理系统、低延迟的实时系统、Storm/Spark流式处理引擎,web/nginx日志、访问日志,消息服务等等,用scala语言编写,Linkedin于2010年贡献给了Apache基金会并成为顶级开源项目。

使用场景

  • 日志收集:一个公司可以用Kafka收集各种服务的log,通过kafka以统一接口服务的方式开放给各种consumer,例如hadoop、Hbase、Solr等。
  • 消息系统:解耦和生产者和消费者、缓存消息等。
  • 用户活动跟踪:Kafka经常被用来记录web用户或者app用户的各种活动,如浏览网页、搜索、点击等活动,这些活动信息被各个服务器发布到kafka的topic中,然后订阅者通过订阅这些topic来做实时的监控分析,或者装载到
  • hadoop、数据仓库中做离线分析和挖掘。
  • 运营指标:Kafka也经常用来记录运营监控数据。包括收集各种分布式应用的数据,生产各种操作的集中反馈,比如报警和报告。

Kafka基本概念

kafka是一个分布式的,分区的消息(官方称之为commitlog)服务。它提供一个消息系统应该具备的功能,但是确有着独特的设计。可以这样来说,Kafka借鉴了JMS规范的思想,但是确并没有完全遵循JMS规范。首先,让我们来看一下基础的消息(Message)相关术语:

  • Broker:消息中间件处理节点,一个Kafka节点就是一个broker,一个或者多个Broker可以组成一个Kafka集群
  • Topic:Kafka根据topic对消息进行归类,发布到Kafka集群的每条消息都需要指定一个topic
  • Producer:消息生产者,向Broker发送消息的客户端
  • Consumer:消息消费者,从Broker读取消息的客户端
  • ConsumerGroup:每个Consumer属于一个特定的Consumer Group,一条消息可以被多个不同的Consumer Group消费,但是一个Consumer Group中只能有一个Consumer能够消费该消息
  • Partition:物理上的概念,一个topic可以分为多个partition,每个partition内部消息是有序的

因此,从一个较高的层面上来看,producer通过网络发送消息到Kafka集群,然后consumer来进行消费。

Untitled

服务端(brokers)和客户端(producer、consumer)之间通信通过TCP协议来完成。

Kafka基本使用

环境准备

由于Kafka是用Scala语言开发的,运行在JVM上,因此在安装Kafka之前需要先安装JDK。

yum install java‐1.8.0‐openjdk* ‐y

kafka依赖zookeeper,所以需要先安装zookeeper

wget https://mirror.bit.edu.cn/apache/zookeeper/zookeeper‐3.5.8/apache‐zookeeper‐3.5.8‐bin.tar.gz
tar ‐zxvf apache‐zookeeper‐3.5.8‐bin.tar.gz
cd apache‐zookeeper‐3.5.8‐bin
cp conf/zoo_sample.cfg conf/zoo.cfg# 启动zookeeper
bin/zkServer.sh start
bin/zkCli.sh
ls / #查看zk的根目录相关节点

下载安装包

下载2.4.1 release版本,并解压:
wget https://mirror.bit.edu.cn/apache/kafka/2.4.1/kafka_2.11‐2.4.1.tgz # 2.11是scala的版本,2.4.1是kafka的版本
tar ‐xzf kafka_2.11‐2.4.1.tgz
cd kafka_2.11‐2.4.1

修改配置

修改配置文件config/server.properties:

#broker.id属性在kafka集群中必须要是唯一
broker.id=0
#kafka部署的机器ip和提供服务的端口号
listeners=PLAINTEXT://192.168.65.60:9092
#kafka的消息存储文件
log.dir=/usr/local/data/kafka‐logs
#kafka连接zookeeper的地址
zookeeper.connect=192.168.65.60:2181

启动服务

现在来启动kafka服务:
启动脚本语法:`kafka­server­start.sh [­daemon] server.properties`
可以看到,server.properties的配置路径是一个强制的参数,­daemon表示以后台进程运行,否则ssh客户端退出后,就会停止服务。(注意,在启动kafka时会使用linux主机名关联的ip地址,所以需要把主机名和linux的ip映射配置到本地host里,用vim /etc/hosts)

# 启动kafka,运行日志在logs目录的server.log文件里
bin/kafka‐server‐start.sh ‐daemon config/server.properties #后台启动,不会打印日志到控制台或者用
bin/kafka‐server‐start.sh config/server.properties &

# 我们进入zookeeper目录通过zookeeper客户端查看下zookeeper的目录树
bin/zkCli.sh
ls / #查看zk的根目录kafka相关节点
ls /brokers/ids #查看kafka节点

# 停止kafka
bin/kafka‐server‐stop.sh

server.properties核心配置

PropertyDefaultDescription
broker.id0每个broker都可以用一个唯一的非负整数id进行标识;这个id可以作为broker的“名字”,你可以选择任意你喜欢的数字作为id,只要id是唯一的即可。
log.dirs/tmp/kafka-logskafka存放数据的路径。这个路径并不是唯一的,可以是多个,路径之间只需要使用逗号分隔即可;每当创建新partition时,都会选择在包含最少partitions的路径下进行。
listenersPLAINTEXT://192.168.65.60:9092server接受客户端连接的端口,ip配置kafka本机ip即可
zookeeper.connectlocalhost:2181zooKeeper连接字符串的格式为:hostname:port,此处hostname和port分别是ZooKeeper集群中某个节点的host和port;zookeeper如果是集群,连接方式为hostname1:port1, hostname2:port2, hostname3:port3
log.retention.hours        168每个日志文件删除之前保存的时间。默认数据保存时间对所有topic都一样。
num.partitions1创建topic的默认分区数
default.replication.factor1自动创建topic的默认副本数量,建议设置为大于等于2
min.insync.replicas1当producer设置acks为-1时,min.insync.replicas指定replicas的最小数目(必须确认每一个repica的写数据都是成功的),如果这个数目没有达到,producer发送消息会产生异常
delete.topic.enablefalse是否允许删除主题

创建主题

现在我们来创建一个名字为“test”的Topic,这个topic只有一个partition,并且备份因子也设置为1:

bin/kafka‐topics.sh ‐‐create ‐‐zookeeper 192.168.65.60:2181 ‐‐replication‐factor 1 ‐‐partitions 1 ‐‐topic test

现在我们可以通过以下命令来查看kafka中目前存在的topic

bin/kafka-topics.sh --list --zookeeper 119.91.21.45:2181

Untitled

除了我们通过手动的方式创建 Topic,当 producer 发布一个消息到某个指定的 Topic 时,如果这个 Topic 不存在,那么 Kafka 也会自动创建。 

删除主题

bin/kafka-topics.sh --delete --topic test --zookeeper 119.91.21.45:2181

发送消息

kafka自带了一个 producer 命令客户端,可以从本地文件中读取内容,或者我们也可以在命令行中直接输入内容,并将这些内容以消息的形式发送到 kafka 集群中。在默认情况下,每一个行会被当做一个独立的消息。

首先我们要运行发布消息的脚本,然后在命令行中输入要发送的消息内容:

bin/kafka-console-producer.sh --broker-list 119.91.21.45:9092 --topic test 
>this is a msg
>this is a another msg

Untitled

 消费消息

对于 consumer,kafka 同样也携带了一个命令行客户端,会将获取到的内容在命令中进行输出,默认是消费 consumer 启动之后的消息:

bin/kafka-console-consumer.sh --bootstrap-server 119.91.21.45:9092 --topic test

Untitled

如果想要消费之前的消息可以通过 --from-beginning 参数指定,如下命令: 

bin/kafka-console-consumer.sh --bootstrap-server 119.91.21.45:9092 --from-beginning --topic test

Untitled

消费多主题 

bin/kafka-console-consumer.sh --bootstrap-server 119.91.21.45:9092 --whitelist "test|test-2"

单播消费

一条消息只能被某一个消费者消费的模式,类似 queue 模式(队列),只需让所有消费者在同一个消费组里即可

分别在两个客户端执行如下消费命令,然后往主题里发送消息,结果只有一个客户端能收到消息

bin/kafka-console-consumer.sh --bootstrap-server 119.91.21.45:9092 --consumer-property group.id=testGroup --topic test

多播消费

一条消息能被多个消费者消费的模式,类似 publish-subscribe 模式(发布-订阅),针对 Kafka 同一条消息只能被同一个消费组下的某一个消费者消费的特性,要实现多播只要保证这些消费者属于不同的消费组即可。我们再增加一个消费者,该消费者属于 testGroup-2 消费组,结果两个客户端都能收到消息

bin/kafka-console-consumer.sh --bootstrap-server 119.91.21.45:9092 --consumer-property group.id=testGroup-2 --topic test

查看消费组名

bin/kafka-consumer-groups.sh --bootstrap-server 119.91.21.45:9092 --list

Untitled

查看消费组的消费偏移量 

bin/kafka-consumer-groups.sh --bootstrap-server 119.91.21.45:9092 --describe --group testGroup

Untitled

  • current-offset:当前消费组的已消费偏移量
  • log-end-offset:主题对应分区消息的结束偏移量(HW)
  • lag:当前消费组未消费的消息数

主题Topic和消息日志Log 

可以理解 Topic 是一个类别的名称,同类消息发送到同一个 Topic 下面。对于每一个 Topic,下面可以有多个分区(Partition)日志文件:topic是逻辑上的概念,partition是物理上的概念

Untitled

Partition 是一个有序的 message 序列,这些 message 按顺序添加到一个叫做 commit log 的文件中。每个 partition 中的消息都有一个唯一的编号,称之为offset,用来唯一标识某个分区中的 message。

每个 partition,都对应一个 commit log 文件。一个 partition 中的 message 的 offset 都是唯一的,但是不同的 partition 中的 message 的 offset 可能是相同的。

kafka 一般不会删除消息,不管这些消息有没有被消费。只会根据配置的日志保留时间(log.retention.hours)确认消息多久被删除,默认保留最近一周的日志消息。kafka 的性能与保留的消息数据量大小没有关系,因此保存大量的数据消息日志信息不会有什么影响。

每个 consumer 是基于自己在 commit log 中的消费进度(offset)来进行工作的。在 kafka 中,消费 offset 由 consumer 自己来维护;一般情况下我们按照顺序逐条消费 commit log 中的消息,当然我们也可以通过指定 offset 来重复消费某些消息,或者跳过某些消息。

这意味着 kafka 中的 consumer 对集群的影响是非常小的,添加一个或者减少一个 consumer,对于集群或者其他 consumer 来说都是没有影响的,因为每个consumer 都是维护各自的消费 offset。

创建多个分区的主题

bin/kafka-topics.sh --create --zookeeper 119.91.21.45:2181 --replication-factor 1 --partitions 2 --topic test1

查看 topic 的情况

bin/kafka-topics.sh --describe --zookeeper 119.91.21.45:2181 --topic test1

Untitled

 第一行是所有分区的概要信息,之后的每一行表示每一个 partition 的信息。

  • Leader 节点负责给定 Partition 的所有读写请求,同一个主题不同分区 leader 副本一般不一样(为了容灾)
  • Replicas 表示某个 Partition 在哪几个 Broker 上存在备份。不管这个节点是不是 Leader 节点,甚至这个节点挂了,也会列出。
  • Isr 是 Replicas 的一个子集,它只列出当前还存活着的,并且已经同步备份了该 Partition 的节点。

我们可以运行相同的命令查看之前创建的名称为 “test” 的 topic:

bin/kafka-topics.sh --describe --zookeeper 119.91.21.45:2181 --topic test

Untitled

之前设置了 topic 的 partition 数量为1,备份因子为1,因此显示就如上所示了。

可以进入 kafka 的数据文件存储目录查看 test 和 test1 主题的消息日志文件:        

Untitled

消息日志文件主要存放在分区文件夹里的以 log 结尾的日志文件里,如下是 test1 主题对应的分区 0 的消息日志:

Untitled

可以通过如下命令增加 topic 的分区数量(目前 kafka 不支持减少分区):

bin/kafka-topics.sh -alter --partitions 3 --zookeeper 119.91.21.45:2181 --topic test

理解 Partition

一个 topic 代表逻辑上的一个业务数据集,比如按照数据库里不同表的数据操作消息区分放入不同的 topic,订单相关操作的消息放入订单 topic,用户相关操作的消息放入用户 topic,对于大型网站来说,后端数据都是海量的,订单消息很可能是非常巨量的,比如有几百个 G 甚至达到 TB 级别,如果把这么多数据都放在一台机器上那肯定会有容量限制问题,那么就可以在 topic 内部划分多个 partition 来分片存储数据,不同的 partition 可以位于不同的机器上,每台机器上都运行一个 Kafka 的进程 Broker。

为什么要对 Topic 下的数据进行分区存储?

1、commit log 文件会受到所在机器的文件系统大小的限制,分区之后可以将不同的分区放在不同的机器上,相当于对数据做了分布式存储,理论上一个topic 可以处理任意数量的数据。

2、为了提高并行度。

kafka集群实战

对于 kafka 来说,一个单独的 broker 意味着 kafka 集群中只有一个节点。要想增加 kafka 集群中的节点数量,只需要多启动几个 broker 实例即可。为了有更好的理解,现在我们在一台机器上同时启动三个 broker 实例。

首先,我们需要建立好其他2个 broker 的配置文件:

cp config/server.properties config/server-1.properties
cp config/server.properties config/server-2.properties

配置文件需要修改的内容分别如下:

1、config/server-1.properties

# broker.id属性在kafka集群中必须是唯一的
broker.id=1
# kafka部署的机器ip和提供服务的端口号
listeners=PLAINTEXT://10.0.12.14:9093 # 内网地址
advertised.listeners=PLAINTEXT://119.91.21.45:9093 # 公网地址
log.dir=/usr/local/data/kafka-logs-1
# kafka连接zookeeper的地址,要把多个kafka实例组成集群,对应连接的zookeeper必须相同
zookeeper.connect=119.91.21.45:2181

2、config/server-2.properties

# broker.id属性在kafka集群中必须是唯一的
broker.id=2
# kafka部署的机器ip和提供服务的端口号
listeners=PLAINTEXT://10.0.12.14:9094 # 内网地址
advertised.listeners=PLAINTEXT://119.91.21.45:9094 # 公网地址
log.dir=/usr/local/data/kafka-logs-2
# kafka连接zookeeper的地址,要把多个kafka实例组成集群,对应连接的zookeeper必须相同
zookeeper.connect=119.91.21.45:2181

目前我们已经有一个 zookeeper 实例和一个 broker 实例在运行了,现在我们只需要再启动2个 broker 实例即可:

bin/kafka-server-start.sh -daemon config/server-1.properties
bin/kafka-server-start.sh -daemon config/server-2.properties

注意:如果我们的云服务器内存较小,启动多个 Kafka 会显示内存不足,这时候可以通过修改 Kafka 启动内存大小来解决!

vim /bin/kafka-server-start.sh

Untitled

查看 zookeeper 确认集群节点是否都注册成功: 

ls /brokers/ids

Untitled

现在我们创建一个新的 topic,副本数设置为3,分区数设置为2: 

bin/kafka-topics.sh --create --zookeeper 119.91.21.45:2181 --replication-factor 3 --partitions 2 --topic my-replicated-topic

Untitled

查看 topic 的情况: 

bin/kafka-topics.sh --describe --zookeeper 119.91.21.45:2181 --topic my-replicated-topic
  • Leader 节点负责给定 Partition 的所有读写请求,同一个主题不同分区的 leader 副本一般是不一样的(为了容灾)
  • Replicas 表示某个 Partition 在哪几个 Broker 上存在备份。不管这个节点是不是 Leader 节点,甚至这个节点挂了,也会列出。
  • Isr 是 Replicas 的一个子集,它只列出当前还存活着的,并且已经同步备份了该 Partition 的节点。

现在我们向新建的 my-replicated-topic 中发送一些 Message,Kafka 集群可以加上所有 Kafka 节点:

bin/kafka-console-producer.sh --broker-list 8.142.132.36:9092,8.142.132.36:9093,8.142.132.36:9094 --topic my-replicated-topic
>my test msg 1
>my test msg 2

集群消费

log 的 partitions 分布在 kafka 集群中的不同 broker 上,每个 broker 可以请求备份其他 broker 上的 partition 数据。kafka 集群支持配置一个 partition 备份的数量。

针对每个 partition,都有一个 broker 起到 “leader” 的作用,0个或多个其他的 broker 作为 “follwers” 的作用。leader 处理所有针对这个 partition 的读写请求,而 followers 则被动复制 leader 的结果,不提供读写(主要是为了保证多副本数据与消费的一致性)。如果这个 leader 失效了,那么其中的一个 follower 将会自动的变成新的 leader。

Producers

生产者将消息发送到 topic 中去,同时负责选择将 message 发送到 topic 的哪一个 partition 中。通过 round-robin 做简单的负载均衡,也可以根据消息中的某一个关键字来进行区分。通常第二种方式使用的更多。

Consumers

传统的消息传递模式有2种:队列(queue)和(publish-subscribe)

  • queue 模式:多个 consumer 从服务器中读取数据,消息只会到达一个 consumer。
  • publish-subscribe 模式:消息会被广播给所有的 consumer。

Kafka 基于这2种模式提供了一种 consumer 的抽象概念:consumer group

  • queue 模式:所有的 consumer 都位于同一个 consumer group 下。
  • publish-subscribe 模式:所有的 consumer 都有着自己唯一的 consumer group。

Untitled

上图说明:由2个 broker 组成的 kafka 集群,某个主题总共有4个 partition(P0-P3),分别位于不同的 broker 上。这个集群由2个 Consumer Group 进行消费, A 有 2 个 consumer instances ,B 有 4 个。

通常一个 topic 会有几个 consumer group,每个 consumer group 都是一个逻辑上的订阅者( logical subscriber )。每个 consumer group 由多个 consumer instance 组成,从而达到可扩展和容灾的功能。

顺序消费 

一个 partition 同一时刻在一个 consumer group 中只能由一个 consumer instance 进行消费,从而保证消费顺序。

consumer group 中的 consumer instance 的数量不能比一个 Topic 中的 partition 数量还多,否则多出来的 consumer 消费不到消息。

Kafka 只在 partition 的范围内保证消息消费的局部顺序性,不能在同一个 topic 中的多个 partition 中保证总的消费顺序性。

如果需要在总体上保证消费的顺序,那么我们可以通过将 topic 的 partition 数量设置为1,将 consumer group 中的 consumer instance 数量也设置为1,但是这样会影响性能,所以 kafka 的顺序消费很少用。

Java客户端访问Kafka

引入maven依赖

<dependency><groupId>org.apache.kafka</groupId><artifactId>kafka-clients</artifactId><version>2.4.1</version>
</dependency>

消息发送端

public class MsgProducer {private final static String TOPIC_NAME = "my-replicated-topic";public static void main(String[] args) throws InterruptedException, ExecutionException {Properties props = new Properties();props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "119.91.21.45:9092,119.91.21.45:9093");/*** 发送消息持久化机制参数* 1、acks=0: 表示producer不需要等待任何broker确认收到消息的回复,就可以继续发送下一条消息。性能最高,但是最容易丢消息。* 2、acks=1: 至少要等待leader已经成功将数据写入本地log,但是不需要等待所有follower是否成功写入。就可以继续发送下一条消息。* 这种情况下,如果follower没有成功备份数据,而此时leader又挂掉,则消息会丢失。* 3、acks=-1或all: 需要等待 min.insync.replicas(默认为1,推荐配置大于等于2) 这个参数配置的副本个数都成功写入日志,* 这种策略会保证只要有一个备份存活就不会丢失数据。这是最强的数据保证。一般除非是金融级别,或跟钱打交道的场景才会使用这种配置。*/props.put(ProducerConfig.ACKS_CONFIG, "1");/*** 发送失败会重试,默认重试间隔100ms,重试能保证消息发送的可靠性,但是也可能造成消息重复发送,比如网络抖动,* 所以需要在接收者那边做好消息接收的幂等性处理*/props.put(ProducerConfig.RETRIES_CONFIG, 3);// 重试间隔设置props.put(ProducerConfig.RETRY_BACKOFF_MS_CONFIG, 300);// 设置发送消息的本地缓冲区,如果设置了该缓冲区,消息会先发送到本地缓冲区,可以提高消息发送性能,默认值是33554432,即32MBprops.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 33554432);/*** kafka本地线程会从缓冲区取数据,批量发送到broker,设置批量发送消息的大小,默认值是16384,即16kb,就是说一个batch满了16kb就发送出去*/props.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384);/*** 默认值是0,意思就是消息必须立即被发送,但这样会影响性能* 一般设置10毫秒左右,就是说这个消息发送完后会进入本地的一个batch,如果10毫秒内,这个batch满了16kb就会随batch一起被发送出去* 如果10毫秒内,batch没满,那么也必须把消息发送出去,不能让消息的发送延迟时间太长*/props.put(ProducerConfig.LINGER_MS_CONFIG, 10);// 把发送的key从字符串序列化为字节数组props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());// 把发送消息value从字符串序列化为字节数组props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());// 创建Kafka发送者Producer<String, String> producer = new KafkaProducer<String, String>(props);int msgNum = 5;final CountDownLatch countDownLatch = new CountDownLatch(msgNum);for (int i = 1; i <= msgNum; i++) {// 构建订单对象Order order = new Order(i, 100 + i, 1, 1000.00);/*** 指定发送分区* ProducerRecord<String, String> producerRecord = new ProducerRecord<String, String>(TOPIC_NAME, 0, order.getOrderId().toString(), JSON.toJSONString(order));* 未指定发送分区,具体发送的分区计算公式:hash(key) % partitionNum*/ProducerRecord<String, String> producerRecord = new ProducerRecord<String, String>(TOPIC_NAME, order.getOrderId().toString(), JSON.toJSONString(order));// 等待消息发送成功的同步阻塞方法
//            RecordMetadata metadata = producer.send(producerRecord).get();
//            System.out.println("同步方式发送消息结果:" + "topic-" + metadata.topic() + "|partition-" + metadata.partition() + "|offset-" + metadata.offset());// 异步回调方式发送消息producer.send(producerRecord, new Callback() {public void onCompletion(RecordMetadata metadata, Exception exception) {if (exception != null) {System.err.println("发送消息失败:" + exception.getStackTrace());}if (metadata != null) {System.out.println("异步方式发送消息结果:" + "topic-" + metadata.topic() + "|partition-" + metadata.partition() + "|offset-" + metadata.offset());}countDownLatch.countDown();}});//送积分 TODO}countDownLatch.await(5, TimeUnit.SECONDS);producer.close();}}

消息接收端

public class MsgConsumer {private final static String TOPIC_NAME = "my-replicated-topic";private final static String CONSUMER_GROUP_NAME = "testGroup";public static void main(String[] args) throws Exception {Properties props = new Properties();props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "119.91.21.45:9092,119.91.21.45:9093,119.91.21.45:9094");// 消费分组名props.put(ConsumerConfig.GROUP_ID_CONFIG, CONSUMER_GROUP_NAME);// 是否自动提交offset,默认就是trueprops.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false");// 自动提交offset的间隔时间(一般不会用自动提交)
//        props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "1000");
//        props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false");/*** 当消费主题的是一个新的消费组,或者指定offset的消费方式,offset不存在,那么应该如何消费* latest(默认) :只消费自己启动之后发送到主题的消息* earliest:第一次从头开始消费,以后按照offset记录继续消费,这个需要区别于consumer.seekToBeginning(每次都从头开始消费)*///props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");/*** consumer给broker发送心跳的间隔时间,broker接收到心跳如果此时有rebalance发生会通过心跳响应将* rebalance方案下发给consumer,这个时间可以稍微短一点*/props.put(ConsumerConfig.HEARTBEAT_INTERVAL_MS_CONFIG, 1000);/*** 服务端broker多久感知不到一个consumer心跳就认为他故障了,会将其踢出消费组,对应的Partition也会被重新分配给其他consumer,默认是10秒*/props.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, 10 * 1000);// 一次poll最大拉取消息的条数,如果消费者处理速度很快,可以设置大点,如果处理速度一般,可以设置小点props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 50);/*** 如果两次poll操作间隔超过了这个时间,broker就会认为这个consumer处理能力太弱,* 会将其踢出消费组,将分区分配给其他consumer消费*/props.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, 30 * 1000);// 将key和value进行反序列化props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());KafkaConsumer<String, String> consumer = new KafkaConsumer<String, String>(props);// 消费者订阅主题
//        consumer.subscribe(Arrays.asList(TOPIC_NAME)); // 如果指定了分区,那么这行代码不需要写// 消费指定分区
//        consumer.assign(Arrays.asList(new TopicPartition(TOPIC_NAME, 0)));// 消息回溯消费
//        consumer.assign(Arrays.asList(new TopicPartition(TOPIC_NAME, 0)));
//        consumer.seekToBeginning(Arrays.asList(new TopicPartition(TOPIC_NAME, 0)));/*** 指定offset消费* 从0分区的offset等于10开始消费*/
//        consumer.assign(Arrays.asList(new TopicPartition(TOPIC_NAME, 0)));
//        consumer.seek(new TopicPartition(TOPIC_NAME, 0), 10);// 从指定时间点开始消费
//        List<PartitionInfo> topicPartitions = consumer.partitionsFor(TOPIC_NAME);
//        // 从1小时前开始消费
//        long fetchDataTime = new Date().getTime() - 1000 * 60 * 60;
//        Map<TopicPartition, Long> map = new HashMap<>();
//        for (PartitionInfo par : topicPartitions) {
//            map.put(new TopicPartition(TOPIC_NAME, par.partition()), fetchDataTime);
//        }
//        // 获取最近一小时的offset
//        Map<TopicPartition, OffsetAndTimestamp> parMap = consumer.offsetsForTimes(map);
//        for (Map.Entry<TopicPartition, OffsetAndTimestamp> entry : parMap.entrySet()) {
//            TopicPartition key = entry.getKey();
//            OffsetAndTimestamp value = entry.getValue();
//            if (key == null || value == null) continue;
//            Long offset = value.offset();
//            System.out.println("partition-" + key.partition() + "|offset-" + offset);
//            System.out.println();
//            //根据消费里的timestamp确定offset
//            if (value != null) {
//                consumer.assign(Arrays.asList(key));
//                consumer.seek(key, offset);
//            }
//        }while (true) {/*** poll()是拉取消息的长轮询* 何为长轮询?如果一秒钟之内没有拉到消息,那么客户端会一直进行拉取,直到拉取的总时间为1秒。如果某一次拉到消息,那么会立即返回*/ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(1000));for (ConsumerRecord<String, String> record : records) {System.out.printf("收到消息:partition = %d,offset = %d, key = %s, value = %s%n", record.partition(), record.offset(), record.key(), record.value());}if (records.count() > 0) {/*** 手动同步提交offset,当前线程会阻塞直到offset提交成功* 一般使用同步提交,因为提交之后一般也没有什么逻辑代码了*/consumer.commitSync();/*** 手动异步提交offset,当前线程提交offset不会阻塞,可以继续处理后面的代码逻辑*/
//                consumer.commitAsync(new OffsetCommitCallback() {
//                    @Override
//                    public void onComplete(Map<TopicPartition, OffsetAndMetadata> offsets, Exception exception) {
//                        if (exception != null) {
//                            System.err.println("Commit failed for " + offsets);
//                            System.err.println("Commit failed exception: " + exception.getStackTrace());
//                        }
//                    }
//                });}}}
}

Spring Boot整合Kafka

引入依赖

引入spring boot kafka依赖

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

配置文件

server:port: 8080spring:kafka:bootstrap-servers: 119.91.21.45:9092,119.91.21.45:9093,119.91.21.45:9094producer: # 生产者retries: 3 # 设置大于0的值,则客户端会将发送失败的记录重新发送batch-size: 16384buffer-memory: 33554432acks: 1# 指定消息key和消息体的编解码方式key-serializer: org.apache.kafka.common.serialization.StringSerializervalue-serializer: org.apache.kafka.common.serialization.StringSerializerconsumer:group-id: default-groupenable-auto-commit: falseauto-offset-reset: earliestkey-deserializer: org.apache.kafka.common.serialization.StringDeserializervalue-deserializer: org.apache.kafka.common.serialization.StringDeserializerlistener:# RECORD:# 当每一条记录被消费者监听器(ListenerConsumer)处理之后提交# BATCH:# 当每一批poll()的数据被消费者监听器(ListenerConsumer)处理之后提交# TIME:# 当每一批poll()的数据被消费者监听器(ListenerConsumer)处理之后,距离上次提交时间大于TIME时提交# COUNT:# 当每一批poll()的数据被消费者监听器(ListenerConsumer)处理之后,被处理record数量大于等于COUNT时提交# COUNT_TIME:# TIME | COUNT 有一个条件满足时提交# MANUAL:# 当每一批poll()的数据被消费者监听器(ListenerConsumer)处理之后, 手动调用Acknowledgment.acknowledge()后提交# MANUAL_IMMEDIATE:# 手动调用Acknowledgment.acknowledge()后立即提交ack-mode: MANUAL_IMMEDIATE

发送者代码

@RestController
public class KafkaController {private final static String TOPIC_NAME = "my-replicated-topic";@Autowiredprivate KafkaTemplate<String, String> kafkaTemplate;@RequestMapping("/send")public void send() {kafkaTemplate.send(TOPIC_NAME, 0, "key", "this is a msg");}}

消费者代码

@Component
public class MyConsumer {/*** @param record* @KafkaListener(groupId = "testGroup", topicPartitions = {* @TopicPartition(topic = "topic1", partitions = {"0", "1"}),* @TopicPartition(topic = "topic2", partitions = "0",* partitionOffsets = @PartitionOffset(partition = "1", initialOffset = "100"))* },concurrency = "6")* //concurrency就是同组下的消费者个数,就是并发消费数,必须小于等于分区总数*/@KafkaListener(topics = "my-replicated-topic", groupId = "xiexuGroup")public void listenZhugeGroup(ConsumerRecord<String, String> record, Acknowledgment ack) {String value = record.value();System.out.println(value);System.out.println(record);// 手动提交offsetack.acknowledge();}// 配置多个消费组/*@KafkaListener(topics = "my-replicated-topic",groupId = "tulingGroup")public void listenTulingGroup(ConsumerRecord<String, String> record, Acknowledgment ack) {String value = record.value();System.out.println(value);System.out.println(record);ack.acknowledge();}*/
}

相关文章:

Kafka基本原理

总述 简介 Kafka是最初由Linkedin公司开发&#xff0c;是一个分布式、支持分区的&#xff08;partition&#xff09;、多副本的&#xff08;replica&#xff09;&#xff0c;基于zookeeper协调的分布式消息系统&#xff0c;它的最大的特性就是可以实时的处理大量数据以满足各…...

css3的重点内容

css3的重点内容 浮动 父级边框塌陷问题 浮动的清除 clear:left; //清除左侧浮动 clear:right; //清除右侧浮动 clear:both; //清除两侧浮动解决方案 增加父级元素的高度增加一个空的div&#xff0c;之后清除浮动通过overflow来进行相关元素的修剪给父类添加相应的伪类元素…...

《Roller: Fast and Efficient Tensor Compilation for Deep Learning》

《Roller: Fast and Efficient Tensor Compilation for Deep Learning》 用于深度学习 快速高效的张量编译器 作者 微软亚洲研究院以及多伦多大学等多所高校 摘要 当前编译为了产生高效的kernel时&#xff0c;搜索空间大&#xff0c;通常使用机器学习的方法 找到最优的方案…...

顺丰同城测试开发一面 49min答案,全文7000字,面试总结都在这里了

今天给大家分享一份顺丰同城的测试开发一面面试真题。老规矩&#xff0c;当你看到这份面试题的时候&#xff0c;先不要着急去看答案&#xff0c;你可以想想假如你在面试现场&#xff0c;你会怎么回答&#xff1f;这个思考的过程其实也是很重要的。 全文7000字干货&#xff0c;…...

docker启动容器服务之后访问失败

关于docker启动容器服务之后&#xff0c;宿主机访问失败&#xff08;解决方法&#xff09; 注&#xff1a;在进行docker容器启动宿主机进行容器访问时&#xff0c;无需进行网络的配置&#xff0c;docker容器在启动时会自动解决 第一种原因及修改方法 在进行启动的时候&#…...

GraalVM-云原生时代的JVM(Java)

文章目录一、GraalVM是什么&#xff1f;二、GraalVM有哪些特点&#xff1f;2.1、高性能2.2、多语言支持2.3、互操作性2.4、安全性三、GraalVM的应用效果3.1、提高性能3.2、简化开发3.3、降低成本3.4、节省资源3.5、支持云环境四、使用GraalVM编译springboot应用程序4.1、下载并…...

如何外网登录访问瑞友天翼应用虚拟化系统?——快解析内网端口映射方案

瑞友天翼应用虚拟化系统&#xff08;GWT System&#xff09;是国内具有自主知识产权的应用虚拟化平台&#xff0c;是基于服务器计算&#xff08;Server-based Computing&#xff09;的应用虚拟化平台。如何将内网平台提供到互联网上外网访问&#xff0c;是我们比较关注的问题。…...

蓝海彤翔执行副总裁张加廷接受【联播苏州】独家专访

今年春节档&#xff0c;科幻类电影《流浪地球2》票房口碑双丰收&#xff0c;截至目前&#xff0c;累计票房已破 38 亿&#xff0c;淘票票评分 9.6 &#xff0c;影片的特效质感可以媲美国际顶尖水平。其中&#xff0c;蓝海彤翔为影片的后期制作提供了出色的渲染服务。2月21日&am…...

iOS Airplay Screen Mirroring 同屏技术详解

投屏技术已经被大量用在身边的产品&#xff0c;比如电视投屏&#xff0c;投影仪&#xff0c;视频会议产品中。 在iOS平台外的其他平台中都已经有非常成熟的标准和实现。但在封闭的苹果iOS和Mac系统中&#xff0c;苹果使用私有的Airplay协议进行多屏互动&#xff0c;只开放给自己…...

更新 Python 100道基础入门检测练习题【下篇】(附答案)

前言 大家早好、午好、晚好吖 ❤ ~ 爆肝更新 Python 100道基础入门练习题【篇上】 更多精彩内容、资源皆可点击文章下方名片获取此处跳转 实例021&#xff1a;猴子偷桃 题目&#xff1a; 猴子吃桃问题&#xff1a;猴子第一天摘下若干个桃子&#xff0c;当即吃了一半&#xf…...

[RDMA-高级计算机网络report] Congestion Control for Large-Scale RDMA Departments

本文主要解决的问题是在RoCEv2体系中&#xff0c;基于优先级的拥塞控制PFC是一种粗粒度的机制。 它在端口&#xff08;或端口加优先级&#xff09;级别上运行&#xff0c;并且不区分流。PAUSE机制是基于每个端口&#xff08;和优先级&#xff09;的&#xff0c;而不是基于每个流…...

ROS2功能包Hello world(python)

文章目录环境准备Python创建工作空间、功能包及节点方法编译使用环境准备 为了便于日后复现&#xff0c;相关环境已经打包到docker中。 拉取docker镜像 docker pull 1224425503/ros2_foxy_full:latest新建容器 docker run -dit --rm --privilegedtrue --network host -e NV…...

数学建模竞赛的一些心得体会

1.数学建模经验首先简要的介绍一下我的情况。数学建模我也是在大一暑假开始接触的&#xff0c;之前对其没有任何的了解。我本身对数学也有相对较厚的兴趣&#xff0c;同时我也是计算机专业的学生&#xff0c;因此&#xff0c;我觉得我可参加数学建模的这个比赛。大一的暑假参加…...

什么是自动化测试?自动化测试现状怎么样?

什么是自动化测试&#xff1a;其实自动化测试&#xff0c;就是让我们写一段程序去测试另一段程序是否正常的过程&#xff0c;自动化测试可以更加省力的替代一部分的手动操作。 现在自动化测试的现状&#xff0c;也是所有学习者关心的&#xff0c;但现在国内公司主要是以功能测…...

CHAPTER 2 Web HA集群部署 - Heartbeat

Web HA集群部署 - Heartbeat1. Heartbeat 概述1.1 Heartbeat主要组成部分2. 环境依赖2.1 环境及组件软件2.2 关闭firewalld & selinux2.3 配置双机互信&#xff0c;SSH密钥登录​​2.4 同步时间&#xff08;以主节点时间为准&#xff09;2.5 配置域名解析3 安装软件3.1 安装…...

蓝桥杯每日一题:不同路径数(dfs深度优先)

给定一个 nm的二维矩阵&#xff0c;其中的每个元素都是一个 [1,9] 之间的正整数。 从矩阵中的任意位置出发&#xff0c;每次可以沿上下左右四个方向前进一步&#xff0c;走过的位置可以重复走。 走了 k 次后&#xff0c;经过的元素会构成一个 (k1) 位数。 请求出一共可以走出…...

NCRE计算机等级考试Python真题(十)

第十套试题1、数据库系统的核心是___________。A.数据库管理系统B.数据模型C.软件工具D.数据库正确答案&#xff1a; A2、下列叙述中正确的是___________。A.线性表链式存储结构的存储空间可以是连续的&#xff0c;也可以是不连续的B.线性表链式存储结构与顺序存储结构的存储空…...

【蓝桥杯嵌入式】点亮LED灯,流水灯的原理图解析与代码实现——STM32

&#x1f38a;【蓝桥杯嵌入式】专题正在持续更新中&#xff0c;原理图解析✨&#xff0c;各模块分析✨以及历年真题讲解✨都在这儿哦&#xff0c;欢迎大家前往订阅本专题&#xff0c;获取更多详细信息哦&#x1f38f;&#x1f38f;&#x1f38f; &#x1fa94;本系列专栏 - 蓝…...

RK3288-android8-es7210-阵列麦克风

ES7210驱动包 应需求调试一个ES7210的阵列麦克风 首先移植 From 234647c69a57c32198c65836e7fc521dc22e444b Mon Sep 17 00:00:00 2001 From: LuoXiaoTan <lxt@rock-chips.com> Date: Tue, 10 Jul 2018 18:08:50 -0700 Subject: [PATCH] ASoC: codecs: add es7210 adc …...

硬件工程师常见问题与答疑

在工作中&#xff0c;尤其是做了很多年的&#xff0c;有些问题可能不知道&#xff0c;又不好意思问&#xff0c;怕别人说你连这个都不知道&#xff1f;很尴尬&#xff0c;而且百度又搜不到&#xff0c;本博主收集了很多答疑&#xff0c;希望里面有对你有用的&#xff0c;或者是…...

【Java】Java进阶学习笔记(一)—— 面向对象(封装)

【Java】Java进阶学习笔记&#xff08;一&#xff09;—— 面向对象&#xff08;封装&#xff09;一、类中成分1、类中成分2、this关键字this() 访问构造器方法3、static关键字1. 成员变量的区分2. 成员方法的区分3. 成员变量访问语法的区分二、封装1、封装的定义封装的好处2、…...

jsp拆迁管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 JSP 拆迁管理系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql5.0&…...

CCNP350-401学习笔记(易错题合集)

CCNP350-401学习笔记&#xff08;1-50题&#xff09;_殊彦_sy的博客-CSDN博客CCNP350-401学习笔记&#xff08;2023.2.17&#xff09;https://blog.csdn.net/shuyan1115/article/details/129088574?spm1001.2014.3001.5502CCNP350-401学习笔记&#xff08;51-100题&#xff09…...

喀秋莎Camtasia2023最新版本电脑录屏剪辑软件

录屏软件的鼻祖是techSmith 的喀秋莎&#xff08;Techsmith Camtasia Studio&#xff09;&#xff0c;视频编辑软件Camtasia 2023发布&#xff0c;十大新功能放出!作为一个自媒体人&#xff0c;每天都要录制编辑视频&#xff0c;选择一个好的视频编辑工具就是大家首先面临的一个…...

「考研算法」

考研算法 前言 本系列文章涉及的算法内容&#xff0c;针对的是哈尔滨工业大学854科目。在本文中通过具体的算法题进行讲解相应算法。 今天涉及的算法主要有线性筛&#xff0c;十大排序中快速排序和归并排序。 后续会有动态规划的相关算法以及尝试模型的总结&#xff0c;如果…...

Android Framework-操作系统基础

最近在看《深入理解Android内核设计思想&#xff08;第2版&#xff09;》&#xff0c;个人感觉很不错&#xff0c;内容很多&#xff0c;现将书里个人认为比较重要的内容摘录一下&#xff0c;方便后期随时翻看。 计算机体系结构 硬件是软件的基石&#xff0c;所有的软件功能最…...

美国最新调查显示 50% 企业已在用 ChatGPT,其中 48% 已让其代替员工,你怎么看?

美国企业开始使用ChatGPT&#xff0c;我认为这不是什么新闻。 如果美国的企业现在还不使用ChatGPT&#xff0c;那才是个大新闻。 据新闻源显示&#xff0c;已经使用chatGPT的企业中&#xff0c;48%已经让其代替员工工作。 ChatGPT的具体职责包括&#xff1a;客服、代码编写、招…...

[Java·算法·中等]LeetCode17. 电话号码的字母组合

每天一题&#xff0c;防止痴呆题目示例分析思路1题解1分析思路2题解2题目 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。…...

C#7/C#8/C#9 与dotnetSDK 以及dotnet framework对应关系

语言版本 对应的.net framework版本 对应的.net sdk版本 推荐使用的vs studio C#7.3 3.5、 4.0、 4.5 、4.5.1、 4.5.2 、4.6 、4.6.1、 4.6.2 4.7.1、 4.7.2 .netcore 2.0、.netcore2.1、 .netcore2.2 C#8.0 / F#4.7 不支持 .netcore 3.0、.netcore 3.1 C# 9.0 …...

jvm调优经验总结

最近一段时间很忙&#xff0c;忙到每天10点多11点下班还是感觉有很多事没有做完&#xff0c;不过倒也没有什么太过低落的情绪&#xff0c;有时候只安静的看一个视频&#xff0c;简单看点文字&#xff0c;或者平静的坐着&#xff0c;并没有太多想法。短时间的工作压力是可以接受…...

网赌网站做流量渗透/微信小程序开发多少钱

AutoJs一键解密是一款电脑上的AutoJs脚本文件解密工具&#xff0c;有了它&#xff0c;你可以对任何一个AutoJs脚本进行一键解密操作。具体使用情况请有需要的朋友前来西西下载&#xff01;软件初衷最近论坛用AutoJS编写的脚本程序越来越多&#xff0c;每次审核都要拖进手机才能…...

90设计网站真实稿费/如何做免费网站推广

在真正的生产大数据环境中&#xff0c;存储管理是DBA的主要工作之一&#xff0c;比如生产系统上表空间的监控&#xff0c;当一个表空间满或者使用率过高时&#xff0c;可能需要DBA添加一块磁盘到操作系统中&#xff0c;然后再在新加的磁盘上添加数据文件。如果这个操作作用的环…...

广州建设教育网站/百度服务电话6988

1,assert宠 包含文件&#xff1a;<assert.h> 原型&#xff1a;void assert(int expression) ; 如果expression为假(0),向stderr打印一条出错信息,然后调用函数abort 来终止程序. 在开发阶段通过在<assert.h>的包含语句前插入#define NDEBUG来禁用asset. #include &…...

网站设计与网页制作毕业论文/黄金网站app视频播放画质选择

现在数据库或是某个文件里有一些数据需要显示在界面上该怎么办呢&#xff1f;我们的目的就是将这些数据设法放到界面上去可以显示出来&#xff0c;这个将数据放到界面上去的过程可以叫住数据绑定。在Flex应用中程序中&#xff0c;数据绑定的实际是借助事件机制来完成的&#xf…...

成都网站建设推荐q479185700顶上/app开发需要多少钱

iOS/Android DevCamp&#xff1a;移动开发者大本营 即将来袭 2012-06-18 12:20|54次阅读|来源&#xff1a;CSDN【已有0条评论】发表评论 关键词&#xff1a;iOS/Android,DevCamp|作者&#xff1a;张宁|收藏这篇资讯 每年的WWDC都得到了移动应用开发业界的重点关注&#xff0c;今…...

南昌有做网站的吗/中央电视台一套广告价目表

1.配置jenkins环境 命令参考前一篇《docker自行构建jenkin》。单机部署即可。本实例中。jenkins&#xff0c;ansible安装在同一机器 。 jenkins需要配置/etc/sudoers.还需要给/bin/bash&#xff0c;以及jenkins的home目录下的.ssh下需要复制一份ansible的ssh私钥 1.配置/etc/su…...