博客:cbb777.fun
全平台账号:安妮的心动录
github: https://github.com/anneheartrecord
下文中我说的可能对,也可能不对,鉴于笔者程度无限,请君自辨。有问题欢送大家找我探讨
什么是音讯队列
音讯队列,咱们个别会称为MQ(Message Queue),也就是说音讯队列的实质就是一个队列,而队列是一种先进先出的数据结构,提供消息传递和音讯排队模型,能够在分布式环境下提供利用解耦、弹性伸缩、流量削峰、异步通信、数据同步、微服务之间通信等性能,作为分布式系统架构中的一个重要组件,有着无足轻重的位置
咱们会将要传输的数据、音讯放在音讯队列中
其中,往MQ里放货色的叫做生产者
从MQ外面取音讯的叫做消费者
为什么要用音讯队列
解耦
当初有一个零碎A,A能够产生一个UserId
而后有零碎B和零碎C都须要这个UserId去做相干的操作
这样就会有一个问题,每当呈现改变的时候,我都要改变整个零碎,比方B不须要A的音讯了,那么就要改A、B,新来的一个D服务,须要用到A的音讯,那么又须要批改,整个零碎的冗余度很高
引入音讯队列之后:零碎A将id写到音讯队列中,BC服务从音讯队列中拿数据
零碎A只负责写数据,不关怀数据的生产、解决,而BC服务只负责拿数据,即便BC服务挂了,也和零碎A没关系,只和音讯队列无关,这样就能做到多个服务之间的解耦
异步
如果是同步的话,A必须要期待B C D 解决完之后能力返回,整个工夫比拟久
如果是异步的话,A(比方注册服务)将音讯写道音讯队列之后就能够返回,之后再发给邮件服务和短信服务生产
限流、削峰
当申请来的时候,先把申请放在音讯队列中,而后零碎再依据本人可能解决的申请数去音讯队列外面拿数据,这样即便每秒申请数很大,也不会把零碎打崩
流量管制
音讯队列通常有很多种形式来实现流量管制
1.配额管制:通过为每个生产者或者消费者调配配额,限度它们能够发送或者承受的音讯数量。这能够保障音讯队列中的资源不会被适度应用,并确保零碎在高负载状况下的稳定性
2.窗口机制:当生产者将音讯发送到音讯队列的时候,音讯队列会给每个生产和调配一个发送窗口,当消费者确认之后把对应的音讯从窗口外面删除
3.缓冲区:音讯队列把音讯往缓冲区里丢,消费者从缓冲区里去取
4.速率限度:这个个别是在客户端实现的,能够实现生产者在每秒、每分钟生产多少条音讯
如何保障音讯不被反复生产
常见的音讯队列都有确认机制(ACK机制),当消费者生产数据之后会给音讯队列中间件发送一个确认音讯,音讯队列收到之后就会把这条音讯从队列中删除。
当呈现网络传输等故障,ACK没有传送到音讯队列,导致音讯队列不晓得消费者曾经生产过该音讯了,再次将音讯分发给其余的消费者
解决:
1.看场景,如果场景不须要幂等,那么能够不论,比方这条音讯拿去插入数据库,反复插入主键雷同的数据是会主动出错的,再比方做redis的set操作,也不须要管,屡次set之后依然是幂等的
2.筹备第三方介质做生产记录,比方加个redis,给音讯调配一个全局id,只有生产过该音讯就将<id,message>写入redis。消费者生产之前先去redis中查有没有生产记录即可
如何保障音讯的可靠性传输
音讯队列丢数据次要有三个可能
1.生产者丢数据
2.音讯队列组件丢数据
3.消费者丢数据
生产者:能够采纳transaction机制,开启事务来发送音讯,如果发送失败就回滚。然而生产中用的不多,因为会导致吞吐量的降落,个别都是用confirm机制,如果生产者胜利把音讯发送给队列,队列会回一个ack,否则回一个nack
音讯队列:能够开启长久化,而且个别是集群部署的,有master和slave节点,个别都是同步复制,只有主节点和从节点都写入胜利才返回ack给生产者
消费者:勾销主动确认,主动确认后音讯队列收到ACK会立马把音讯从队列中删掉,而是手动确认(即解决后才回ACK)。
音讯队列须要思考的问题
高可用
音讯队列必定不是单机的,这样可用性和健壮性都十分差,所以我的项目中应用的音讯队列都得集群或者分布式
数据问题
音讯失落:当消费者拿了数据还没应用的时候,服务就挂掉了,就会导致音讯的失落,个别会应用ACK应答机制,当消费者拿到音讯发送确认ACK信号,音讯队列才会把对应的音讯删掉
音讯沉积:音讯沉积分为客户端沉积和服务端沉积
个别都会设置告警规定来告诉开发者音讯沉积的问题
如果是客户端音讯沉积,那能够思考扩充生产线程或者节点来解决, 针对于某些非凡场景,如果音讯沉积曾经影响到业务,并且沉积的音讯能够跳过不生产,那么能够重置生产音讯地位为最新地位开始生产,疾速复原业务。
如果是服务端音讯沉积,思考服务端宕机的状况,疾速复原之后从新可用
消费者取数据
两种办法
1.push 音讯队列有新音讯的时候被动叫消费者去拿,实时性强。如果消费者故障,服务端沉积音讯。
2.pull 消费者一直的轮询音讯队列,看看有没有新数据,如果有就生产,实时性弱。
音讯队列的传输模式
点对点模型
用于音讯生产者和音讯消费者之间点到点的通信,音讯生产者将音讯发送到某个特定的消费者
特点:
1.每个音讯只有一个消费者
2.发送者和接收者都没有工夫依赖
3.接受者确认音讯承受和解决胜利
公布订阅模型
公布订阅模型反对向一个特定的音讯主题产生音讯,在这种模型下,公布和订阅者彼此不晓得对方的存在,多个消费者能够取得音讯,在发布者和订阅者之间存在工夫依赖性,发布者publish须要建设一个订阅subscription,以便消费者可能订阅。订阅者必须放弃继续的活动状态并承受音讯
主题、订阅、消费者(组)之间的关系为 M:N:O
在这种状况下,订阅者未连贯时,公布的音讯将在订阅者从新连贯的时候从新公布
特点:
1.每个音讯能够有多个订阅者
2.客户端只有订阅之后能力收到音讯
3.长久订阅和非长久订阅
长久订阅:订阅关系建设之后,音讯就不会隐没,不论订阅者是否在线
非长久订阅:订阅者为了承受音讯,必须始终在线,当只有一个订阅者的时候等于点对点模式
pub-sub vs queue
公布订阅和队列模式是音讯队列中的两种不同的音讯模式
公布-订阅模式:发布者将音讯发送到特定的主题(topic)上,订阅者能够抉择订阅感兴趣的主题,从而承受与该主题相干的音讯。在该模式中,音讯被播送给所有订阅者,每个订阅者能够独立解决音讯,订阅者之间不会互相烦扰。公布-订阅模式通常用于播送音讯或者告诉
队列模式:音讯发送到队列中,而后一个或者多个消费者从队列中收取并解决音讯。在该模式中,每条音讯只能被一个消费者接管和解决。如果有多个消费者,音讯将被平均分配给它们。队列模式通常用于实现任务分配或者负载平衡等场景
市面上音讯队列比照
记住两个最常见的MQ的比照就能够,硬盘MQ代表是Kafka 内存MQ代表是RabbitMQ
Kafka的长处是客户端反对多语言、应用pull模式,反对音讯批量操作,反对replica机制,Zookeeper主动选举leader恢复能力,数据牢靠,有容错容灾的能力,单机吞吐量为10W级,提早毫秒,数据基于硬盘层面存储,多Client反对有序,不反对事务,然而能够通过LOW LEVEL API的形式保障音讯只反对生产一次
RabbitMQ的长处是客户端反对多语言,多协定反对,不反对音讯批量操作,有pull和push两种模式,应用的主从模式master/slave,master提供服务,slave做备份,数据牢靠(因为有备份),单机吞吐量为万级别,音讯提早为微秒级,内存级别,能够被动开启长久化,反对集群和负载平衡,不保障多Client音讯有序
pulsar kafka rabbitmq nsq的异同
1.pulsar 和 kafka基于公布订阅模式,而rabbitmq 和 nsq基于的是队列模式
2.pulsar和kafka都采纳了长久化机制,以反对高吞吐量和高可靠性,而rabbitmq和nsq则将数据存储在内存中,以反对更低的提早和更高的吞吐量
3.pulsar和kafka都反对 多租户和多数据中心部署,能够轻松地在多个数据中心或者云平台上进行扩大,而rabbitmq和nsq则更加适宜单个数据中心的部署
4.pulsar和kafka都具备杰出的可伸缩性和高可用性,解决数据单位是百万级别的;而rabbitmq和nsq则更加适宜小规模的应用程序,具备更低的提早和更高的性能
5.pulsar和kafka都提供了音讯流的处理程序,使用户能够对音讯进行实时剖析和解决
总的来说:pulsar和kafka更适宜解决大量音讯和数据流,rabbitmq和nsq则更适宜小规模应用程序,具备更低的提早和更高的性能
Pulsar底层实现
Pulsar是Apache基金会的顶级我的项目,是云原生的分布式音讯队列,集音讯、存储、轻量化函数式计算为一体,采纳计算与存储拆散架构设计,反对多租户、长久化存储、多机房跨区域数据恢复机制,具备强一致性、高吞吐、低时延及高可扩展性等流数据存储个性
- 云原生MQ
- 单个Pulsar实例原生反对多集群,可跨机房实现音讯复制
- 反对超过一百万个topic
- 反对多语言客户端
- 主题多种订阅模式(独占、共享和故障转移)
- 通过Book Keeper来实现长久化存储,保障消息传递
分层式存储,可在数据古老时将数据从热存储卸载到冷存储中
音讯
Messages有很多的局部组成, 上面是几个值得注意的
- value/payload 音讯的数据
- properties 可选的属性,是一个key->value的键值对
- producer name 生产者名称
- publish time 公布工夫戳
- sequence id 在topic中 每个msg属于一个有序的队列 sequence id是它在序列中的秩序
Pulsar和其余的MQ一样,会对音讯的大小做出限度
这个限度通过broker.conf中的maxMessageSize 决定
不设置的话,默认为5MB
生产者
生产者是关联到topic的程序,它公布音讯到Pulsar的broker上
发送模式
- 异步发送:生产者发送音讯之后会期待broker的确认,如果没有收到确认则认为是发送失败
- 同步发送:会把音讯放在阻塞队列中,而后立马返回,而后这个阻塞队列会往broker中发消息
主题拜访模式
- Shared(共享) 多个生产者能够公布一个主题,这是默认设置
- Exclusive(独占) 一个主题只能由一个生产者公布,如果曾经有生产者链接,其余生产者试图公布该主题将立刻失去谬误。如果"老"的生产者与broker产生网络分区,"老"生产者将被驱赶,"新"生产者将被选为下一个惟一的生产者
- WaitForExclusive(独占期待) 如果曾经有一个生产者连贯,生产者的创立是未决的,直到生产者取得独占拜访。胜利成为排他性的生产者被视为领导者,因而,如果想实现leader选举计划,能够应用这种模式
压缩、批处理与分块
压缩:咱们能够被动压缩生产者在传输期间公布的音讯,Pulsar目前反对以下类型的压缩
- LZ4
- ZLIB
- ZSTD
- SNAPPY
批处理:如果批处理开启,producer将会积攒一批音讯,而后通过一次申请发送进来。批处理的大小取决于最大的音讯数量及最大的提早公布
分块:分块和批处理不能同时启用,要启用分块,必须提前禁用批处理。Chunking只反对长久化的主题
一个producer与一个订阅consumer的分块音讯
当生产者向主题发送一批大的分块音讯和一般的非分块音讯时。将M1切成分块M1-C1、M1-C2、M1-C3。这个broker在其治理的ledger外面保留所有的三个块音讯,而后以雷同的程序分发给消费者(独占/灾备)。消费者将在内存缓存所有的块音讯,直到收到所有的音讯快。将这些音讯合并成为原始的音讯M1,发送给解决过程
消费者
消费者通过订阅关联到主题,而后承受音讯的程序
接管模式
音讯能够通过同步或者异步的形式从broker承受
同步:同步接管将会阻塞,直到音讯可用
异步:异步接管立即返回future值,一旦新音讯可用,它将即可实现
监听
客户端类库提供了它们对于consumer的监听实现,在这个接口中,一旦承受到新的音讯,received办法将被调用
确认
消费者胜利解决音讯之后须要发送确认(ack)给broker,以让broker丢掉这条音讯(否则将始终存储)。音讯的确认能够一一进行,也能够累积到一起。累计确认的时候,消费者只须要确认最初一条它收到的音讯,所有之前的音讯都认为被胜利生产。累积确认不能用于shared模式,因为shared订阅为同一个订阅引入了多个消费者
主题
和其余的MQ一样,Pulsar中的topic是带有名称的通道,用来从producer到consumer传输音讯,topic的名称是合乎良好构造的URL
{persistent|non-persistent}://tenant/namespace/topic
- peisistent|non-persistent 定义了topic的类型,Pulsar反对两种不同的topic:长久化和非长久化,默认是长久化类型,也就是会保留到硬盘上的类型
- tenant 实例中topic的租户,tenant是Pulsar多租户的基本要素,能够被跨集群的流传
- namespace topic的治理单元,与topic组的管理机制相干。大多数的topic配置在namespace层面失效,每个tenant能够有多个namespace
- topic 主题的最初组成部分
Partitioned topics 分区主题
一般的主题只由单个broker提供服务,这限度了主题的最大吞吐量,分区主题是由多个broker解决的一种非凡类型的主题,因而容许更高的吞吐量
分区主题实际上实现为N个主题,N是分区的数量。当音讯公布到分区主题的时候,每个音讯都被路由到几个Broker中的一个。分区在broker之间的散布由Pulsar主动的进行解决
如上图,Topic有五个分区,划分在3个broker上,因为分区比broker多,前两个broker别离解决两个分区,而第三个broker只解决一个分区(Pulsar主动解决分区的散布)
此主题的音讯将播送给两个消费者,路由模式决定将每个音讯公布到哪个分区,而订阅模式决定将哪些音讯发送到哪个消费者
路由模式
- RoundRobinPartition message无key则轮询,有key则hash指定分区(默认模式)
- SinglePartition message 无key 则producer将会随机抉择一个分区,将所有的音讯都发送给该分区。如果message有key,那么会hash指定分区
- CustomParition 应用自定义音讯路由实现
程序保障
音讯的程序与路由模式和音讯的key无关
- Per-key-partition (按key分区) 具备雷同key的所有音讯将被程序搁置在同一个分区中
- Per-producer (依照Producer) 来自同一生产者的所有音讯都是有序的
哈希计划
HashingScheme是一个enum,示意在抉择要为特定音讯应用的分区时可用的规范哈希函数集
有两种类型的规范哈希函数可用:JavaStringHash和Murmur3_32Hash,生产者的默认哈希函数是Java,然而当生产者的客户端是多语言的时候,Java是没用的
长久/非长久化主题
默认状况下,Pulsar会保留所有没确认的音讯到Book Keeper中,长久Topic音讯会在broker重启或者consumer出问题的时候保留下来
Pulsar也反对非长久化Topic 这些Topic的音讯只存在于内存中,不会存储到磁盘
因为Broker不会对音讯进行长久化存储,当Producer将音讯发送给Broker时,Broker能够立刻将ack返回给Producer,所以非长久化的消息传递会比长久化的更快。绝对的,当Broker因为一些起因宕机、重启后,非长久化的Topic音讯都会隐没,订阅者将无奈收到这些音讯。
Dead letter topic 死信主题
死信主题容许你在用户无奈胜利生产某些音讯时应用新音讯。在这种机制中,无奈应用的音讯存储在独自的主题中,成为死信主题。
死信主题依赖于音讯的从新投递,因为确认超时或者否定确认,音讯将被从新发送。如果要对音讯应用否定确认,请确保在确认超时之前对齐进行否定确认。
Retry letter topic 重试主题
对于许多在线业务零碎,因为业务逻辑解决中出现异常,音讯会被反复生产。
若要配置从新生产失败音讯的延迟时间,能够配置生产者将音讯发送到业务主题和重试主题,并在消费者上启用主动重试。当在消费者上启用主动重试的时候,如果音讯没有被生产,那么就会存储到重试主题中,在指定的延迟时间后,消费者会被动承受来自重试主题的失败音讯
订阅模式
Pulsar反对exclusive (独占) failover(灾备) shared(共享) 和 key_shared(Key共享)四种音讯订阅模式,示意图如下
独占模式
默认的音讯订阅模式。只能有一个消费者生产音讯
灾备模式
灾备模式下,一个topic也是只有单个consumer生产一个订阅关系的音讯,然而在这个模式下,每个消费者会被排序,当后面的消费者无奈连贯上broker后,音讯会由下一个消费者进行生产
共享模式
共享模式下,音讯可被多个consumer同时生产,无奈保障生产的程序,音讯通过roundrobin的形式投递到每一个消费者
key共享模式
依照key对音讯进行投递,雷同的key的音讯会被投递到同一个consumer上,生产示意图如下
音讯保留与过期
默认策略
- 立刻删除所有被消费者确认过的音讯
- 以backlog的模式,长久化保留所有未被确认的音讯
两个个性
- 音讯保留能够让你保留consumer确认过的音讯
- 音讯过期能够让你给未被确认的音讯设置ttl
音讯保留和过期是针对namesapce层面进行设置和治理的
音讯去重
实现音讯去重的一种形式是确保音讯只生产一次,即生产者幂等,这种形式的毛病在于把音讯去重的工作交给利用来做。
在pulsar中,broker反对配置开启音讯去重,用户不须要被动在代码中保障Producer只生产一次,启动之后即便音讯被屡次发送到topic上,也只会被长久化到磁盘一次
原理:Producer对每一个发送的音讯,都会采纳递增的形式生成一个惟一的sequence ID,这个音讯会放在message的元数据中传递给broker。
同时,broker也会保护一个pendingmessage队列,当broker返回发送胜利ack之后,producer会将pendingmessage队列中的sequence id删除,标识producer工作这个音讯生产胜利。
broker会记录针对每个producer承受到的最大sequence id和曾经解决完的最大sequence id
当broker开启音讯去重之后,Broker会针对每个音讯申请进行是否去重的判断,如果音讯反复,则间接返回ack,不走后续存储的流程
延时音讯
延时音讯性能容许Consumer可能在音讯发送到topic之后,过一段时间之后能力生产到这条音讯。在这种集中中,音讯在公布到broker之后,会被存储在book keeper中,当到音讯特定的延迟时间时,音讯就会传递给consumer
broker不会在存储的时候做非凡解决,而是会把设置了延迟时间的音讯退出到DelayedDeliveryTracker中,当到了指定的发送工夫时,Tracker才会把这条音讯推送给消费者
原理:
在Pulsar中有两种形式实现提早音讯,别离为deliverAfter和deliverAt
deliverAfter能够指定在多长时间之后进行生产
deliverAt能够指定具体的提早生产工夫戳
DelayedDeliveryTracker会记录所有须要提早投递的音讯的index,index由timestamp、 ledger id 、和entry id三局部组成,其中ledger id 和 entry id用来定位该音讯
timestamp除了记录须要投递的工夫,还用于提早优先级队列排序。tracker会依据延迟时间对音讯进行排序
多租户模式
Pulsar的云原生架构人造反对多租户,每个租户下还反对多Namespace,非常适合做共享大集群,不便保护。此外Pulsar人造反对租户之间的逻辑隔离,避免相互烦扰,还能实现大集群资源的充分利用
- Tenant(租户)和 Namespace是Pulsar反对多租户的两个外围概念
- 在租户级别,Pulsar为特定的租户预留适合的存储空间、利用受权和认证机制
- 在namespace级别,Pulsar有一系列的配置策略(policy),包含存储配额、流控、音讯过期策略等等
对立音讯模型
Pulsar做了队列模型和流模型的对立,在topic级别只须要保留一份数据,同一份数据可屡次生产。以流式、队列等形式计算不同的订阅模型,大大的晋升了灵便度
同时Pulsar通过事务采纳Exactly-Once刚好一次的语义,在进行音讯传输过程中,能够确保音讯不重不丢
分片流
Pulsar将无界的数据看作是分片的流,分片扩散存储在分层存储(tiered storage)、BookKeeper集群和Broker节点上,而对外提供一个对立的、无界数据的视图
不须要用户显示迁徙数据,对用户无感知,缩小存储老本并放弃近似有限的存储
跨地区复制
Pulsar中的跨地区复制是将Pulsar中长久化的音讯在多个集群之间备份
在Pulsar2.4.0中新增了复制订阅模式,在某个集群生效状况下,该性能能够在其余集群复原消费者的生产状态,从而达到热备模式下的音讯服务高可用
架构
单个Pulsar集群由以下三局部组成
- 一个或者多个broker 用于负责解决和负载平衡producer收回的音讯 并将这些音讯分派给consumer。Broker 和 Pulsar配置存储交互来解决相应的工作,并将音讯存储在BookKeeper实例中(bookies);Broker底层依赖的是Zookeeper集群来解决特定的工作
- 蕴含一个或者多个bookie的BookKeeper负责音讯的长久化存储
- 一个ZooKeeper集群用来解决多个Pulsar集群之间的协调工作
Pulsar拆散出Broker和Bookie两层架构,Broker为无状态的服务,用于公布和生产音讯,而BookKeeper专一于存储,Pulsar存储是分片的,这种架构能够防止扩容时受到限制,实现数据的独立拓展和疾速复原
Brokers
Pulsar的broker是一个无状态的组件,次要负责运行另外的两个组件
- 一个HTTP服务器(service discovery) 它裸露了REST系统管理接口以及在生产者和消费者之间进行Topic查找的API
- 一个调度散发器(Dispatcher) 它是一个异步的TCP服务器 通过自定义二进制协定利用与所有相干的数据传输
出于性能思考,音讯通常从Managed Ledger缓存中分派进来,除非积压超过缓存大小。如果积压的音讯对于缓存来说太大了,则Broker开始从BookKeeper中读取Entries
为了反对全局Topic异地复制,Broker会管制Replicators追踪本地公布的条目
ZooKeeper元数据存储
Pulsar应用ZooKeeper进行元数据存储、集群配置和协调
- 配置存储Quorum存储了租户、命名空间、和其余须要全局统一的配置项
- 每个集群有本人独立的本地ZooKeeper保留集群外部的配置,例如broker负责哪几个主题及所有权归属元数据、broker负载报告ledger元数据等等
BookKeeper长久化存储
Apache Pulsar为应用程序提供有保障的信息传递,如果音讯胜利达到broker,就认为其预期达到了目的地
为了提供这种保障,未确认送达的音讯须要长久化直到它们被确认送达。这种消息传递模式通常成为长久消息传递,在Pulsar外部,没分新音讯都被保留并同步N份
BookKeeper是一个分布式的预写日志(WAL)零碎,有如下几个个性
- 使得Pulsar可能利用独立的日志,称为ledgers,随着工夫的推移能够为topic创立多个ledgers
- 保障多零碎挂掉时的ledgers的读取一致性
- 提供不同Boookies之间平均的IO散布的个性
- 容量和吞吐量都具备程度伸缩性,可能通过减少bookies立刻减少容量到集群中
本文由mdnice多平台公布