1.什么是MQ?

消息总线(Message Queue),是一种跨进程、异步的通信机制,用于上下游传递消息。由消息系统来确保消息的可靠传递。

2.MQ是干什么用的?

应用解耦、异步、流量削锋、数据分发、错峰流控、日志收集等

3.MQ衡量标准

1.服务性能

2.数据存储

3.集群架构

主流竞品分析
当前市面上mq的产品很多,比如RabbitMQ、Kafka、ActiveMQ、ZeroMQ和阿里巴巴捐献给Apache的RocketMQ。甚至连redis这种NoSQL都支持MQ的功能。
ActiveMQ
ActiveMQ是apache出品,最流行的,能力强劲的开源消息总线,并且它一个完全支持JMS规范的消息中间件。其丰富的API、多种集群构建模式使得它成为业界老牌消息中间件,在中小型企业中应用广泛。
但是其性能稍差,在面对高并发的情况下,会出现消息阻塞、堆积、延迟等问题。
默认采用了基于内存的kahaDB进行存储,如果需要保证消息的可靠性,也可以选择关系行数据库进行存储。
集群架构模式如下:




java rabbitmq 连接不上 rabbitmq 连接数限制_数据


Master-Slave模式:通过zookeeper对主从进行管理,正常情况下,从节点不会提供服务。当主节点出现问题后,zookeeper会高效的将主节点下掉,从节点来提供服务。
NetWork模式:两套主从Master-Slave节点。由网络联通,将其变为分布式的集群架构。
Kafka
Kafka是LinkedIn开源的分布式发布-订阅消息系统,目前归属于Apache顶级项目。Kafka主要特点就是基于Pull的模式来处理消息消费追求高吞吐量,一开始的目的就是用于日志收集和传输。0.8版本开始支持复制,不支持事务,对消息的重复、丢失、错误没有严格要求,适合产生大量数据的互联网服务的数据收集业务。能够支持廉价的服务器上以每秒100k条数据的吞吐量。(有ack机制,可以保证不丢失,不能保证不重复。)
高效的读写基于操作系统低层的Page Cache。仅仅使用内存管理,不存在内存和磁盘之间的IO操作。
集群架构模式如下:


java rabbitmq 连接不上 rabbitmq 连接数限制_消息队列_02


通过replicate进行节点间数据的复制,尽量保证数据的可靠性。

RocketMQ
RocketMQ是阿里开源的消息中间件,目前也已经孵化为Apache顶级项目,它是纯Java开发,具有高吞吐量、高可靠性、适合大规模分布式系统应用的特点。RocketMQ思路起源于Kafka,它对消息的可靠传输以及事务性做了优化,目前在阿里集团被广泛应用于交易、充值、流计算、消息推送、日志流式处理、binlog分发等场景。
在2.0版本,RocketMQ集群也是通过Zookeeper进行管理。在3.0之后,放弃Zookeeper,使用NameServer进行集群的管理和协调。
能够保障消息的顺序消费,提供了丰富的消息拉取等处理模式,消费者可以高效进行水平扩展,能够承载上亿级别数据量级。
可以支持多种集群架构模式:Master-Slave模式、双Master-Slave模式、多主多从模式等等。
支持多种刷盘策略:同步双写、异步复制。借助了零拷贝等技术。
集群架构模式如下:


java rabbitmq 连接不上 rabbitmq 连接数限制_java rabbitmq 连接不上_03


java rabbitmq 连接不上 rabbitmq 连接数限制_服务器_04


技术背景知识介绍
AMQP高级消息队列协议

AMQP(Advanced Message Queuing Protocol)高级消息队列协议:

高级消息队列协议。它是应用层协议的一个开放标准,为面向消息的中间件设计,基于此协议的客户端与消息中间件可传递消息,并不受产品、开发语言等条件的限制。
AMQP中消息的路由过程和JMS存在一些差别。AMQP中增加了Exchange和Binging的角色。生产者把消息发布到Exchange上,消息最终到达队列并被消费者接收,而Binding决定交换器的消息应该发送到哪个队列。


java rabbitmq 连接不上 rabbitmq 连接数限制_消息队列_05


AMQP消息路由过程
Erlang语言Erlang语言最初用于交换机领域的架构模式,这样使得RabbitMQ在Broker之间进行数据交互的性能非常优秀(Erlang有着和原生Socket一样的延迟)。
RabbitMQ
RabbitMQ是一个开源的消息代理和队列服务器,用来通过普通协议在不同的应用之间共享数据(跨平台跨语言)。RabbitMQ是使用Erlang语言编写,并且基于AMQP协议实现。
RabbitMQ的优势:

  • 可靠性(Reliablity):使用了一些机制来保证可靠性,比如持久化、传输确认、发布确认。
  • 灵活的路由(Flexible Routing):在消息进入队列之前,通过Exchange来路由消息。对于典型的路由功能,Rabbit已经提供了一些内置的Exchange来实现。针对更复杂的路由功能,可以将多个Exchange绑定在一起,也通过插件机制实现自己的Exchange。
  • 消息集群(Clustering):多个RabbitMQ服务器可以组成一个集群,形成一个逻辑Broker。
  • 高可用(Highly Avaliable Queues):队列可以在集群中的机器上进行镜像,使得在部分节点出问题的情况下队列仍然可用。
  • 多种协议(Multi-protocol):支持多种消息队列协议,如STOMP、MQTT等。
  • 多种语言客户端(Many Clients):几乎支持所有常用语言,比如Java、.NET、Ruby等。
  • 管理界面(Management UI):提供了易用的用户界面,使得用户可以监控和管理消息Broker的许多方面。
  • 跟踪机制(Tracing):如果消息异常,RabbitMQ提供了消息的跟踪机制,使用者可以找出发生了什么。
  • 插件机制(Plugin System):提供了许多插件,来从多方面进行扩展,也可以编辑自己的插件。

RabbitMQ的整体架构


java rabbitmq 连接不上 rabbitmq 连接数限制_rabbitmq连接数一直增加_06


消息流转


java rabbitmq 连接不上 rabbitmq 连接数限制_服务器_07


RabbitMQ各组件功能


java rabbitmq 连接不上 rabbitmq 连接数限制_数据_08


  • Broker:标识消息队列服务器实体.
  • Virtual Host:虚拟主机。标识一批交换机、消息队列和相关对象。虚拟主机是共享相同的身份认证和加密环境的独立服务器域。每个vhost本质上就是一个mini版的RabbitMQ服务器,拥有自己的队列、交换器、绑定和权限机制。vhost是AMQP概念的基础,必须在链接时指定,RabbitMQ默认的vhost是 /。
  • Exchange:交换器,用来接收生产者发送的消息并将这些消息路由给服务器中的队列。
  • Queue:消息队列,用来保存消息直到发送给消费者。它是消息的容器,也是消息的终点。一个消息可投入一个或多个队列。消息一直在队列里面,等待消费者连接到这个队列将其取走。
  • Banding:绑定,用于消息队列和交换机之间的关联。一个绑定就是基于路由键将交换机和消息队列连接起来的路由规则,所以可以将交换器理解成一个由绑定构成的路由表。
  • Channel:信道,多路复用连接中的一条独立的双向数据流通道。信道是建立在真实的TCP连接内地虚拟链接,AMQP命令都是通过信道发出去的,不管是发布消息、订阅队列还是接收消息,这些动作都是通过信道完成。因为对于操作系统来说,建立和销毁TCP都是非常昂贵的开销,所以引入了信道的概念,以复用一条TCP连接。
  • Connection:网络连接,比如一个TCP连接。
  • Publisher:消息的生产者,也是一个向交换器发布消息的客户端应用程序。
  • Consumer:消息的消费者,表示一个从一个消息队列中取得消息的客户端应用程序。
  • Message:消息,消息是不具名的,它是由消息头和消息体组成。消息体是不透明的,而消息头则是由一系列的可选属性组成,这些属性包括routing-key(路由键)、priority(优先级)、delivery-mode(消息可能需要持久性存储[消息的路由模式])等。

Kafka、RocketMQ、RabbitMQ比较


java rabbitmq 连接不上 rabbitmq 连接数限制_java rabbitmq 连接不上_09


mq消息堆积处理

1.大量消息在mq里积压

场景:几千万条数据在MQ里积压了七八个小时,从下午4点多,积压到了晚上很晚,10点多,11点多。线上故障了,这个时候要不然就是修复consumer的问题,让他恢复消费速度,然后傻傻的等待几个小时消费完毕。这个肯定不行。一个消费者一秒是1000条,一秒3个消费者是3000条,一分钟是18万条,1000多万条。
所以如果你积压了几百万到上千万的数据,即使消费者恢复了,也需要大概1小时的时间才能恢复过来。

解决方案:
这种时候只能操作临时扩容,以更快的速度去消费数据了。具体操作步骤和思路如下:
①先修复consumer的问题,确保其恢复消费速度,然后将现有consumer都停掉。

②临时建立好原先10倍或者20倍的queue数量(新建一个topic,partition是原来的10倍)。

③然后写一个临时分发消息的consumer程序,这个程序部署上去消费积压的消息,消费之后不做耗时处理,直接均匀轮询写入临时建好分10数量的queue里面。

④紧接着征用10倍的机器来部署consumer,每一批consumer消费一个临时queue的消息。

⑤这种做法相当于临时将queue资源和consumer资源扩大10倍,以正常速度的10倍来消费消息。

⑥等快速消费完了之后,恢复原来的部署架构,重新用原来的consumer机器来消费消息。

2.消息设置了过期时间,过期就丢了

假设你用的是rabbitmq,rabbitmq是可以设置过期时间的,就是TTL,如果消息在queue中积压超过一定的时间就会被rabbitmq给清理掉,这个数据就没了。那这就是第二个坑了。这就不是说数据会大量积压在mq里,而是大量的数据会直接搞丢。解决方案:
这种情况下,实际上没有什么消息挤压,而是丢了大量的消息。所以第一种增加consumer肯定不适用。
这种情况可以采取 “批量重导” 的方案来进行解决。
在流量低峰期(比如夜深人静时),写一个程序,手动去查询丢失的那部分数据,然后将消息重新发送到mq里面,把丢失的数据重新补回来。

3.积压消息长时间没有处理,mq放不下了

如果走的方式是消息积压在mq里,那么如果你很长时间都没处理掉,此时导致mq都快写满了,咋办?这个还有别的办法吗?解决方案:
这个就没有办法了,肯定是第一方案执行太慢,这种时候只好采用 “丢弃+批量重导” 的方式来解决了。

首先,临时写个程序,连接到mq里面消费数据,收到消息之后直接将其丢弃,快速消费掉积压的消息,降低MQ的压力,然后走第二种方案,在晚上夜深人静时去手动查询重导丢失的这部分数据。

案例:消息堆积的治理

单个队列中堆积10万条消息不会有丝毫影响,但超过1千万时,将引起严重问题,如内存、磁盘告警、connection阻塞等

可以使用Shovel,当某个队列消息堆积严重时,可以使用Shovel将队列中消息移交给另一个集群

1、当检测到集群cluster1中队列queue1中有严重消息堆积,如通过/api/queues/vhost/name获取消息个数messages超过2千万或者消息占用大小messages_bytes超过10GB时,启用shovel1将queue1中消息转发至备份集群cluster2中队列queue2
2、当检测到queue1中消息个数低于1百万或者消息大小低于1GB时,停止shovel1,让原本队列消化剩余堆积
3、当检测到queue1消息个数低于10万个或占用大小低于100MB时,开启shovel2将queue2队列中暂存的消息返还队列queue1
4、当检测到queue1消息个数超过1百万,或消息占用高于1GB时,将shovel2停掉


java rabbitmq 连接不上 rabbitmq 连接数限制_java rabbitmq 连接不上_10


Rabbitmq的数据迁移

启用shovel插件命令:

rabbitmq-plugins enable rabbitmq_shovel
rabbitmq-plugins enable rabbitmq_shovel_management


java rabbitmq 连接不上 rabbitmq 连接数限制_数据_11


1.同一个集群,同一个vhost直接迁移queue数据


java rabbitmq 连接不上 rabbitmq 连接数限制_数据_12


java rabbitmq 连接不上 rabbitmq 连接数限制_rabbitmq连接数一直增加_13


java rabbitmq 连接不上 rabbitmq 连接数限制_服务器_14


2.跨集群,跨vhost的数据迁移

RabbitMQ通过shovel插件迁移数据

生产环境中会遇到RabbitMQ数据迁移的场景,例如:切换云服务厂商、不同Region之间数据迁移、新搭建RabbitMQ实例,数据需要同步至新的RabbitMQ实例。

当业务需要可靠且连续地将消息从一个 broker 的 queue 里搬运(转发)到另一个 broker 的 exchange 时(最终达到某个 queue 里 )使用;作为 source 的 queue 和作为 destination 的 exchange 可以位于同一个 broker 上(通常要求处于不同的 vhost 下),也可以位于不同的 broker 上

前提条件:

源RabbitMQ实例打开了shovel插件。

目的RabbitMQ实例打开了shovel插件。

源实例与目的实例能够网络互通。

源mq上执行命令,将192.168.88.171:5560 omsapi_system 的vhost下的DownloadOrderHandle_D 队列数据迁移到 192.168.99.82:5550 omsapi_system 的vhost下

rabbitmqctl set_parameter shovel hidden_shovel '{"src-uri":"amqp://admin:differ888@192.168.88.171:5560/omsapi_system",
"src-queue":"DownloadOrderHandle_DL","dest-uri":"amqp://admin:differ888@192.168.99.82:5550/omsapi_system","dest-queue":"
DownloadOrderHandle_DL","prefetch-count":64,"reconnect-delay":5,"publish-properties":[],"add-forward-headers":true,"ack-mode":"on-confirm"}'

3.queue下个别会员号的message迁移

插件不支持,需要python将消息过滤出来写入需要导入的mq集群