tinyMQ

本文记述了笔者对中间件中消息队列的学习历程,以及一个模仿rabbitMQ架构的tinyMQ的实现历程。

进程间通信、尤其是跨主机的进程间通信是现代网络应用系统中绕不过的问题,中间件对此给出的解决方案也是非常多的——RPC、消息队列等。

RPC的内容在此暂且不论,待后续有机会再作讨论;今天的主题在消息队列,及笔者的一个拙劣实现。

消息队列

消息队列,也称消息中间件,是一种基于事件驱动模型的消息传递机制,它是一种非阻塞的消息传递机制,可以让程序在不同的系统之间进行通信。

消息队列的结构

与RPC强调的远程调用相比,消息队列不强调对函数调用这一行为的模拟,而是把这一行为抽象成消息,消息队列的消息可以是任意类型的数据,可以是字符串、数字、二进制等,将消息发布,由消息的消费方来决定对应的行为。

消息队列的特点

消息队列的特点已是老生常谈了

  1. 解耦: 消息队列的消息发布者和消息队列的消息消费者之间不需要知道对方的地址与接口。比起RPC仅仅将程序分离运行,而调用时还需要知晓接口,消息队列连对方的接口都不需要,基本实现了完全的解耦。
  2. 异步: 消息队列的消息发布者和消息队列的消息消费者之间不需要等待对方的响应。消息队列的发布者发布消息后并不关系结果,而消费者则自助式地消费信息,做出相应,二者之间完全异步,提高了性能。
  3. 削峰: 这一点也是基于异步展开的。消息队列可以暂时或持久地保存发布者发布的消息,而不需要消费者消费消息,消费者可以暂时不消费消息,而可以在需要时再消费消息。这样便能提高服务对突发性流量的应对能力。

但是,代价是什么呢?

消息队列借由放弃调用方/发布者与被调用方/消费者中的一致性,获取了完全的解耦,令人向往的异步;但是,这大大降低了编程人员对系统行为的把控。

很可能上游发布消息,而下游没能接收到消息;或者下游接收到消息了,而没能正确响应;再或者下游响应了,但时间差过大。

这一切都导致上游无法假定下游的行为,从无法进行一些依赖于下游行为的操作。

一般而言,我们会将一些无需反馈的服务放在消息队列的消费者端,如发送不重要的通知邮件等;而较为强调及时性、一致性的服务,如数据库操作,交由RPC处理。

常见的消息队列

消息队列发展多年,经历过野蛮发展、百花齐放的时期,也经历了大一统时期(AMQP协定),如今则处于统一背景下的稍作自由。

由于协议诸多,且相对复杂,笔者也无法完全掌握,就不信口胡诌,而交由读者自己去探索了。

TinyMQ

为了更好地学习消息中间件的相关细节,以及顺带锻炼一下自己的并发编程能力,笔者将TinyMQ作为笔者的消息中间件学习的第一个例子。

TinyMQ的架构

TinyMQ是笔者在中间件课程组队实验中的产物,它是一个简单的消息中间件,模仿rabbitMQ而成。当然,由于笔者与搭档精力、能力有限,没能亲自解读rabbitMQ源码,仅能依赖网络上的资料进行推测。

rabbitMQ中,消息发布者并不直接将消息放入队列,而是将消息交给exchanger,exchanger再将消息交给队列;消息消费者向MQ订阅消息队列,消息队列在合适的时机将队列中的消息发送给订阅者。

其间,消息发布者与Exchanger构成多对多关系,表征不同上游可以发布同一消息,同一上游也可以发布不同消息;Exchanger与Queue构成多对多关系,以此实现消息的订阅自由性;而订阅者与Queue则常常是一对一关系,用于保证消息的唯一性,多对多则用于实现负载均衡,订阅统一Queue的消费者构成订阅组,仅有其中一人能接收到消息。

在TinyMQ中,对Exchanger作了进一步简化,Exchanger仅仅是一个key值到订阅该key值的Queue的映射。

发布者将带有key值的消息发布到MQ,MQ处理该消息:

  1. 开启线程,处理请求
  2. 子线程查找该key
    1. 存在:查找该消息的key对应的Queue,对每个Queue投递一份该消息的副本
    2. 不存在:丢弃该消息
  3. 子线程断开连接,结束线程

而当订阅者上线,连接到MQ时:

  1. MQ开启子线程,与订阅者保持连接
  2. 子线程会查找该订阅者对应的Queue
    1. 若有消息:并将该Queue中的消息投递给订阅者
    2. 若无消息:阻塞,等待消息
  3. 直到消费者取消请阅,断开连接

而在tinyMQ的客户端:

  • Publisher:开启发布线程,将队列中的消息异步发送
  • Consumer:
    • Subscribe模式:异步模式,开启监听线程,监听指定的消息队列,收到消息执行回调函数
    • Get模式:同步模式,获取指定队列中的一条消息,返回给主线程,由主线程处理

实现较为粗糙,但是足以满足笔者的需求,今后若有需要,可能会进一步完善吧。

Github-TinyMQ