Open lukaliou123 opened 2 years ago
NameServer:单点,供Producer和Consumer获取Broker地址 Producer:产生并发送消息 Consumer:接受并消费消息 Broker:消息暂存,消息转发 RocketMQ由NameServer注册中⼼集群、Producer⽣产者集群、Consumer消费者集群和若⼲ Broker(RocketMQ进程)组成,它的架构原理是这样的:
Queue就是来源于数据结构的FIFO队列。而Topic是个抽象的概念,每个Topic底层对应N个queue,而数据也真实存在queue上的
不会,每条消息都会持久化到commitLog中,每个Consumer连接到Broker后维持消费进度信息,当有消息消费后只是当前Consumer的消费进度(CommitLog的offset)更新了,4.6版本后默认48小时会删除不再使用的的CommiLog文件
消费者获取消息有两种模式:推送模式和拉取模式 1.PushConsumer 推送模式(虽然 RocketMQ 使用的是长轮询)的消费者。消息的能及时被消费。使用非常简单,内部已处理如线程池消费、流控、负载均衡、异常处理等等的各种场景。 长轮询,就是我们在 《 精尽【消息队列 】面试题》 提到的,push + pull 模式结合的方式。
2.PullConsumer 拉取模式的消费者。应用主动控制拉取的时机,怎么拉取,怎么消费等。主动权更高。但要自己处理各种场景
要保证消息的顺序消费,有三个关键点 消息顺序发送 消息顺序存储 消息顺序消费
1.消息顺序发送:消息顺序发送,多线程发送的消息无法保证有序性,因此,需要业务方在发送时,针对同一个业务编号(如同一笔订单)的消息需要保证在一个线程内顺序发送,在上一个消息发送成功后,在进行下一个消息的发送。对应到mq中,消息发送方法就得使用同步发送,关键点在于单线程同步顺序发送消息。
3.消息顺序消费:保证消息顺序消费,同一个queue就只能被一个消费者所消费,因此对broker中消费队列加锁是无法避免的。同一时刻,一个消费队列只能被一个消费者消费,消费者内部,也只能有一个消费线程来消费该队列。即,同一时刻,一个消费队列只能被一个消费者中的一个线程消费。关键点在于保证一个队列同一个时刻只能被一个消费者中一个线程消费
https://blog.csdn.net/Rong_Toa/article/details/111239690 是因为使⽤了顺序存储、Page Cache和异步刷盘
1.我们在写⼊commitlog的时候是顺序写⼊的,这样⽐随机写⼊的性能就会提⾼很多 2.写⼊commitlog的时候并不是直接写⼊磁盘,⽽是先写⼊操作系统的PageCache 3.最后由操作系统异步将缓存中的数据刷到磁盘
RocketMQ主要的存储⽂件包括commitlog⽂件、consumequeue⽂件、indexfile⽂件 Broker在收到消息之后,会把消息保存到commitlog的⽂件当中,⽽同时在分布式的存储当中,每个broker都会保存⼀部分topic的数据, 同时,每个topic对应的messagequeue下都会⽣成consumequeue⽂件⽤于保存commitlog的物理位置偏移量offset,indexfile中会保存 key和offset的对应关系。
CommitLog⽂件保存于${Rocket_Home}/store/commitlog⽬录中,从图中我们可以明显看出来⽂件名的偏移量,每个⽂件默认1G,写满 后⾃动⽣成⼀个新的⽂件
由于同⼀个topic的消息并不是连续的存储在commitlog中,消费者如果直接从commitlog获取消息效率⾮常低,所以通过consumequeue 保存commitlog中消息的偏移量的物理地址,这样消费者在消费的时候先从consumequeue中根据偏移量定位到具体的commitlog物理⽂件,然后根据⼀定的规则(offset和⽂件⼤⼩取模)在commitlog中快速定位。
事务消息加上事务反查机制 事务消息就是MQ提供的类似XA的分布式事务能力,通过事务消息可以达到分布式事务的最终一致性。
半事务消息就是MQ收到了生产者的消息,但是没有收到二次确认,不能投递的消息 实现原理如下: 1.生产者先发送一条半事务消息到MQ 2.MQ收到消息后返回ack确认 3.生产者开始执行本地事务 4.如果事务执行成功发送commit到MQ,失败发送rollback 5.如果MQ长时间未收到生产者的二次确认commit或者rollback,MQ对生产者发起消息回查 6.生产者查询事务执行最终状态 7.根据查询事务状态再次提交二次确认
1. 消息队列的使用场景
1.异步:A系统需要发送请求给B系统处理,由于B系统需要查询更新数据库花费时间较长,以至于A系统要等待B系统处理完毕后再发送下个请求,造成A系统资源浪费。使用消息队列后,A系统生产完消息后直接丢进消息队列。
2.解耦:A系统发送个数据到BCD三个系统,接口调用发送,那如果E系统也要这个数据呢?那如果C系统现在不需要了呢?现在A系统又要发送第二种数据了呢?更加崩溃的是,A系统要时时刻刻考虑BCDE四个系统如果挂了咋办?我要不要重发?我要不要把消息存起来?使用消息队列就能解决这个问题,A系统只负责生产数据,不需要考虑消息被哪个系统来消费。
3.削峰:A系统调用B系统处理数据,每天0点到11点,A系统风平浪静,每秒并发请求数量就100个。结果每次一到11点~1点,每秒并发请求数量突然会暴增到1万条。但是B系统最大的处理能力就只能是每秒钟处理1000个请求啊,那系统就会直接崩掉。引入消息队列后,把请求数据先存入消息中间件系统中,消费系统慢慢拉取消费,可能会比正常的慢一点,但是不至于打挂服务器。等流量高峰下去了,服务也就没压力了。
2.消息队列的缺点
1.系统可用性降低:系统引入的外部依赖越多,越容易挂掉,本来就是A系统调用BCD三个系统的接口就好了,ABCD四个系统好好的,没啥问题,偏偏加个 MQ 进来,万一 MQ 挂了怎么办呢?MQ 挂了,整套系统崩溃了,这不就凉了吗?
2.系统复杂性提高:本来代码想怎样写就怎样写,现在加入一个 MQ 之后,还得考虑如何去维护它。除此之外,还有一大堆问题,例如消息重复消费、消息丢失、消息的顺序消费等等,烦死了。
3.数据一致性问题:A系统处理完了直接返回成功了,别人以为你这个请求就成功了。但是问题是,要是BCD三个系统那里,BD两个系统写库成功了,结果C系统写库失败了,怎么办?这数据就不一致了。(解决方法:分布式事务)