想使用消息队列,先考虑下这些问题

消息队列优势

消息队列(Message Queue,简称MQ),其主要用于在复杂的微服务系统中进行消息通信,它的优点可以大致整理成以下几点:

  1. 服务间解耦
  2. 提高服务并发、性能
  3. 突发流量削峰
  4. ...

服务间解耦

微服务系统业务之间相互依赖,各种调用错综复杂,如果不能良好对服务进行解耦那一个服务的可用性、并发都会受到其他服务的影响。

在没有引用MQ的之前服务调用大概是这些步骤:

图上的A服务是直接调用的,这是没啥问题的,但是服务上线后要迭代更新的麻,这个时候要是服务C的开发人员有点代码小洁癖说:我这个C服务接口命名不太好,我需要重新更新下,当A服务的小哥哥还戴着小耳机听着小歌曲,突然就得改代码了~~。

后来负责服务C的那小哥哥也不好意思了,提出大家一起使用MQ吧,于是A、C的调用就变成下面这个样子了:

服务A不直接调用C而是向消息队列中发送消息(生产者),另一边的C取出队列中的消息(消费者)进行处理,这样A、C就完成了解耦。

提高服务并发、性能

举个例子,在没引入MQ之前服务调用多个服务都是同步调用,比如像这样:

服务A要顺序的调用B、C服务来完成业务逻辑如果A->B需要200ms,A->C需要200ms,再加上自身业务逻辑处理可能需要花费500ms,其中有400ms是调用A和B的花费,明明自身100ms就能处理完还白白浪费400ms,不能忍啊于是可以引入MQ做一下改造:

这下有了MQ,A服务只需要发一条消息比如花费50ms,再加上自身业务逻辑的100ms,那整个调用过程只需要花费150ms了,这样对并发和性能都有一定的改善。

突发流量削峰

突发流量就是互联网很常见的情况,有时候有热点、突发事件,那平常QPS为100的接口,突然提升10-20倍这个时候没有MQ所有流量直接进入服务,这对服务和数据库都是很大的挑战:

再次引入MQ就情况就不一样了,服务A先将请求丢给MQ,然后可以慢慢消费掉:

消息队列带来的一些问题

使用MQ还有很多好处,但是他也会带一些麻烦事。首先就是会降低系统的可用性,比如MQ挂了怎么办呢?所以在引入MQ之前就需要考虑之后带来的哪些问题,不能只看它的好处也需要考虑它不好的地方。比如下面列出的这些问题要如果解决:

  1. 如何保证消息队列的高可用?
  2. 如何保证消息不被重复消费?
  3. 如何保证消息不丢失?
  4. 如何保证消息的消费顺序?

下面我们来分析下这些问题。

如何保证消息队列的高可用?

如果是单机消息队列,一台机器挂了消息队列都就不用了,这是不能接受的,如果是一个消息队列群集,一台机器挂了还有其他机器能正常提供服务,所以要保证消息队列的高可用,我们就需要做消息队列集群。

以RabbitMQ为例它有两种集群模式:

  1. 普通模式
  2. 镜像模式

普通模式

普通模式,RabbitMQ会同步各个节点的数据/状态,但不包括消息队列,默认情况下,消息队列驻留在一个节点上,尽管它们在所有节点上都是可见且可访问的。

在这种模式下,每个节点都有会所有节点的元数据信息,所以当发送消息到队列时,无论连接的是哪一个节点都能正确的发送,但是节点只会同步其他节点的元数据,消息队列的数据还是在一个节点上,如果这个节点挂了那就意味着发消息就会失败,无法保证消息队列的高可用。

镜像模式

默认情况下,RabbitMQ中Queue与Binding、Exchange不一样,它只会存于声明队列的节点中,但是可以选择使Queue跨多个节点进行镜像。

每一个镜像队列由一个Master和一个或多个镜像组成,任何队列的的操作,都会先应用到Master节点上然后传播到多个镜像节点。如果Master节点挂了,最老的镜像节点将会成为新的Master节点。

总结

RabbitMQ有两种集群方法:普通模式镜像模式,要实现消息队列的高可用可以选一种合适的集群方式来达到,关于RabbitMQ的集群搭建方式,由于篇幅有限这里就不多说,可自行查看 Distributed RabbitMQ文章。

如何保证消息不被重复消费?

想象下消费者收到重复的消息会发生什么情况,比如订单支付消息,如果支付服务收到两条重复的消息让用户去支付两次,那用户肯定是不愿意的,明明已经支付过了还要支付。

如上图中第四步消费消息B的时候失败了,如果支付服务在做完业务之后,发送ACK之前服务挂了,MQ没有收到ACK,由于消息还存在队列中,服务恢复正常后会再次收到消息,如果支付不做检查那用户就会发生两次支付。

要避免这个重复消费的问题,可以在消费端引入内存、Redis、数据库来保存消息消费记录,根据消息Id来判断消息是否已经被消费过。

如何保证消息不丢失?

假设有订单服务和支付服务,正常流程是用户下单成功,然后向支付服务发送支付消息,这里面就涉及订单服务、支付服务、MQ的交互了,消息丢失可以分为三种情况:

  1. 生产者消息丢失
  2. MQ消息丢失
  3. 消费者消息丢失

生产者消息丢失

生产者消息丢失,可以使用本地消息表解决、消息确认/重发等方式来解决。以RabbitMQ为例,它有confirm机制,发出去的消息是否入队列,会使用回调的形式告知生产者,生产者收到消息后判断是Ack还是Nak,如果是Nak则重发消息。

此时还会有问题,如果极端情况下订单服务挂了,再次重启后消息就真丢失了,所以最好还是在生产中对消息做持久化,待订单服务恢复后使用Job重新发送消息。

MQ消息丢失

MQ消息丢失一般为未开启持久化,MQ挂了再次重启后消息丢失,所以应当将消息持久化到磁盘中。如果MQ收到消息后在同步到磁盘之前MQ挂了,那磁盘中也没有消息,这样还是会导致消息丢失消息,不过这只是小概率事件。

消费者消息丢失

消费者消息丢失,大都为开启了autoAck选项,消费者收到消息后还未完成处理,此时服务挂了,由于开启了autoAck, MQ会以为此消息已经被成功消费,将消息从队列中移除,而服务恢复过后也不会收到原来的消息了。

如果保证消息的消费顺序?

有些场景下要保持消息的顺序消费怎么办?比如写Log都是一条条打印出来,如果发到消息队列后出现消费顺序不一致那消息的那日志就会乱掉,给看日志的人带来不必要的麻烦。比如为了加快日志的处理速度使用三个消费都处理日志:

按图上的流程,消费者A、B、C可能分别消费日志1、2、3,这时候就无法保证消息的处理顺序。要保证消息的消费顺序,首先让消息都发送到同一个队列,然后使用一个消费者去处理消息:

这样消息的处理速度就大大降低,要保持消息的顺序,则又想让消息的处理速度不至于太慢,可以引用本地队列:

原文地址:https://www.cnblogs.com/liliuguang/p/11750654.html

时间: 2024-11-06 07:20:52

想使用消息队列,先考虑下这些问题的相关文章

RabbitMQ消息队列在PHP下的应用

消息队列的实现中,RabbitMQ以其健壮和可靠见长.公司的项目中选择了它作为消息队列的实现.关于MQ的机制和原理网上有很多文章可以看,这里就不再赘述,只讲几个比较容易混淆的问题 1,binding key和routing key binding key和routing key是都不过是自己设置的一组字符,只是用的地方不同,binding key是在绑定交换机和队列时候通过方法传递的字符串,routing key是在发布消息时候,顺便带上的字符串,有些人说这两个其实是一个东西,也对也不对,说对,

进程间通信——XSI IPC之消息队列

进程间通信XSI IPC有3种:消息队列.共享内存.信号量.它们之间有很多相似之处,但也有各自的特殊的地方.消息队列作为其中比较简单的一种,它会有些什么东西呢,来一起探讨探讨.. 消息队列结构 消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法. 每个数据块都被认为是一个类型,接受进程接收的数据块可以有不同的类型值. 我们可以通过发送消息来避免命名管道的同步和阻塞问题. 消息队列与管道不同的是,消息队列是基于消息的,而管道是基于字节流的,且消息队列的读取不一定是先入先出. 命名管道:

消息队列二三事

最近在看kafka的代码,就免不了想看看消息队列的一些要点:服务质量(QOS).性能.扩展性等等,下面一一探索这些概念,并谈谈在特定的消息队列如kafka或者mosquito中是如何具体实现这些概念的. 服务质量 服务语义 服务质量一般可以分为三个级别,下面说明它们不同语义. At most once 至多一次,消息可能丢失,但绝不会重复传输. 生产者:完全依赖底层TCP/IP的传输可靠性,不做特殊处理,所谓"发送即忘".kafka中设置acks=0. 消费者:先保存消费进度,再处理消

常用消息队列对比

作为中间件,消息队列是分布式应用间交换信息的重要组件.消息队列可驻留在内存或磁盘上, 队列可以存储消息直到它们被应用程序读走.通过消息队列,应用程序可以在不知道彼此位置的情况下独立处理消息,或者在处理消息前不需要等待接收此消息.所以消息队列可以解决应用解耦.异步消息.流量削锋等问题,是实现高性能.高可用.可伸缩和最终一致性架构中不可以或缺的一环.下面对消息队列就直接使用MQ表示. 现在比较常见的MQ产品主要是ActiveMQ.RabbitMQ.ZeroMQ.Kafka.MetaMQ.Rocket

mysql之消息队列

消息队列:在消息的传输过程中保存消息的容器. 消息队列管理器在将消息从它的源中继到它的目标时充当中间人.队列的主要目的是提供路由并保证消息的传递:如果发送消息时接收者不可用,消息队列会保留消息,直到可以成功地传递它. 如图所示: 在不使用消息队列的情况下,用户的请求数据直接写入数据库,再高并发的情况下,会对数据库造成巨的压力,同时也使得响应延迟加剧.在使用消息队列后,用户请求的数据发送给消息队列后立即返回,再由消息队列的消费者进程(通常情况下,该进程独立部署在专门的服务器集群上)从消息队列中获取

php 消息队列

转:php 消息队列 本消息队列用于linux下,进程通信 #根据路径和后缀创建一个id $key = ftok(__DIR__, 'R'); #获取队列中的消息 $q = msg_get_queue($key); #删除队列 msg_remove_queue($q); #获取队列的状态信息 $status = msg_stat_queue($q); print_r($status); echo "\n"; for($i=0;$i<100;$i++) { /** * 向队列里添加

mysql实现消息队列

mysql之消息队列 消息队列:在消息的传输过程中保存消息的容器. 消息队列管理器在将消息从它的源中继到它的目标时充当中间人.队列的主要目的是提供路由并保证消息的传递:如果发送消息时接收者不可用,消息队列会保留消息,直到可以成功地传递它. 如图所示: 在不使用消息队列的情况下,用户的请求数据直接写入数据库,再高并发的情况下,会对数据库造成巨的压力,同时也使得响应延迟加剧.在使用消息队列后,用户请求的数据发送给消息队列后立即返回,再由消息队列的消费者进程(通常情况下,该进程独立部署在专门的服务器集

细说linux IPC(十):system V 消息队列

[版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet 或 .../gentleliu,文章仅供学习交流,请勿用于商业用途] system V消息队列和posix消息队列类似,linux系统这两种消息队列都支持.先来看一下system V消息队列相关操作及其函数. msgget()函数创建一个消息队列或打开一个消息队列. #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h&

消息队列 链接

NAMEmq_overview —— POSIX消息队列概述 DESCRIPTIONPOSIX消息队列允许进程以消息的形式交换数据.此API与System V消息队列(msgget(2),msgsnd(2),msgrcv(2)等)有明显不同,但做的事情差不多. 消息队列通过mq_open(3)创建和打开,此函数返回一个消息队列描述符(mqd_t),它用于之后的调用中引用打开的消息队列.每个消息队列由一个名字标识,该名字具有这样的格式/somename,亦即,一个空字符结尾,以斜线开头,最多跟着N