Kafka

消息中间件的作用

  • 异步
  • 解耦
  • 削峰填谷
    在这里插入图片描述
    在这里插入图片描述
    MQ对比
    在这里插入图片描述

RabbitMQ
优点:管理界面最好用;支持语言较多;
缺点:不支持扩展(集群)
主要流程:生产者发送消息给交换机,通过路由机制投递到对应的队列中

RocketMQ
优点:在阿里有大规模应用
缺点:支持语言较少

Kafka
优点:天生分布式,性能好,支持多语言
缺点:运维难度大

Kafka启动

正常情况下端口为9092
linux的脚本上少一级目录
在这里插入图片描述
在这里插入图片描述

zookeeper-server-start.bat   ../../config/zookeeper.propertieskafka-server-start.bat  ../../config/server.properties

Kafka生产和消费的过程

在这里插入图片描述

Kafka中的核心概念

消息和批次

消息 ,Kafka里的数据单元,也就是一般消息中间件里的消息的概念(可以比作数据库中一条记录)。消息由字节数组组成。消息还可以包含键(可选元数据,也是字节数组),主要用于对消息选取分区。

作为一个高效的消息系统,为了提高效率,消息可以被分批写入Kafka。批次就是一组消息,这些消息属于同一个主题和分区。如果只传递单个消息,会导致大量的网络开销,把消息分成批次传输可以减少这开销。但是,这个需要权衡(时间延迟和吞吐量之间),批次里包含的消息越多,单位时间内处理的消息就越多,单个消息的传输时间就越长(吞吐量高延时也高)。如果进行压缩,可以提升数据的传输和存储能力,但需要更多的计算处理。

对于Kafka来说,消息是晦涩难懂的字节数组,一般使用序列化和反序列化技术,格式常用的有JSON和XML,还有Avro(Hadoop开发的一款序列化框架),具体怎么使用依据自身的业务来定。

主题和分区

Kafka里的消息用主题进行分类(主题好比数据库中的表),主题下有可以被分为若干个 分区(分表技术) 。分区本质上是个提交日志文件,有新消息,这个消息就会以追加的方式写入分区(写文件的形式),然后用先入先出的顺序读取。
分区(Partition)(可以理解为队列):用于将主题(Topic)中的数据划分成多个片段,以实现数据的水平扩展和并行处理。每个主题可以划分为一个或多个分区,每个分区在 Kafka 集群中由一组 Kafka 服务器(Broker)来管理
在这里插入图片描述
因为主题会有多个分区,所以在整个主题的范围内,是无法保证消息的顺序的,单个分区则可以保证。

Kafka通过分区来实现数据冗余和伸缩性,因为分区可以分布在不同的服务器上,那就是说一个主题可以跨越多个服务器(这是Kafka高性能的一个原因,多台服务器的磁盘读写性能比单台更高)。

分区怎么划分:消息的消费没有顺序的要求 则根据生产和消费能力;要做到完全的顺序消息,就设置一个分区

生产者和消费者、偏移量、消费者群组

一般消息中间件里生产者和消费者的概念。一些其他的高级客户端API,像数据管道API和流式处理的Kafka Stream,都是使用了最基本的生产者和消费者作为内部组件,然后提供了高级功能。

生产者默认情况下把消息均衡分布到主题的所有分区上,如果需要指定分区,则需要使用消息里的消息键和分区器。

消费者订阅主题,一个或者多个,并且按照消息的生成顺序读取。消费者通过检查所谓的偏移量来区分消息是否读取过。偏移量是一种元数据,一个不断递增的整数值,创建消息的时候,Kafka会把他加入消息。在一个主题中一个分区里,每个消息的偏移量是唯一的。每个分区最后读取的消息偏移量会保存到Zookeeper或者Kafka上,这样分区的消费者关闭或者重启,读取状态都不会丢失。(偏移量)

多个消费者可以构成一个消费者群组。怎么构成?共同读取一个主题的消费者们,就形成了一个群组。群组可以保证每个分区只被一个消费者使用。
在这里插入图片描述
消费者和分区之间的这种映射关系叫做消费者对分区的所有权关系,很明显,一个分区只有一个消费者,而一个消费者可以有多个分区。

(吃饭的故事:一桌一个分区,多桌多个分区,生产者不断生产消息(消费),消费者就是买单的人,消费者群组就是一群买单的人),一个分区只能被消费者群组中的一个消费者消费(不能重复消费),如果有一个消费者挂掉了<James跑路了>,另外的消费者接上)

Broker和集群

一个独立的Kafka服务器叫Broker。broker的主要工作是,接收生产者的消息,设置偏移量,提交消息到磁盘保存;为消费者提供服务,响应请求,返回消息。在合适的硬件上,单个broker可以处理上千个分区和每秒百万级的消息量。(要达到这个目的需要做操作系统调优和JVM调优)

多个broker可以组成一个集群。每个集群中broker会选举出一个集群控制器。控制器会进行管理,包括将分区分配给broker和监控broker。

集群里,一个分区从属于一个broker,这个broker被称为首领。但是分区可以被分配给多个broker,这个时候会发生分区复制。

集群中Kafka内部一般使用管道技术进行高效的复制。
在这里插入图片描述
分区复制带来的好处是,提供了消息冗余。一旦首领broker失效,其他broker可以接管领导权。当然相关的消费者和生产者都要重新连接到新的首领上。

保留消息

在一定期限内保留消息是Kafka的一个重要特性,Kafka broker默认的保留策略是:要么保留一段时间(7天),要么保留一定大小(比如1个G)。到了限制,旧消息过期并删除。但是每个主题可以根据业务需求配置自己的保留策略(开发时要注意,Kafka不像Mysql之类的永久存储)。

kafka基本的操作和管理

## 列出所有主题 
./kafka-topics.sh --bootstrap-server localhost:9092 --list## 列出所有主题的详细信息
./kafka-topics.sh --bootstrap-server localhost:9092 --describe## 创建主题主题名 my-topic ,1副本,8分区
./kafka-topics.sh --bootstrap-server localhost:9092  --create --topic my-topic --replication-factor 1 --partitions 8##增加分区,注意:分区无法被删除
./kafka-topics.sh --bootstrap-server localhost:9092 --alter --topic my-topic --partitions 16## 创建生产者(控制台)
./kafka-console-producer.sh --broker-list localhost:9092 --topic my-topic## 创建消费者(控制台)
./kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic my-topic --from-beginning --consumer.config ../config/consumer.properties##  kafka终止命令
./kafka-server-stop.sh 

Broker配置

常规配置

broker.id

在单机时无需修改,但在集群下部署时往往需要修改。它是个每一个broker在集群中的唯一表示,要求是正数。当该服务器的IP地址发生改变时,broker.id没有变化,则不会影响consumers的消息情况

listeners

监听列表(以逗号分隔 不同的协议(如plaintext,trace,ssl、不同的IP和端口)),hostname如果设置为0.0.0.0则绑定所有的网卡地址;如果hostname为空则绑定默认的网卡。如果没有配置则默认为java.net.InetAddress.getCanonicalHostName()。

如:PLAINTEXT://myhost:9092,TRACE://:9091或 PLAINTEXT://0.0.0.0:9092,

zookeeper.connect

zookeeper集群的地址,可以是多个,多个之间用逗号分割。(一组hostname:port/path列表,hostname是zk的机器名或IP、port是zk的端口、/path是可选zk的路径,如果不指定,默认使用根路径)

log.dirs

Kafka把所有的消息都保存在磁盘上,存放这些数据的目录通过log.dirs指定。可以使用多路径,使用逗号分隔。如果是多路径,Kafka会根据“最少使用”原则,把同一个分区的日志片段保存到同一路径下。会往拥有最少数据分区的路径新增分区。

num.recovery.threads.per.data.dir

每数据目录用于日志恢复启动和关闭时的线程数量。因为这些线程只是服务器启动(正常启动和崩溃后重启)和关闭时会用到。所以完全可以设置大量的线程来达到并行操作的目的。注意,这个参数指的是每个日志目录的线程数,比如本参数设置为8,而log.dirs设置为了三个路径,则总共会启动24个线程。

auto.create.topics.enable

是否允许自动创建主题。如果设为true,那么produce(生产者往主题写消息),consume(消费者从主题读消息)或者fetch
metadata(任意客户端向主题发送元数据请求时)一个不存在的主题时,就会自动创建。缺省为true。

delete.topic.enable=true

删除主题配置,默认未开启

主题配置

新建主题的默认参数

num.partitions

每个新建主题的分区个数(分区个数只能增加,不能减少 )。这个参数一般要评估,比如,每秒钟要写入和读取1000M数据,如果现在每个消费者每秒钟可以处理50MB的数据,那么需要20个分区,这样就可以让20个消费者同时读取这些分区,从而达到设计目标。(一般经验,把分区大小限制在25G之内比较理想)

log.retention.hours

日志保存时间,默认为7天(168小时)。超过这个时间会清理数据。bytes和minutes无论哪个先达到都会触发。与此类似还有log.retention.minutes和log.retention.ms,都设置的话,优先使用具有最小值的那个。(提示:时间保留数据是通过检查磁盘上日志片段文件的最后修改时间来实现的。也就是最后修改时间是指日志片段的关闭时间,也就是文件里最后一个消息的时间戳)

log.retention.bytes

topic每个分区的最大文件大小,一个topic的大小限制 = 分区数*log.retention.bytes。-1没有大小限制。log.retention.bytes和log.retention.minutes任意一个达到要求,都会执行删除。(注意如果是log.retention.bytes先达到了,则是删除多出来的部分数据),一般不推荐使用最大文件删除策略,而是推荐使用文件过期删除策略。

log.segment.bytes

分区的日志存放在某个目录下诸多文件中,这些文件将分区的日志切分成一段一段的,称为日志片段。这个属性就是每个文件的最大尺寸;当尺寸达到这个数值时,就会关闭当前文件,并创建新文件。被关闭的文件就开始等待过期。默认为1G。

如果一个主题每天只接受100MB的消息,那么根据默认设置,需要10天才能填满一个文件。而且因为日志片段在关闭之前,消息是不会过期的,所以如果log.retention.hours保持默认值的话,那么这个日志片段需要17天才过期。因为关闭日志片段需要10天,等待过期又需要7天。

log.segment.ms

作用和log.segment.bytes类似,只不过判断依据是时间。同样的,两个参数,以先到的为准。这个参数默认是不开启的。

message.max.bytes

表示一个服务器能够接收处理的消息的最大字节数,注意这个值producer和consumer必须设置一致,且不要大于fetch.message.max.bytes属性的值(消费者能读取的最大消息,这个值应该大于或等于message.max.bytes)。该值默认是1000000字节,大概900KB~1MB。如果启动压缩,判断压缩后的值。这个值的大小对性能影响很大,值越大,网络和IO的时间越长,还会增加磁盘写入的大小。

Kafka设计的初衷是迅速处理短小的消息,一般10K大小的消息吞吐性能最好(LinkedIn的kafka性能测试)

硬件配置对Kafka性能的影响

为Kafka选择合适的硬件更像是一门艺术,就跟它的名字一样,分别从磁盘、内存、网络和CPU上来分析,确定了这些关注点,就可以在预算范围之内选择最优的硬件配置。

磁盘吞吐量/磁盘容量

磁盘吞吐量(IOPS 每秒的读写次数)会影响生产者的性能。因为生产者的消息必须被提交到服务器保存,大多数的客户端都会一直等待,直到至少有一个服务器确认消息已经成功提交为止。也就是说,磁盘写入速度越快,生成消息的延迟就越低。(SSD固态贵单个速度快,HDD机械偏移可以多买几个,设置多个目录加快速度,具体情况具体分析)

磁盘容量的大小,则主要看需要保存的消息数量。如果每天收到1TB的数据,并保留7天,那么磁盘就需要7TB的数据。

内存

Kafka本身并不需要太大内存,内存则主要是影响消费者性能。在大多数业务情况下,消费者消费的数据一般会从内存(页面缓存,从系统内存中分)中获取,这比在磁盘上读取肯定要快的多。一般来说运行Kafka的JVM不需要太多的内存,剩余的系统内存可以作为页面缓存,或者用来缓存正在使用的日志片段,所以一般Kafka不会同其他的重要应用系统部署在一台服务器上,因为他们需要共享页面缓存,这个会降低Kafka消费者的性能。

网络

网络吞吐量决定了Kafka能够处理的最大数据流量。它和磁盘是制约Kafka拓展规模的主要因素。对于生产者、消费者写入数据和读取数据都要瓜分网络流量。同时做集群复制也非常消耗网络。

CPU

Kafka对cpu的要求不高,主要是用在对消息解压和压缩上。所以cpu的性能不是在使用Kafka的首要考虑因素。

总的来说,要为Kafka选择合适的硬件时,优先考虑存储,包括存储的大小,然后考虑生产者的性能(也就是磁盘的吞吐量),选好存储以后,再来选择CPU和内存就容易得多。网络的选择要根据业务上的情况来定,也是非常重要的一环。

Kafka生产与消费全流程

Kafka消息发送和消费的流程(非集群)

在这里插入图片描述

简单入门

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

producer类

必选属性

创建生产者对象时有三个属性必须指定。

bootstrap.servers

该属性指定broker的地址清单,地址的格式为host:port。

清单里不需要包含所有的broker地址,生产者会从给定的broker里查询其他broker的信息。不过最少提供2个broker的信息(用逗号分隔,比如:127.0.0.1:9092,192.168.0.13:9092),一旦其中一个宕机,生产者仍能连接到集群上。

key.serializer

生产者接口允许使用参数化类型,可以把Java对象作为键和值传broker,但是broker希望收到的消息的键和值都是字节数组,所以,必须提供将对象序列化成字节数组的序列化器。

key.serializer必须设置为实现org.apache.kafka.common.serialization.Serializer的接口类

Kafka的客户端默认提供了ByteArraySerializer,IntegerSerializer,StringSerializer,也可以实现自定义的序列化器。

三种发送方式

value.serializer

同 key.serializer。

发送并忘记
在这里插入图片描述
同步发送

/*** 类说明:发送消息--同步模式*/// 设置属性Properties properties = new Properties();// 指定连接的kafka服务器的地址properties.put("bootstrap.servers","127.0.0.1:9092");// 设置String的序列化properties.put("key.serializer", StringSerializer.class);properties.put("value.serializer", StringSerializer.class);// 构建kafka生产者对象KafkaProducer<String,String> producer  = new KafkaProducer<String, String>(properties);try {ProducerRecord<String,String> record;try {// 构建消息record = new ProducerRecord<String,String>("my-topic", "kafka-test","yuyang");// 发送消息Future<RecordMetadata> future =producer.send(record);RecordMetadata recordMetadata = future.get();if(null!=recordMetadata){System.out.println("offset:"+recordMetadata.offset()+","+"partition:"+recordMetadata.partition());}} catch (Exception e) {e.printStackTrace();}} finally {// 释放连接producer.close();}

异步发送

/*** 类说明:发送消息--异步模式*/// 设置属性Properties properties = new Properties();// 指定连接的kafka服务器的地址properties.put("bootstrap.servers","127.0.0.1:9092");// 设置String的序列化properties.put("key.serializer", StringSerializer.class);properties.put("value.serializer", StringSerializer.class);// 构建kafka生产者对象KafkaProducer<String,String> producer  = new KafkaProducer<String, String>(properties);try {ProducerRecord<String,String> record;try {// 构建消息record = new ProducerRecord<String,String>("msb", "teacher","lijin");// 发送消息producer.send(record, new Callback() {@Overridepublic void onCompletion(RecordMetadata recordMetadata, Exception e) {if (e == null){// 没有异常,输出信息到控制台System.out.println("offset:"+recordMetadata.offset()+"," +"partition:"+recordMetadata.partition());} else {// 出现异常打印e.printStackTrace();}}});} catch (Exception e) {e.printStackTrace();}} finally {// 释放连接producer.close();}

consumer类

必选属性

创建消费者对象时一般有四个属性必须指定。

bootstrap.servers、value.Deserializer key.Deserializer 含义同生产者

可选属性

group.id 并非完全必需,它指定了消费者属于哪一个群组,但是创建不属于任何一个群组的消费者并没有问题。不过绝大部分情况都会使用群组消费。

消费者群组

Kafka里消费者从属于消费者群组,一个群组里的消费者订阅的都是同一个主题,每个消费者接收主题一部分分区的消息。
在这里插入图片描述
主题T有4个分区,群组中只有一个消费者,则该消费者将收到主题T1全部4个分区的消息。

在这里插入图片描述
在群组中增加一个消费者2,那么每个消费者将分别从两个分区接收消息,上图中就表现为消费者1接收分区1和分区3的消息,消费者2接收分区2和分区4的消息。

在这里插入图片描述
群组中有4个消费者,那么每个消费者将分别从1个分区接收消息。
在这里插入图片描述
但是,当增加更多的消费者,超过了主题的分区数量,就会有一部分的消费者被闲置,不会接收到任何消息。

往消费者群组里增加消费者是进行横向伸缩能力的主要方式。所以有必要为主题设定合适规模的分区,在负载均衡的时候可以加入更多的消费者。但是要记住,一个群组里消费者数量超过了主题的分区数量,多出来的消费者是没有用处的。

消费者代码
在这里插入图片描述

自定义序列化,反序列化器

创建生产者对象必须指定序列化器,默认的序列化器并不能满足所有的场景。完全可以自定义序列化器。只要实现org.apache.kafka.common.serialization.Serializer接口即可。
在这里插入图片描述
自定义序列化

实体类
在这里插入图片描述
id的长度4个字节,字符串的长度描述4个字节, 字符串本身的长度nameSize个字节

序列化器

/*** @author* 自定义序列化器* @create 2023-08-04 12:20*/
public class UserSerializer implements Serializer<User> {public byte[] serialize(String topic, User data) {try {byte[] name;int nameSize;if(data==null){return null;}if(data.getName()!=null){name = data.getName().getBytes("UTF-8");//字符串的长度nameSize = data.getName().length();}else{name = new byte[0];nameSize = 0;}/*id的长度4个字节,字符串的长度描述4个字节,字符串本身的长度nameSize个字节*/ByteBuffer buffer = ByteBuffer.allocate(4+4+nameSize);buffer.putInt(data.getId());//4buffer.putInt(nameSize);//4buffer.put(name);//nameSizereturn buffer.array();} catch (Exception e) {throw new SerializationException("Error serialize User:"+e);}}}

自定义序列化容易导致程序的脆弱性。上面的实现里,有多种类型的消费者,每个消费者对实体字段都有各自的需求,比如,有的将字段变更为long型,有的会增加字段,这样会出现新旧消息的兼容性问题。特别是在系统升级的时候,经常会出现一部分系统升级,其余系统被迫跟着升级的情况。

解决这个问题,可以考虑使用自带格式描述以及语言无关的序列化框架。比如Protobuf,Kafka官方推荐的Apache Avro

分区

因为在Kafka中一个topic可以有多个partition,所以当一个生产发送消息,这条消息应该发送到哪个partition,这个过程就叫做分区。

在新建消息的时候,可以指定partition,只要指定partition,那么分区器的策略则失效。
在这里插入图片描述

系统分区器

DefaultPartitioner 默认分区策略

全路径类名:org.apache.kafka.clients.producer.internals.DefaultPartitioner

  • 如果消息中指定了分区,则使用它
  • 如果未指定分区但存在key,则根据序列化key使用murmur2哈希算法对分区数取模。
  • 如果不存在分区或key,则会使用粘性分区策略

采用默认分区的方式,键的主要用途有两个:

一,用来决定消息被写往主题的哪个分区,拥有相同键的消息将被写往同一个分区。

二,还可以作为消息的附加消息。

RoundRobinPartitioner 分区策略

全路径类名:org.apache.kafka.clients.producer.internals.RoundRobinPartitioner

  • 如果消息中指定了分区,则使用它
  • 将消息平均的分配到每个分区中。

即key为null,那么这个时候一般也会采用RoundRobinPartitioner

UniformStickyPartitioner 纯粹的粘性分区策略

全路径类名:org.apache.kafka.clients.producer.internals.UniformStickyPartitioner

他跟DefaultPartitioner 分区策略的唯一区别就是。

DefaultPartitionerd 如果有key的话,那么它是按照key来决定分区的,这个时候并不会使用粘性分区
UniformStickyPartitioner 是不管你有没有key, 统一都用粘性分区来分配

另外关于粘性分区策略

从客户端最新的版本上来看(3.3.1),有两个序列化器已经进入 弃用阶段。

这个客户端在3.1.0都还不是这样。关于粘性分区策略
如果感兴趣可以看下这篇文章

https://bbs.huaweicloud.com/blogs/348729?utm_source=oschina&utm_medium=bbs-ex&utm_campaign=other&utm_content=content

自定义分区器

/*** 类说明:自定义分区器,以value值进行分区*/
public class SelfPartitioner implements Partitioner {public int partition(String topic, Object key, byte[] keyBytes,Object value, byte[] valueBytes, Cluster cluster) {List<PartitionInfo> partitionInfos = cluster.partitionsForTopic(topic);int num = partitionInfos.size();int parId = Utils.toPositive(Utils.murmur2(valueBytes)) % num;//来自DefaultPartitioner的处理return parId;}}

生产缓冲机制

客户端发送消息给kafka服务器的时候、消息会先写入一个内存缓冲中,然后直到多条消息组成了一个Batch,才会一次网络通信把Batch发送过去。主要有以下参数:

buffer.memory

设置生产者内存缓冲区的大小,生产者用它缓冲要发送到服务器的消息。如果数据产生速度大于向broker发送的速度,导致生产者空间不足,producer会阻塞或者抛出异常。缺省33554432 (32M)

buffer.memory: 所有缓存消息的总体大小超过这个数值后,就会触发把消息发往服务器。此时会忽略batch.size和linger.ms的限制。
buffer.memory的默认数值是32 MB,对于单个 Producer 来说,可以保证足够的性能。 需要注意的是,如果您在同一个JVM中启动多个 Producer,那么每个 Producer 都有可能占用 32 MB缓存空间,此时便有可能触发 OOM。

batch.size

当多个消息被发送同一个分区时,生产者会把它们放在同一个批次里。该参数指定了一个批次可以使用的内存大小,按照字节数计算。当批次内存被填满后,批次里的所有消息会被发送出去。但是生产者不一定都会等到批次被填满才发送,半满甚至只包含一个消息的批次也有可能被发送(linger.ms控制)。缺省16384(16k) ,如果一条消息超过了批次的大小,会写不进去。

linger.ms

指定了生产者在发送批次前等待更多消息加入批次的时间。它和batch.size以先到者为先。也就是说,一旦获得消息的数量够batch.size的数量了,他将会立即发送而不顾这项设置,然而如果获得消息字节数比batch.size设置要小的多,需要“linger”特定的时间以获取更多的消息。这个设置默认为0,即没有延迟。设定linger.ms=5,例如,将会减少请求数目,但是同时会增加5ms的延迟,但也会提升消息的吞吐量。

为何要设计缓冲机制

1、减少IO的开销(单个 ->批次)但是这种情况基本上也只是linger.ms配置>0的情况下才会有,因为默认inger.ms=0的,所以基本上有消息进来了就发送了,跟单条发送是差不多!!

2、减少Kafka中Java客户端的GC

比如缓冲池大小是32MB。然后把32MB划分为N多个内存块,比如说一个内存块是16KB(batch.size),这样的话这个缓冲池里就会有很多的内存块。

你需要创建一个新的Batch,就从缓冲池里取一个16KB的内存块就可以了,然后这个Batch就不断的写入消息

下次别人再要构建一个Batch的时候,再次使用缓冲池里的内存块就好了。这样就可以利用有限的内存,对他不停的反复重复的利用。因为如果你的Batch使用完了以后是把内存块还回到缓冲池中去,那么就不涉及到垃圾回收了。
在这里插入图片描述

消费者偏移量提交

一般情况下,调用poll方法的时候,broker返回的是生产者写入Kafka同时kafka的消费者提交偏移量,这样可以确保消费者消息消费不丢失也不重复,所以一般情况下Kafka提供的原生的消费者是安全的,但是事情会这么完美吗?

自动提交

最简单的提交方式是让消费者自动提交偏移量。 如果enable.auto.commit被设为 true,消费者会自动把从poll()方法接收到的最大偏移量提交上去。提交时间间隔由auto.commit.interval.ms控制,默认值是5s。

自动提交是在轮询里进行的,消费者每次在进行轮询时会检査是否该提交偏移量了,如果是,那么就会提交从上一次轮询返回的偏移量。

不过,在使用这种简便的方式之前,需要知道它将会带来怎样的结果。

假设仍然使用默认的5s提交时间间隔, 在最近一次提交之后的3s发生了再均衡,再均衡之后,消费者从最后一次提交的偏移量位置开始读取消息。这个时候偏移量已经落后了3s,所以在这3s内到达的消息会被重复处理。可以通过修改提交时间间隔来更频繁地提交偏移量, 减小可能出现重复消息的时间窗, 不过这种情况是无法完全避免的。

在使用自动提交时,每次调用轮询方法都会把上一次调用返回的最大偏移量提交上去,它并不知道具体哪些消息已经被处理了,所以在再次调用之前最好确保所有当前调用返回的消息都已经处理完毕(enable.auto.comnit被设为 true时,在调用 close()方法之前也会进行自动提交)。一般情况下不会有什么问题,不过在处理异常或提前退出轮询时要格外小心。

消费者的配置参数

auto.offset.reset

earliest
当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,从头开始消费
latest
当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,消费新产生的该分区下的数据

只要group.Id不变,不管auto.offset.reset 设置成什么值,都从上一次的消费结束的地方开始消费。

消费者组群

kafka的群组消费—负载均衡建立在分区级别
一个分区不能同时给一个消费者群组中的多个消费者消费
在这里插入图片描述
RocketMQ顺序消费,消息取模

Kafka的消费全流程

多线程安全问题

当前系统线程核心数
Runtime.getRuntime().availableProcessors()

Kafka的Producer(生产者)是原始线程安全的
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/76046.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python入门【​编辑、组合、设计模式_工厂模式实现 、设计模式_单例模式实现、工厂和单例模式结合、异常是什么?异常的解决思路 】(十七)

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱敲代码的小王&#xff0c;CSDN博客博主,Python小白 &#x1f4d5;系列专栏&#xff1a;python入门到实战、Python爬虫开发、Python办公自动化、Python数据分析、Python前后端开发 &#x1f4e7;如果文章知识点有错误…

构造方法 与代理

构造方法 public class Son {String name;/*构造方法1.与类名相同2.没有返回值也没有void*//*作用&#xff1a;实例化初始值使用new关键字必须要有构造器*/public Son(){//无参构造方法this.name"sss";}//有参构造方法public Son(String name){//定义有参构造 就必…

【云存储】使用OSS快速搭建个人网盘教程(阿里云)

使用OSS快速搭建个人网盘 一、基础概要1. 主要的存储类型1.1 块存储1.2 文件存储1.3 对象存储 2. 对象存储OSS2.1 存储空间2.2 地域2.3 对象2.4 读写权限2.5 访问域名&#xff08;Endpoint&#xff09;2.6 访问密钥2.7 常用功能&#xff08;1&#xff09;创建存储空间&#xff…

WebGL Shader着色器GLSL语言

在2D绘图中的坐标系统&#xff0c;默认情况下是与窗口坐标系统相同&#xff0c;它以canvas的左上角为坐标原点&#xff0c;沿X轴向右为正值&#xff0c;沿Y轴向下为正值。其中canvas坐标的单位都是’px’。 WebGL使用的是正交右手坐标系&#xff0c;且每个方向都有可使用的值的…

MFC第二十六天 CRgn类简介与开发、封装CMemoryDC类并应用开发

文章目录 CRgn类简介与开发CRgn类简介CRgn类区域管理开发CRgn类区域管理与不规则形状的选取 封装CMemoryDC类并应用开发CMemoryDC.h封装CMemoryDC开发游戏透明动画CFlashDlg.hCFlashDlg.cpp 封装CMemoryDC开发游戏动画 附录四大窗口CDC派生类 CRgn类简介与开发 CRgn类简介 CR…

c高级:day3

作业: 1. 整理思维导图 2.判断家目录下,普通文件的个数和目录文件的个数 #!/bin/bash ######################################################################## # File Name: zy1.sh # Created Time: 2023年08月04日 星期五 19时13分08秒 ##############################…

计算机网络(2) --- 网络套接字UDP

计算机网络&#xff08;1&#xff09; --- 网络介绍_哈里沃克的博客-CSDN博客https://blog.csdn.net/m0_63488627/article/details/131967378?spm1001.2014.3001.5501 目录 1.端口号 2.TCP与UDP协议 1.TCP协议介绍 1.TCP协议 2.UDP协议 3.理解 2.网络字节序 发送逻辑…

html,css日常记录

动态绑定class&#xff0c;当index3!0时&#xff0c;class的值为bdt :class"{bdt:index3!0}" 动态绑定style中的height和lineheight&#xff0c;其中&#xff0c;height的值为函数px(item.length)的返回值&#xff0c;函数名可以随意取。 :style"{height:px(…

SpringBoot使用@Autowired将实现类注入到List或者Map集合中

前言 最近看到RuoYi-Vue-Plus翻译功能 Translation的翻译模块配置类TranslationConfig&#xff0c;其中有一个注入TranslationInterface翻译接口实现类的写法让我感到很新颖&#xff0c;但这种写法在Spring 3.0版本以后就已经支持注入List和Map&#xff0c;平时都没有注意到这…

基于 Redux + TypeScript 实现强类型检查和对 Json 的数据清理

基于 Redux TypeScript 实现强类型检查和对 Json 的数据清理 突然像是打通了任督二脉一样就用了 generics 搞定了之前一直用 any 实现的类型…… 关于 Redux 的部分&#xff0c;这里不多赘述&#xff0c;基本的实现都在这里&#xff1a;Redux Toolkit 调用 API 的四种方式 和…

机器学习概述及其主要算法

目录 1、什么是机器学习 2、数据集 2.1、结构 3、算法分类 4、算法简介 4.1、K-近邻算法 4.2、贝叶斯分类 4.3、决策树和随机森林 4.4、逻辑回归 4.5、神经网络 4.6、线性回归 4.7、岭回归 4.8、K-means 5、机器学习开发流程 6、学习框架 1、什么是机器学习 机器…

Emacs之set-face-attribute与font-lock-add-keywords用法区别(一百二十八)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

在java中如何使用openOffice进行格式转换,word,excel,ppt,pdf互相转换

1.首先需要下载并安装openOffice,下载地址为&#xff1a; Apache OpenOffice download | SourceForge.net 2.安装后&#xff0c;可以测试下是否可用&#xff1b; 3.build.gradle中引入依赖&#xff1a; implementation group: com.artofsolving, name: jodconverter, version:…

笔记:Android 9系统启动流程

启动流程 1.按下电源&#xff0c;系统启动 当电源键按下时&#xff0c;引导芯片代码&#xff08;汇编指令&#xff09;会从预定的地方&#xff08;固化在ROM&#xff09;开始执行&#xff0c;将引导程序 BootLoader 加载到 RAM中&#xff0c;然后执行 2.引导程序 BootLoader …

【3维视觉】3D空间常用算法(点到直线距离、面法线、二面角)

3D空间点到直线的距离 3D空间点到直线的距离 3D空间的曲率 三维空间有三个基本元素&#xff0c;点&#xff0c;线&#xff0c;面。那么曲率是如何定义的呢&#xff1f; 点的曲率&#xff1f; 线的曲率&#xff1f; 面的曲率&#xff1f; 法曲率 设曲面上的曲线在某一点处的切…

Unity 使用SharpZipLib解压时报错

报错信息&#xff1a; NotSupportedException: Encoding 936 data could not be found. Make sure you have correct international System.Text.Encoding.GetEncoding (System.Int32 codepage) ICSharpCode.SharpZipLib.Zip.ZipConstants.ConvertToString。 出现问题分析&…

【SpringBoot】日志是+基于lombok的日志输出

博主简介&#xff1a;想进大厂的打工人博主主页&#xff1a;xyk:所属专栏: JavaEE进阶 在我们日常的程序开发中&#xff0c;日志是程序的重要组成部分&#xff0c;想象⼀下&#xff0c;如果程序报错了&#xff0c;不让你打开控制台看⽇志&#xff0c;那么你能找到报错的原因吗…

leetcode37. 解数独(java)

解数独 解数独题目描述回溯算法代码演示 回溯算法 解数独 难度 困难 leetcode37. 解数独 题目描述 编写一个程序&#xff0c;通过填充空格来解决数独问题。 数独的解法需 遵循如下规则&#xff1a; 1.数字 1-9 在每一行只能出现一次。 2.数字 1-9 在每一列只能出现一次。 3.数字…

【C++】开源:ncurses终端TUI文本界面库

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍ncurses终端文本界面库。 无专精则不能成&#xff0c;无涉猎则不能通。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&#xff0c;下…

【2023年电赛国一必备】E题报告模板--可直接使用

创作不易&#xff0c;麻烦关注CSDN【技术交流、免费报告资料】 通过百度网盘分享的文件&#xff1a;https://pan.baidu.com/s/1aXzYwLMLx_b59abvplUiYw?pwddn71 提取码:dn71 复制这段内容打开「百度网盘APP 即可获取」 任务 图1 任务内容 要求 图2 基本要求内容 图3 发挥部…