springboot集成kafka
目录
1、kafka的消费者是pull(拉)还是push(推)模式,这种模式有什么好处?
kafka 如何不消费重复数据?比如扣款,我们不能重复的扣。
6、kafka 分布式(不是单机)的情况下,如何保证消息的顺序消费?
10、partition的数据文件(offffset,MessageSize,data)
11、kafka如何实现数据的高效读取?(顺序读写、分段命令、二分查找)
12、 Kafka 消费者端的 Rebalance 操作什么时候发生?
13、Kafka 中的ISR(InSyncRepli)、OSR(OutSyncRepli)、AR(AllRepli)代表什么?
1、kafka的消费者是pull(拉)还是push(推)模式,这种模式有什么好处?
producer 将消息推送到 broker,consumer 从broker 拉取消息。
一些消息系统比如 Scribe 和 Apache Flume 采用了 push 模式,将消息推送到下游的 consumer。这样做有好处也有坏处:由 broker 决定消息推送的速率,对于不同消费速率的 consumer 就不太好处理了。消息系统都致力于让 consumer 以最大的速率最快速的消费消息,但不幸的是,push 模式下,当 broker 推送的速率远大于 consumer 消费的速率时,consumer 恐怕就要崩溃了。最终 Kafka 还是选取了传统的 pull 模式。
优点: Pull 模式的另外一个好处是 consumer 可以自主决定是否批量的从 broker 拉取数据。Push 模式必须在不知道下游 consumer 消费能力和消费策略的情况下决定是立即推送每条消息还是缓存之后批量推送。如果为了避免 consumer 崩溃而采用较低的推送速率,将可能导致一次只推送较少的消息而造成浪费。Pull 模式下,consumer 就可以根据自己的消费能力去决定这些策略。
缺点: 如果 broker 没有可供消费的消息,将导致 consumer 不断在循环中轮询,直到新消息到达。为了避免这点,Kafka 有个参数可以让 consumer阻塞知道新消息到达(当然也可以阻塞知道消息的数量达到某个特定的量这样就可以批量发送)。
Kafka高性能原因
1. 分区
生产者往Kafka发送消息时必须指定发往哪个主题,消费者需要订阅某个主题才能进行消费。一个主题下的分区可以分布在集群的不同broker上面,也就是说,一个主题可以横跨多个broker。这样的话,生产者在指定主题(可以指定也可以不指定分区)发送消息的时候,Kafka会将消息分发至不同的分区,如果这些分区不在同一个broker上,就相当于并发的写入多台broker,性能自然要比写入单台broker要高。对于消费者,Kafka引入了消费组(Consumer Group)的概念,每个消费者都有一个对应的消费组。一个分区只能被一个消费组中的一个消费者消费,但是可以被不同消费组中的另一个消费者消费。可以在一个消费组里起多个消费者,每个消费者消费一个分区,这样就提高了消费者的性能。需要注意的是,消费组里的消费者个数如果多于分区数的话,那些多出来的消费者就会处于空闲状态,所以一个消费组里的消费者个数跟分区数相等就好了。下图展示了消费者组与分区的关系。
分区的设计使得Kafka消息的读写性能可以突破单台broker的I/O性能瓶颈,可以在创建主题的时候指定分区数,也可以在主题创建完成之后去修改分区数,通过增加分区数可以实现水平扩展,但是要注意,分区数也不是越多越好,一般达到某一个阈值之后,再增加分区数性能反而会下降,具体阈值需要对Kafka集群进行压测才能确定。
2. 日志分段存储
为了防止日志(Log)过大,Kafka引入了日志分段(LogSegment)的概念,将日志切分成多个日志分段。在磁盘上,日志是一个目录,每个日志分段对应于日志目录下的日志文件、偏移量索引文件、时间戳索引文件(可能还有其他文件)。
向日志中追加消息是顺序写入的,只有最后一个日志分段才能执行写入操作,之前所有的日志分段都不能写入数据。
为了便于检索,每个日志分段都有两个索引文件:偏移量索引文件和时间戳索引文件。每个日志分段都有一个基准偏移量baseOffset,用来表示当前日志分段中第一条消息的offset。偏移量索引文件和时间戳索引文件是以稀疏索引的方式构造的,偏移量索引文件中的偏移量和时间戳索引文件中的时间戳都是严格单调递增的。查询指定偏移量(或时间戳)时,使用二分查找快速定位到偏移量(或时间戳)的位置。可见Kafka中对消息的查找速度还是非常快的。
3. 消息顺序追加
Kafka是通过文件追加的方式来写入消息的,只能在日志文件的最后追加新的消息,并且不允许修改已经写入的消息,这种方式就是顺序写磁盘,而顺序写磁盘的速度是非常快的。
4. 页缓存
页缓存是操作系统实现的一种主要的磁盘缓存,以此用来减少对磁盘I/O的操作。具体来说,就是把磁盘中的数据缓存到内存中,把对磁盘的访问变为对内存的访问。
Kafka中大量使用了页缓存,消息都是先被写入页缓存,再由操作系统负责具体的刷盘任务(Kafka中也提供了同步刷盘和间断性强制算盘的功能)。
5. 零拷贝
零拷贝技术是一种避免CPU将数据从一块存储拷贝到另一块存储的技术。Kafka使用零拷贝技术将数据直接从磁盘复制到网卡设备缓冲区中,而不需要经过应用程序的转发。
通常应用程序将磁盘上的数据传送至网卡需要经过4步:
1. 调用read(),将数据从磁盘复制到内核模式的缓冲区;
2. CPU会将数据从内核模式复制到用户模式下的缓冲区;
3. 调用write(),将数据从用户模式下复制到内核模式下的Socket缓冲区;
4. 将数据从内核模式的Socket缓冲区复制到网卡设备。
Kafka 如何保证消息可靠性
1 生产者发送消息丢失
kafka支持3种方式发送消息,这也是常规的3种方式,发送后不管结果、同步发送、异步发送,基本上所有的消息队列都是这样玩的。
发送并忘记,直接调用发送send方法,不管结果,虽然可以开启自动重试,但是肯定会有消息丢失的可能同步发送,同步发送返回Future对象,我们可以知道发送结果,然后进行处理
异步发送,发送消息,同时指定一个回调函数,根据结果进行相应的处理
为了保险起见,一般我们都会使用异步发送带有回调的方式进行发送消息,再设置参数为发送消息失败不停地重试。
acks=all,这个参数有可以配置0|1|all。
0表示生产者写入消息不管服务器的响应,可能消息还在网络缓冲区,服务器根本没有收到消息,当然会丢失消息。
1表示至少有一个副本收到消息才认为成功,一个副本那肯定就是集群的Leader副本了,但是如果刚好Leader副本所在的节点挂了,Follower没有同步这条消息,消息仍然丢失了。
配置all的话表示所有ISR都写入成功才算成功,那除非所有ISR里的副本全挂了,消息才会丢失。
retries=N,设置一个非常大的值,可以让生产者发送消息失败后不停重试
2 kafka broker 自身消息丢失
kafka因为消息写入是通过PageCache异步写入磁盘的,因此仍然存在丢失消息的可能。
因此针对kafka自身丢失的可能设置参数:
replication.factor=N,设置一个比较大的值,保证至少有2个或者以上的副本。
min.insync.replicas=N,代表消息如何才能被认为是写入成功,设置大于1的数,保证至少写入1个或者以上的副本才算写入消息成功。
unclean.leader.election.enable=false,这个设置意味着没有完全同步的分区副本不能成为Leader副本,如果是true的话,那些没有完全同步Leader的副本成为Leader之后,就会有消息丢失的风险。
3 消费者消息丢失
消费者丢失的可能就比较简单,关闭自动提交位移即可,改为业务处理成功手动提交。
因为重平衡发生的时候,消费者会去读取上一次提交的偏移量,自动提交默认是每5秒一次,这会导致重复消费或者丢失消息。
enable.auto.commit=false,设置为手动提交。
还有一个参数我们可能也需要考虑进去的:
auto.offset.reset=earliest,这个参数代表没有偏移量可以提交或者broker上不存在偏移量的时候,消费者如何处理。earliest代表从分区的开始位置读取,可能会重复读取消息,但是不会丢失,消费方一般我们肯定要自己保证幂等,另外一种latest表示从分区末尾读取,那就会有概率丢失消息。