消息队列(Message Queue,简称MQ),指保存消息的一个容器,本质是个队列。
消息队列中间件是分布式系统中重要的组件(单机版也可用:单机版指的是在服务器上安装),主要解决应用耦合,异步消息,流量削锋等问题。实现高性能,高可用,可伸缩和最终一致性架构。
使用较多的消息队列有ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ。
名词解释
在异步处理,应用解耦,流量销峰时使用。
有些业务不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。
下图中下订单过程,传统的做法有两种:1.串行的方式;2.并行的方式,而引入消息队列,将不是必须的业务逻辑,异步处理。
因此在引入MQ后,系统的吞吐量提高了。
用户下单支付后,支付系统需要通知短信服务和积分服务。传统的做法是,支付系统调用短信系统和积分系统的接口。
传统模式的缺点:
如何解决以上问题呢?引入应用消息队列后的方案
订单系统与库存系统耦合
如何解决以上问题呢?引入应用消息队列后的方案,如下图:
支付系统:用户支付,支付系统完成持久化处理,将消息写入消息队列,返回用户支付下单成功
积分系统:订阅支付的消息,采用拉/推的方式,获取支付信息,积分系统根据支付信息,进行积分操作
短信系统:订阅支付的消息,采用拉/推的方式,获取支付信息,短信系统根据支付信息,进行短信操作
假如:在下单时积分系统或短信系统不能正常使用。也不影响正常支付,因为支付后,支付系统写入消息队列就不再关心其他的后续操作了。实现支付系统与积分系统,支付系统和短信系统的应用解耦。
假如双十一当天,很多用户同时下订单,流量激增,订单系统可能会扛不住这么大的请求而挂断。为解决这个问题,一般需要在应用前加入消息队列。
有了消息队列可以将大量请求缓存起来,分散到很长一段时间处理,这样可以大大提到系统的稳定性和用户体验。
例如图中本来8k/s的请求,经过MQ缓存后
业务系统正常时段的QPS如果是1000,流量最高峰是10000,为了应对流量高峰配置高性能的服务器显然不划算,这时可以使用消息队列对峰值流量削峰
再比如领导要给一万名员工下发邮件通知,这显然是瞬时高并发的。这个时候就可以在消息队列缓存一部分
如图:气象局不需要写很多对外调用的接口,只需要为网易,新浪,百度等开通消息队列访问权限,网易,新浪等直接订阅气象局的消息,获取最新天气预报数据。如果新的网站想要加入,气象局也不需要做任何代码变更。
重复消费问题可以说是mq中普遍存在的问题,不管你用哪种mq都无法避免。
如果重复消息不做正确的处理,会对业务造成很大的影响,产生重复的数据,或者导致数据异常,比如会员系统多开通了一个月的会员。
有哪些场景会出现重复的消息呢?
如果mq的消费者业务处理异常的话,就会出现数据一致性问题。
比如:一个完整的业务流程是,下单成功之后,送100个积分。下单写库了,但是消息消费者在送积分的时候失败了,就会造成数据不一致的情况,即该业务流程的部分数据写库了,另外一部分没有写库。
如果下单和送积分在同一个事务中,要么同时成功,要么同时失败,是不会出现数据一致性问题的。但由于跨系统调用,为了性能考虑,一般不会使用强一致性的方案,而改成达成最终一致性即可。
这里说的系统复杂度和系统耦合性是不一样的,比如以前只有:系统A、系统B和系统C 这三个系统,现在引入mq
之后,你除了需要关注前面三个系统之外,还需要关注mq服务,需要关注的点越多,系统的复杂度越高。
mq的机制需要:生产者、mq服务器、消费者。
有一定的学习成本,需要额外部署mq服务器,而且有些mq比如:rocketmq,功能非常强大,用法有点复杂,如果使用不好,会出现很多问题。有些问题,不像接口调用那么容易排查,从而导致系统的复杂度提升了。
RabbitMQ 于 2007 年发布,是使用 Erlang 编程语言编写的,开箱即用的额消息队列,RabbitMQ 是一个相当轻量级的消息队列,非常容易部署和使用。
RabbitMQ 的几个问题:
RocketMQ 是阿里在 2012 年开源的消息队列产品,用 Java 语言实现,在设计时参考了 Kafka,并做出了自己的一些改进,后来捐赠给 Apache 软件基金会,2017 正式毕业,成为 Apache 的顶级项目。RocketMQ 在阿里经历过多次双十一考验,它的性能、稳定性和可靠性都是值得信赖的。
RocketMQ 的性能比 RabbitMQ 要高一个数量级,每秒钟大概能处理几十万条消息。
Apache Kafka 是一个分布式消息发布订阅系统。Kafka 与周边生态系统的兼容性是最好的没有之一,尤其在大数据和流计算领域,几乎所有的相关开源软件系统都会优先支持 Kafka。Kafka 使用 Scala 和 Java 语言开发