- 作者:老汪软件技巧
- 发表时间:2024-08-26 15:01
- 浏览量:
为什么自己要手写一个分布式消息队列
每天大家都在用分布式消息队列,但是也仅限于使用。
个人工作6年,也用过Kafka,rocketmq, rabbitmq, 但是自己似乎从未真正去想过,到底mq里面做了什么,能过支持这么大消息的输入。
以上诸如此类的困惑实在数不胜数...
所以我通过研读rocketmq源码后,并且将核心思想进行提炼后,手写一个属于自己的消息中间件版本,并且在手写的过程中,不断地完善自己的知识体系。把自己学习到的东西都用在该项目中。
参考版本
目前参考rocketmq中4.x版本的架构实现,模拟自己手写一个简易版的分布式消息队列。
角色分工
发送者:
发送消息的角色,要把什么消息发出去,将消息发到什么哪儿。
消息队列:
负责接收发送者发送的消息,并且对消息进行持久化,消息队列可以设置成分布式的,也就是可以将消息分部分存储,比如50%存储在集群中的A节点,另外的50%存储在B节点。
消费者:
消费消息的角色,负责读取什么消息,将消息去具体使用。
系统流程图(图1)
我们仔细思考,除了上图的三个角色,还需要什么呢?
发送者需要将消息发送到消息队列,那么消费者怎么知道真正是要发到哪个ip,这儿总得有个地方存储该ip的信息吧。
同理,消费者也需要到消息队列里面拉取消息,它也得知道消息队列的ip地址是啥。
从上面分析可以得到一个结论:至少需要一个分布式的组件去存储消息队列的ip,便于发送者去发送消息,消费者去读取消息。
说到分布式组件,很多人都会想到zookeeper。 其可以集中管理分布式系统中的配置信息。当配置信息发生变化时,能够通知所有的客户端,使它们及时更新配置。
Kafka就是这样设计的,Kafka低版本将消息集群节点的信息,通过zookeepe维护,这里我不展开叙述。
但是rocketmq的设计没使用zookeeper,其原因有以下几个:
那么rocketmq是怎么解决的呢?它设计了一个叫“nameserv”的模块,整个分布式队列的集群的ip信息,都维护在该模块中,发送者和消费者在启动的时候,需要配置这个nameserv模块的地址,会向这个nameserv拉取集群中,消息队列集群的ip信息,从而进行消息的投递。
优化的架构后如下(图2)
到这里应该会有些朋友会有疑问:
无论是发送者还是消费者, 如果都只是在项目启动的时候,从nameserv中读取消息队列集群的ip,如果消息队列集群中某个ip从集群中剔除了,那怎么办?
这些在这里我不会详细讲,但是这些异常点,正是我们写分布式消息队列这个框架需要考虑的。这也是分布式系统复杂的地方,如何去做容灾和异常考虑,也是框架中最具有挑战的地方。