最近在写项目的某个需求时用到了tdmq,因此系统性的学习了消息队列,以下便是一些学习心得。
消息队列(MQ)是大型分布式系统中一种常见的中间件,主要解决应用耦合、异步消息、流量削锋等问题,已成为异步RPC的主要手段之一。目前企业使用较多的消息队列有ActiveMQ、RabbitMQ、Kafka、ZeroMQ、Pulsar等。
为什么需要消息队列?使用消息队列的场景有很多,结合我在工作中的一些思考,我认为大多数项目引入消息队列都是为了解决错峰流控和服务解耦这两主要问题。
错峰流控错峰:线上部署的系统所面对的请求量会存在高峰和低谷,例如每天的23:00 - 8:00系统每秒接收的请求数为100,8:00 - 22:00系统每秒接收的请求数为3K+,而到了22:00 - 23:00这个时间段,系统每秒接收的请求数为1W+。面对这种情况,我们不可能将系统的QPS设计为1W+(资源都是宝贵的,不比在学校实验室),这时将MQ引入系统架构中,便解决了这种大流量冲击问题。假设将系统的QPS设计为4K,满足了8:00 - 22:00的正常流量需求,在22:00 - 23:00时间段里,可以先把所有的接收的请求放进MQ,系统仍然以4K的速度处理从MQ中请求,无法处理的积压在MQ中在23:00 - 8:00完成处理。这样既防止了系统的宕机(系统处理请求的速度始终没超过Max),也使系统在有限的资源情况下平稳的完成大流量请求。
流控:系统的上下游接口对同一请求的处理速度是不一致的,上游的A接口一秒处理完100个请求就会把100份数据直接丢给下游B接口,而B接口的处理速度可能是一秒50个,这种情况下便会让B接口直接挂掉。此时,如果引入了MQ,可以让A接口处理完后直接写入MQ,在让MQ以每秒50个的速度发送给下游B接口处理,协调了系统各接口通信能力。
可以看出,引入MQ后,对请求的处理会存在一定的时延,所以消息队列主要被用来处理一些对于其他模块非常重要但是对于自身模块不关心的业务。
服务解耦以电商下单举例,系统调用链路为:用提交订单->预扣库存->生成订单->付款消费成功->通知配送系统->通知商家系统->通知后台系统计入财务、日志等模块这几个步骤。当系统没有解耦时,顾客每个订单都是串行执行,需要等待时间太多,同时也浪费了接口的并发带来的性能提高。当引入消息队列对系统架构就行解耦,如下图所示,用户可在订单系统生成订单后就直接返回,使用MQ后续同时处理剩余流程。既不影响整个业务的体验,而且在性能和响应速度上有了极大提升。
**在引入消息队列后可以让系统更改透明化,上下游系统不再互相影响,实现了系统间的解耦。**你只会关注当前模块的需要使用的消息结构,对其他模块的处理逻辑不用关心,适合部门之间的协调工作。
消息队列的演化 基于OS的MQ单机消息队列可以通过操作系统的进程间通信机制实现,如共享内存和消息队列等。
比如可以在共享内存中生成一个双端队列:消息生产进程往队列里添加消息,消息消费进程在队尾有序地取出这些消息。可以把添加消息的进程命名为producer,而取出消息的进程命名为consumer。
单机MQ易于实现,但是缺点也很明显:因为依赖于单机OS的IPC机制,所以无法实现分布式的消息传递,并且消息队列的容量也受限于单机资源。
基于数据库的MQ使用数据库(如Mysql 、Redis等)存储消息, 通过对记录的插入与删除实现消息的生产和消费,继而完成MQ功能。
以Redis 为例, 可以使用Redis自带的list实现。Redis list使用 lpush命令,从队列左边插入数据;使用 rpop命令,从队列右边取出数据。与单机的不同就在与,可以实现分布式环境下多机共用一个消息队列。
存在的缺陷:
- 热key性能问题:某个list的读写请求最终都会落到同一台redis实例上,且无法通过扩容来解决问题。如果对某个list的并发读写非常高,就产生了无法解决的热key,严重可能导致系统崩溃。
- 没有消费确认机制:每当执行rpop消费一条数据,那条消息就被从list中永久删除了。如果消费者消费失败,这条消息也没法找回了。
- 不支持多订阅者:一条消息只能被一个消费者消费,rpop之后就没了。如果队列中存储的是应用的日志,对于同一条消息,监控系统需要消费它来进行可能的报警,BI系统需要消费它来绘制报表,链路追踪需要消费它来绘制调用关系,这种场景redis list就无法支持。
- 不支持二次消费:一条消息rpop之后就没了。如果消费者程序运行到一半发现代码有bug,修复之后想从头再消费一次就不行了。
针对上述缺陷,Redis 5.0引入了stream数据类型,它是专门设计成为消息队列的数据结构,借鉴了很多kafka的设计,但是随着很多分布式MQ组件的出现,仍然显得不够友好。在我看来Redis解决了分布式读数据的问题,MQ解决了分布式处理数据能力,一个读,一个写。
专用MQ中间件随着互联网业务的发展,一个真正的消息队列,已经不是利用队列收发消息那么简单了,业务对MQ的吞吐量、扩展性、稳定性、可靠性等都提出了严苛的要求。因此,专用的分布式消息中间件开始大量出现。常见的有RabbitMQ、RocketMQ、ActiveMQ、RabbitMQ、Kafka、ZeroMQ、Pulsa等等。
如何设计一个专业消息队列


