Kafka是一种基于发布/订阅的分布式消息系统,它能够高效并实时的吞吐数据,以及通过副本冗余机制保证数据安全。
Kafka将消息以topic为单位进行归纳 将向Kafka topic发布消息的程序成为producers. 将预订topics并消费消息的程序成为consumer. Kafka以集群的方式运行,可以由一个或多个服务组成,每个服务叫做一个broker. producers通过网络将消息发送到Kafka集群,集群向消费者提供消息
①异步处理
②应用解耦
③流量削峰
④日志处理
⑤消息通讯等。
优点:
排队:一组用户从队列中读取数据,每条消息发送给指定某个人
发布-订阅:消息广播给所有用户,
①快速:单一的Kafka代理可以处理成千上万的客户端,每秒处理数兆字节的读写操作。
②可伸缩:在一组机器上对数据进行分区
③和简化,以支持更大的数据
④持久:消息是持久性的,并在集群中进
⑤行复制,以防止数据丢失。
⑥设计:它提供了容错保证和持久性
一旦Zookeeper停止工作,它就不能服务客户端请求。
zookeeper用于在集群中不同节点之间进行通信
在kafka中,zk用于提交偏移量,当任意节点失败,都可以从之前提交的偏移量出继续执行
除了提交偏移量,还进行leader检测,新节点状态检测,配置管理,分布式同步
它支持将字节从套接口转移到磁盘,通过内核空间保存副本,并在内核用户之间调用内核。
若用户位置与broker不同数据中心,则可能需要优化套接字缓冲区大小,以对应对长网络延迟
ISR是一组与leader完全同步的消息副本,也就是说ISR包含了所有提交的消息。
当一个副本从leader中脱离出来,就会从ISR中删除
确保任何已发布的消息不会丢失,在机器错误,程序错误时任然可用。
如果首选的副本不在ISR中,控制器将无法将leadership转移到首选的副本。
所有的kafka节点都不许告知:哪些节点是活动的,目标主题分区的leader在哪
consumer消费消息时,向broker发出fetch请求去消费特定分区消息,consumer指定消息在日志中的offset,就可以消费这个位置开始的消息
producer将消息推送到broker,consumer从broker拉取消息。
其他消息系统Flume就采用push模式,将消息推送到consumer,这样做可以由broker决定消息推送的速率,但对于不同消费速度的consumer就不好处理了,当推送速度远远大于consumer消费速度时,可能导致消费者奔溃
pull模式另一个好处就是 consumer可以根据自己的消费能力去决定是否批量拉取数据
pull缺点是,当broker没有消息时,consumer将不断进行轮询。为了避免这点,可通过参数让consumer阻塞直到新数据到达
消息由一个固定长度的头部 和 可变长度的字节数组组成
消息长度:4byte(1+4+n)
版本号:1byte
CRC效验码:4bytes
具体消息:nbytes
在启动kafka集群之前,我们通过配置log.dirs参数指定数据存放目录,这个参数可配置多个目录,用逗号分割开。通常这些目录分布在同步磁盘上用于提高读写性能。
当我们配置多个目录,kafka创建的分区会在哪个目录创建分区目录呢??kafka会在含有分区目录最少的文件夹中创建新得分区目录,分区目录名为topic名+分区ID,
主题中的多个分区文件以文件夹的形式保存到broker中,每个分区序号从0递增,且消息有序
partition文件夹下有多个segment文件(xxx.index,xxx.log)
segment文件默认为1G,如果segment文件大于1G时,会生成一个新得segment文件,文件名为上一个segment最后一条消息的偏移量命名
索引文件(n,length):表示第n个message
a) 通过offset二分查找文件列表定位到segment文件
b) 通过index元数据指针,查找log文件对象对应的message
request.required.acks有三个值 0 1 -1
0:生产者不会等待broker的ack,延迟最低,但存储保证最弱
1:服务端等待分区leader确认接收消息后发送ack,但当leader挂掉其他flower每复制数据也会丢失数据
-1:服务端等待分区所有副本确认接受消息后发送ack,保证不会丢失数据,但延迟最大
消费者每次消费消费数据时,都会记录消费的offset偏移量位置,等待下次消费时,接着上次位置继续消费
主题中的一个分区对应一个消费者成员,增加消费者组的消费者可以增加消费能力,但当超出了分区数目时,消费者会出现空闲情况
一个消费者组内的消息是有序的,消费者组之间是无需的
我们的项目本身的业务体量很小,所以直接单机一把都能都能搞定了,但是后面业务体量不断扩大,采用微服务的设计思想,分布式的部署方式,所以拆分了很多的服务,随着体量的增加以及业务场景越来越复杂了,很多场景单机的技术栈和中间件以及不够用了,而且对系统的友好性也下降了,最后做了很多技术选型的工作,我们决定引入消息队列中间件。
一说到消息队列你脑子就要想到异步、削峰、解耦,条件反射那种。
我们之前的场景很多步骤都是在一个流程里面做完的,就比如我们的订单系统吧,本来我们的业务很简单下了单,付了钱,扣减了库存流程就走完了。
但是后面联通那边来了需求,说要融合抢鲜购和沃门店两套系统,将沃门店的优惠券系统和积分系统加到抢鲜购中,好么,加了个优惠券系统,流程里多了100ms去扣减优惠券,加了个积分系统,流程里多了200ms去增减积分,最后联通那边还要求,我们下单成功后要给用户发短信,然后又多了100ms。
老东家要求所有接口的Rt(response time)在200ms内,超出的全部优化。
敖丙负责的系统QPS9w+,Rt要求在50ms内。
通过异步来实现,用户支付完成后,立马给用户返回下单成功。
为啥不用多线程去做:
联通那边提需求是一会一个变,难道每次新调用一个接口都要重新发布系统,重新在原系统做修改么,这就违反了开闭原则,这就是很典型的系统耦合问题了
当用户发起下单请求时,主流程只经过订单系统,支付系统流程就结束了,然后将支付成功消息发送出去,而后面不管接入什么系统,只要订阅相应的支付成功消息完成相应的逻辑就好了。
这其实是用了消息队列的一个缺点,涉及到分布式事务的知识点
当遇到秒杀活动时,如果流量直接到数据库时,会将数据库打挂的
那怎么办,将下单请求放到队列里面,每秒钟消费多少请求就看自己的消费能力了
技术是把双刃剑,虽然带来了很多好处,但使用之后的问题也是接踵而至。
重复消费,消息丢失,顺序消费,分布式事务等问题。。。
目前在市面上比较主流的消息队列中间件主要有,Kafka、ActiveMQ、RabbitMQ、RocketMQ 等这几种
ActiveMQ和RabbitMQ这两着因为吞吐量还有GitHub的社区活跃度的原因,在各大互联网公司都已经基本上绝迹了
越来越多的公司更青睐RocketMQ这样的消息中间件了
Kafka和RocketMQ一直在各自擅长的领域发光发亮,吞吐量都在10万级。
https://blog.csdn.net/qq_35190492/article/details/103232854?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task
重复消费是个比较严重和常见的问题,但凡用到消息队列,我第一时间就考虑是否重复消费。
在公司做活动模块时,遇到需要给一个活动页面加GMV(销售总额),最后根据它的GMV在什么区间去给他发奖励,这是电商活动常见玩法。
首先电商活动100%都是异步去加的,不然你想,一个用户下一单就操作表加一下金额,在高并发的场景下,数据库根本顶不住。而且用户在下单看一些活动页面,有时候马上就有了,有时候延迟很久,这个速度就取决于消息队列的消费速度。
下个单支付成功就将这个消息发出去,活动开发人员就监听你的支付成功消息,然后再活动GMV表里给你加上去,最终发生重复消费问题。一般消息队列都会有重试机制,就是说如果业务发生异常则会抛出异常要求你重新发一次,当然网络抖动啊,程序bug啊,数据问题啊都可能进行重试。但监听这个订单支付成功消息除了活动模块之外,还有库存模块,优惠券模块,积分模块。如果积分模块处理失败请求重试但此时活动GMV已经将销售额加上了,就会导致重复累加销售额的问题。
我们是通过接口幂进行处理的,接口幂的意思就是说无论调用这个接口多少次,返回的结果都是一样的。
一般幂等接口的实现,我会分场景去考虑,看是强效验还是弱效验。如果和钱相关的场景就做强效盐,否则做弱效验。
key=订单号+场景
用流水表做强效验,是因为涉及到金钱这样的活动,有啥问题可以很方便的通过流水表进行对账,而且还可以帮助开发人员定位问题。
大致处理流程是:下单发支付成功消息,活动模块判断这个订单我处理过没,如果处理过就return返回,如果没处理过,则累加销售额和流水,这两个操作在同一个事务中,要成功都成功。
通过redis保存做弱效验,将key放到redis中,在一定时间内在redis中进行判断 发短信之类
什么是事务呢??事务的4个属性就是ACID
什么是分布式事务呢??比如下单流程可能涉及到10多个环节,下单支付成功了,但优惠券扣减失败了,积分新增失败了,分布式事务就是保证这些在不同服务的操作具有ACID
没有最完美的系统,只有最适合的系统。
2pc提交,Zab协议,最终一致性
分布式事务有很多弊端:长时间锁定数据库资源,并发上不去