消息中间件利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来进行分布式系统的集成。通过提供消息传递和消息排队模型,它可以在分布式环境下扩展进程间的通信。
我们来看几个关键点:
消息 消息中间件定义了一个协议(模型),基于该协议可以传递消息
分布式 消息中间件的引入是为了解决分布式系统的问题,对于简单的单应用系统用不着
举例来说,一个常见的最简单的UGC应用,至少包括后端、APP、审核后台、统计平台)。UGC应用的生命力在于用户产生内容的质量,所以需要审核用户提交的内容;另一方面,需要统计用户产生内容的数据。用户A产生的内容C在系统间的流动路径是这样的,APP -> 后端 -> 审核 -> 统计,其中审核和统计可以同时进行,这就遇到了各系统间的数据通信问题。简单的解决方案是所有的系统公用一套数据库(单库或者集群,总之数据库结构一样)。这样的好处是实现简单,可靠;坏处是没有关注平台间的差异性,不同平台需要的数据结构不一样,通用的数据库容易造成各个平台都达不到最好的性能,比如统计需要对所有数据进行计算;另外各个系统之间在数据库发生了耦合,在统计和审核过程中产生的垃圾SQL可能影响在线的业务。引入消息中间件作为各个系统间通信大使可以有效的解决上述问题:数据在后端产生后,可以扔到消息中间件,其余需要这个数据的系统订阅改消息中间件就可以拿到这条数据并进行自己业务内的操作。这样做的最大好处是系统隔离,系统之间不互相影响,缩小问题的影响范围,避免问题的连锁反应——把三个容易出问题的系统绑在一起,出问题的概率不止提升了三倍!
消息中间件就像是快递员,把东西给我,告诉我送给谁,你忙你的去吧。
消息中间件的衡量标准
消息中间件的三大要素:生产者(Producer)、消息(Message)、消费者(Consumer)。衡量标准基本围绕这三者的交互,这里只说我在选择消息中间件的时候常用的。
消息路由 消息如何经过消息中间件到达消费者,在一定程度上决定了消息中间件的灵活性。简单的命名队列,TOPIC订阅能满足大部分的场景,对于复杂的业务可能需要比如基于PATTERN的路由(RabbitMQ),消息复制,消息生命周期管理(beanstalk)。
消息可靠性 大部分场景下消息是容忍丢失的,或者说对性能的渴求大于可靠性,比如异步发短信,异步发邮件,概数数据统计,日志等。另外有的场景是不允许消息丢失的,消息的丢失会带来数据的不一致,不一致的数据很多时候是灾难的开始,比如异步写数据,下单后减库存,转账等。可靠性基本都依赖于持久化。
消息重放 不常用但是很有用。这个说的是即使是消费过的消息也能设定offset(一般是时间点)重新消费。这个功能在消息下游数据丢失,新系统导入旧数据的时候非常有用,不用再去理繁杂的数据对应关系,按照正常的业务逻辑处理消息就OK了,非常好用!非常好用!非常好用!
消息堆积 抗流量神技。像双十一这种超高峰流量都会用到这个功能,这时候一方面会把消息中间件下游业务(Consumer端)的机器挪到核心业务,另一方面消息中间件在高并发投递消息的时候可能出问题,所以把消息暂存在中间件,等流量高峰过去了再投递到下游业务。
分布式集群支持 高可用的需求,解决单点问题。
ACK 消息确认,在下游业务确认后才将消息标记为已消费,处理超时则重新投递消息,这里要求下游业务自己做可重入(幂等)
消息顺序 有的业务要求消息投递顺序和消费顺序一致,或者至少要求对于单个用户顺序一致,比如用户的赞/取消操作,顺序反了数据就会错乱
性能和扩展 这里指的扩展是能否通过增加Consumer来提高消息的消费速率以及消息中间件的容量是否有理论的上线;性能主要指tps、qps以及并发连接数。
消息协议 优先考虑标准协议或者使用广泛的协议,有利于后期的维护和扩展
Redis作为消息中间件
Redis自带的PUB/SUB机制,即发布-订阅模式。这种模式生产者(producer)和消费者(consumer)是1-M的关系,即一条消息会被多个消费者消费,当只有一个消费者时即可以看做一个1-1的消息队列,但这种方式并不适合题主的场景。首先,数据可靠性的无法保障,题主的数据最终需要落库,如果消息丢失、Redis宕机部分数据没有持久化甚至突然的网络抖动都可能带来数据的丢失,应该是无法忍受的。其次,扩展不灵活,没法通过多加consumer来加快消费的进度,如果前端写入数据太多,同步会比较慢,数据不同步的状态越久,风险越大,可以通过channel拆分的方式来解决,虽然不灵活,但可以规避。这种方案更适合于对数据可靠性要求不高,比如一些统计日志打点。
Redis的PUSH/POP机制,利用的Redis的列表(lists)数据结构。比较好的使用模式是,生产者lpush消息,消费者brpop消息,并设定超时时间,可以减少redis的压力。这种方案相对于第一种方案是数据可靠性提高了,只有在Redis宕机且数据没有持久化的情况下丢失数据,可以根据业务通过AOF和缩短持久化间隔来保证很高的可靠性,而且也可以通过多个client来提高消费速度。但相对于专业的消息队列来说,该方案消息的状态过于简单(没有状态),且没有ack机制,消息取出后消费失败依赖于client记录日志或者重新push到队列里面。