kafka实战

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

Kafka比传统的消息系统有着更强的顺序保证。一个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提供了以下的保证:发送到一个Topic中的message会按照发送的顺序添加到commit log中。意思是,如果消息 M1,M2由同一个producer发送,M1比M2发送的早的话,那么在commit log中,M1的offset就会比commit 2的offset小。一个consumer在commit log中可以按照发送顺序来消费message。如果一个topic的备份因子设置为N,那么Kafka可以容忍N-1个服务器的失败,而存储在commit log中的消息不会丢失。

kafka集群中的controller选举机制

在Kafka集群中会有一个或者多个broker,其中有一个broker会被选举为控制器(Kafka Controller),它负责管理整个集群中所有分区和副本的状态。
在kafka集群启动的时候,会自动选举一台broker作为controller来管理整个集群,选举的过程是集群中每个broker都会尝试在zookeeper上创建一个/controller 临时节点,zookeeper会保证有且仅有一个broker能创建成功,这个broker就会成为集群的总控器controller。 当这个controller角色的broker宕机了,此时zookeeper临时节点会消失,集群里其他broker会一直监听这个临时节点,发现临时节点消失了,就竞争再次创建临时节点,就是我们上面说的选举机制,zookeeper又会保证有一个broker成为新的controller。

HW和LED

HW俗称高水位,HighWatermark的缩写,取一个partition对应的ISR中最小的LEO(log-end-offset)作为HW,consumer最多只能消费到HW所在的位置。另外每个replica都有HW,leader和follower各自负责更新自己的HW的状态。对于leader新写入的消息,consumer不能立刻消费,leader会等待该消息被所有ISR中的replicas同步后更新HW,此时消息才能被consumer消费。这样就保证了如果leader所在的broker失效,该消息仍然可以从新选举的leader中获取。对于来自内部broker的读取请求,没有HW的限制。

消费丢失情况

1)acks=0: 表示producer不需要等待任何broker确认收到消息的回复,就可以继续发送下一条消息。性能最高,但是最容易丢消息。大数据统计报表场景,对性能要求很高,对数据丢失不敏感的情况可以用这种。(2)acks=1: 至少要等待leader已经成功将数据写入本地log,但是不需要等待所有follower是否成功写入。就可以继续发送下一条消息。这种情况下,如果follower没有成功备份数据,而此时leader又挂掉,则消息会丢失。(3)acks=-1或all: 这意味着leader需要等待所有备份(min.insync.replicas配置的备份个数)都成功写入日志,这种策略会保证只要有一个备份存活就不会丢失数据。这是最强的数据保证。一般除非是金融级别,或跟钱打交道的场景才会使用这种配置。当然如果min.insync.replicas配置的是1则也可能丢消息,跟acks=1情况类似。
在这里插入图片描述

消息重复消费

**消息发送端:**发送消息如果配置了重试机制,比如网络抖动时间过长导致发送端发送超时,实际broker可能已经接收到消息,但发送方会重新发送消息。
**消息消费端:**如果消费这边配置的是自动提交,刚拉取了一批数据处理了一部分,但还没来得及提交,服务挂了,下次重启又会拉取相同的一批数据重复处理。
一般消费端都是要做消费幂等处理的。

消息乱序

如果发送端配置了重试机制,kafka不会等之前那条消息完全发送成功才去发送下一条消息,这样可能会出现,发送了1,2,3条消息,第一条超时了,后面两条发送成功,再重试发送第1条消息,这时消息在broker端的顺序就是2,3,1了所以,是否一定要配置重试要根据业务情况而定。也可以用同步发送的模式去发消息,当然acks不能设置为0,这样也能保证消息从发送端到消费端全链路有序。

消息积压

1)线上有时因为发送方发送消息速度过快,或者消费方处理消息过慢,可能会导致broker积压大量未消费消息。此种情况如果积压了上百万未消费消息需要紧急处理,可以修改消费端程序,让其将收到的消息快速转发到其他topic(可以设置很多分区),然后再启动多个消费者同时消费新主题的不同分区。
2)由于消息数据格式变动或消费者程序有bug,导致消费者一直消费不成功,也可能导致broker积压大量未消费消息。此种情况可以将这些消费不成功的消息转发到其它队列里去(类似死信队列),后面再慢慢分析死信队列里的消息处理问题。

延时队列

延时队列存储的对象是延时消息。所谓的“延时消息”是指消息被发送以后,并不想让消费者立刻获取,而是等待特定的时间后,消费者才能获取这个消息进行消费,延时队列的使用场景有很多, 比如 :
1)在订单系统中, 一个用户下单之后通常有 30 分钟的时间进行支付,如果 30 分钟之内没有支付成功,那么这个订单将进行异常处理,这时就可以使用延时队列来处理这些订单了。
2)订单完成1小时后通知用户进行评价。
实现思路:发送延时消息时先把消息按照不同的延迟时间段发送到指定的队列中(topic_1s,topic_5s,topic_10s,…topic_2h,这个一般不能支持任意时间段的延时),然后通过定时器进行轮训消费这些topic,查看消息是否到期,如果到期就把这个消息发送到具体业务处理的topic中,队列中消息越靠前的到期时间越早,具体来说就是定时器在一次消费过程中,对消息的发送时间做判断,看下是否延迟到对应时间了,如果到了就转发,如果还没到这一次定时任务就可以提前结束了。

消息回溯

如果某段时间对已消费消息计算的结果觉得有问题,可能是由于程序bug导致的计算错误,当程序bug修复后,这时可能需要对之前已消费的消息重新消费,可以指定从多久之前的消息回溯消费,这种可以用consumer的offsetsForTimes、seek等方法指定从某个offset偏移的消息开始消费,参见上节课的内容。

kafka为什么快

1、文件顺序读写
2、kafka写入数据时,采用零拷贝技术,减少没必要的拷贝,提升拷贝性能(kafka的零拷贝技术是通过发送一个文件描述符来完成)
3、kafka有很多批量处理,压缩优化
4、支持流数据stream

kafka零拷贝技术

kafka是采用mmap+write()的方法实现零拷贝技术,用mmap替代传统的read,这样就减少了一次cpu的数据拷贝过程,因为他是通过地址映射完成的。mmap有一个计算方式:relativeOffset = offset+baseOffset。同时这个mmap file使用的是虚拟内存,这样就不会占用物理内存。所以,mmap读取数据快(也是因为他建立了pageCache到用户虚拟地址的映射,从而就避免了把数据拷贝到用户空间的cpu的这个过程)。此时还是4次上下文切换,此时kafka使用sendfile实现两次上下文切换,进一步提升性能。
linux2.4版本之后,对sendfile()函数进行了改进,在DMA拷贝数据的过程中,引入了gather操作。kafka通过sendfile函数实现零拷贝的过程:首先用户通过发起sendfile函数,系统调用用户态会切换为内核态(PageCache),之后DMA发起io请求,把硬件的数据通过DMA拷贝到pageCache上去, 然后把内存地址和地址偏移量这些信息从pageCache拷贝到Socket缓冲区(传统方式是cpu拷贝,直接把数据拷贝过去),紧接着DMA控制器会利用gather copy把pageCache的数据打包发送到网卡设备上。kafka底层代码调用的是destChannel.transferFrom(channel,position,count)函数,从而实现我们消费者消费的时候实现零拷贝技术。
另:有一个splice()+DMA copy技术也可以是实现零拷贝技术,splice()可以简单理解为一个管道,与sendfile不同的是在,它相当于在内核态到Socket缓冲区建立一个管道,一端是写绑定pageCace,一端是读绑定Socket缓冲区。PageCache将数据写到splice管道,然后Scoket缓冲区可以直接去读,然后再通过DMA拷贝到网卡设备上这样一个流程。

kafka时间轮

底层代码是在timingWheel是卡夫卡时间轮的入口类,
里面有5个入参:1、tickMs为表盘指针转动一次的时间,也是每个bucket的时间;wheelSize是每一层时间轮上的Bucket数量(默认是20);startMs时间轮对象被创建时的起始时间戳;taskCounter每层时间轮上定时任务的总数;query延时队列(例如:我要创建一个延时任务,他会在currentTime表盘上找到5毫秒这个位置创建一个TimerTaskList,而这个TimerTaskList可以包含多个延时任务(TimerTaskEntry),而query中可以包含多个这样的TimerTaskList。wheelSize为多少最多可以包含多少个)。
出了这些入参外,还有一些定义参数:比如总时长interval = tickMs * wheelSize。currentTime,可以理解为时间间隔的整倍数。buckets等。
TimerTaskList是一个双链表,双向的循环链表。还有设置过期时间,添加和获取过期时间expriation,除此之外还有添加、删除、清空、获取、比较定时任务的方法。
TimerTaskEntry中两个传参:定义定时任务和设置超时时间的参数expriation。它也可以设置删除定时任务的方法。
kafka时间轮里面还有一个addOverFlowWheel的方法可以允许创建第二层时间轮,此时的tickMs要求等于第一层时间轮的总时长的。类似于钟表的时分秒的道理。
里面有个问题,就是很多空格,就是在轮询的时候会出现很多空转这是在浪费资源,由此kafka后来引进一个Java的延时队列:DelayQueue。

各个中间件的区别差异

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值