消息队列——kafka

topic:文件上传,短信,截图,流程图,流程文件,索引文件到es。3个broken,5个partition,3个副本

引入mq的缺陷:

1.可用性降低,复杂性

2.消息重复消费:幂等性,redis天然幂等性,生成唯一id,先去redis里面查,没有就消费,kafka 设置enable.idempotence为True,生成PID,PID大于值的才被接收

3.消息遗失:producer 设置ack为-1,发送到leader并同步到follower才算发消息成功。消费者设置autoAck为false,手动提交offset,防止消费异常而消息遗失,异步和同步

4.顺序:kafka保证partition中顺序,每个消费者消费一个partition,搞多个内存队列,一个内存队列对应一个线程消费

5.一致性,使用transactionalId将消费消息、生产消息、提交消费位移当作原子操作

kafka 中 consumer使用pull模式从服务端拉消息,设置autoack为false,消费者消费完后传回offset,保存在kafka中,宕机后根据保存的offset拉取消息进行消费。partition所有副本统称为AR,副本位于不同的broken以便故障转移,所有与leader保持一定程度同步的副本包括leader组成ISR,ISR是AR的子集,滞后的副本组成OSR,AR=ISR+OSR,正常情况下OSR为空

kafka producer生产者客户端

producer由主线程和sender线程协调运行,主线程中由kafkaProducer创建消息->拦截器->序列化器->分区器 作用后缓存到消息累加器(RecordAccumulator)中,然后Sender线程从中获取消息并发送到kafka。R主线程发送过来的消息会被追加到RecordAccumulator的双端队列中,队列中的内容是ProducerBatch(包含一个或多个ProducerRecord)请求从Sender线程发往Kafka之前还会保存到InFlightRequests中,主要是缓存已经发出但还没有收到响应请求的,从中可以获取leastLoadedNode,未确认的请求越多,负载越大。

producer ack参数:

ack=“1”,leader收到消息确认成功,ack=“0”,发出去就不管了,ack=“all”或者ack=“-1”,所有follower接收到消息才算成功

消费者客户端:

一个消费者可以订阅一个主题或多个,可以用集合也可以用正则表达式。需要指定一个消费者组

kafka消费是基于拉模式的,重复调用poll方法返回所订阅的主题(分区)一组消息ConsumerRecords,提供一个records(TopicPartition)来获取特定分区的消息

消费者在消费完消息之后需要执行消费位移的提交,offset值为x+1。在kafka中默认的消费位移是自动提交,每隔5秒会将拉取到的分区中最大的消息进行提交。客户端可设置enable.auto.commit为false开启手动提交包含commitSync和commitAsync两种类型的方法,Async有OffsetCommitCallback方法

 

再均衡:指分区的所属权从一个消费者转移到另外一个消费者,期间无法获取消息

消费段的多线程实现:

1.每个线程实例化一个KafkaConsumer

2.处理消息模块改成多线程,比第一种少了TCP连接,但顺序难控制,引入一个共享变量来参与提交Map<TopicPartion,Offset> offerts,加锁并发处理。

日志文件:

如果分区设置得合理,那么所有的消息可以均匀的分布到不同的partition中,这样可以实现水平扩展,一个分区对应一个log,一个log包含多个LogSegment,一个logsegment对应磁盘上的一个日志文件和两个索引文件(offset_.log,offset_.index,offset_.timeindex)

向Log文件中追加消息是顺序的,只有最后一个logsegement才能写入消息。以下情况会创建新的logsegement

1.当logsegement超过log.segement.bytes配置的值,即默认1G;

2.偏移量索引文件或时间戳文件的大小达到log.index.size.max.bytes即默认10MG;

3.当前分段消息的最大时间戳与系统当前的时间戳差值默认大于7天

消息压缩:

一般情况下,生产者发送的压缩数据再broken中是保持压缩状态的,消费者获取的也是压缩信息,处理消息时才会解压消息,采用参数compression.type配置压缩方式,默认为producer,保留生产者使用的压缩方式,压缩率越小,压缩效果越好,越占内存

偏移量索引文件,offset.index:

建立消息偏移量offset到物理地址之间的映射关系,索引文件以稀疏索引方式构造,不保证每个消息都有对应的索引项,使用二分查找法定位偏移量位置,如果指定偏移量不在索引文件中,则会返回小于指定偏移量的最大偏移量,然后到对应的log中找到偏移量后顺序查找

偏移量索引项:4B为relativeOffset,4B为position物理地址

kafka高性能的原因:

存储介质的速度:磁带>磁盘>主存>操作系统缓存>寄存器

1.kafka采用顺序写入磁盘的页缓存,比随机写内存速度快。对于一个进程而言,它会在进程内部缓存处理所需的数据,然而这些数据可能存在于操作系统的页缓存,一份数据被缓存两次,除非使用Direct I/O的方式,否则页缓存很难被禁止。

磁盘IO流程:应用程序buffer->C库标注IObuffer->文件系统页缓存->文件系统到磁盘;
写操作:调用fwrite写入C库标准IObuffer就返回,异步操作,写入C库标准IObuffer后不会立即刷新到磁盘会将多次小数据合并,最终调用write函数一次写入缓存页,数据达到缓存后不会立即刷新到磁盘,然后有pdflush线程检测脏页写回磁盘

读操作:用户调用fread到C库标准IObuffer中读取数据,如果成功则返回,否则继续;到页缓存中读取数据,如果成功则返回,否则继续;发起I/O请求,读取数据后缓存buffer和C库标准IObuffer并返回,可以看出,读操作时同步请求

2.零拷贝:将数据直接从磁盘文件复制到网卡设备中,而不需要经由应用程序之手,减少了内核和用户模式之间的上下文切换,底层sendFile方法实现

kafka的协议设计:

43种协议都有对应的Request和Response,每种类型的Request都包含相同结构的协议请求头(api_key,api_version,correlation_id,client_id)和不同的协议请求体(transational_id,acks,timeout,topic_data[]
data[]:主题名称分类,然后再按分区分类,在客户端sender线程中将RecordAccumulator缓存的<分区,List<ProducerBatch>转换为<Node,List<ProducerBatch>,负载分摊给客户端)

时间轮:

kafka采用大量的延时操作,譬如延时拉取、生产、删除等。kafka自定义了一套基于时间轮的延时功能定时器SystemTimer,JDK中的timer和DelayQueue的插入和删除的时间复杂度nlogn。

TimingWheel是一个环形的队列,底层用数组,数组中的每一项是一个TimerTaskList,TimerTaskList是一个环形的双向链表,每一项TimerTaskEntry封装了真正的定时任务,时间轮分层,参考钟表:

第一层tickMs=1ms,wheelSize=60,interval=1min;第二层:tickMs=1min,wheelSize=60,interval=60min...

控制器:

有一个broken被选举为控制器来管理分区和副本,成功竞选的broker会在Zookeeper中创建/controller这个临时节点。每个broken启动时都会取尝试读取改值,如果不为-1,说明已有其他节点竞选成功。每个broken都会在内存中保存控制器的brokerId值。分区leader副本由控制器负责具体实施。按照AR集合中副本的顺序查找第一个存活的副本,并且这个副本在ISR集合中。

分区分配策略StickyAssignor(黏性):1.分区尽可能均匀 2.分区在再均衡后,重分配后等后尽可能与上次分配相同

开启幂等:enable.idempotence设置为true即可,每个新的生产者实例在初始化的时候都会被分配一个PID,这个PID对用户是完全透明的,每个PID发送到的每一个分区都有对应的序列号,生产者每发送一条消息就会将<PID,分区>对应的序列号的值加1.broken会在内存中为每一对<PID,分区>维护一个序列号。当该序列号接收的比原来的大,broken才会接收它。幂等性不能跨多个分区运作,提供transactionalId将消费消息、生产消息、提交消费位移当作原子操作,transactionalId和PID一一对应。前者由用户显示设置,后者由kafka内部分配

消息发送流程:

生产者发送消息至leader副本中,消息被追加到leader副本的本地日志,更新日志偏移量;
follower副本向leader副本请求同步数据,leader副本读取本地日志,并更新对应拉取的follower副本的信息,返回给follower副本;
follower副本将消息追加到本地日志中,并更新日志的偏移量信息

延时队列的使用场景:

在订单系统中:一个用户下单之下通常由30分钟的时间进行支付,如果30分钟之内没有支付就行异常处理;订单完成1小时后通知用户进行评价

手机遥控家里的只能设备在指定时间进行工作。


Kafka Streams :把存储在Kafka中的数据进行处理和分析,然后将最终所得的数据结果写回到kafka或发送到外部系统

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值