1 引言
- 什么是MQ
消息总线(Message Queue),是一种跨进程、异步的通信机制,用于上下游传递消息。由消息系统来确保消息的可靠传递。
- 常用的MQ中间件有哪些
RabbitMQ:实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件)。RabbitMQ:服务器是用Erlang语言编写的,而群集和故障转移是构建在开放电信平台框架上的。所有主要的编程语言均有与代理接口通讯的客户端库。
Kafka:一种高吞吐量的分布式发布订阅消息系统。
ActiveMQ、RocketMQ等
- 安装
安装地址:https://www.rabbitmq.com/install-windows.html
先下载Erlang语言的安装环境,再下Rabbit的服务版本
2 RabbitMQ基础部分
- 基本概念
Connect:是RabbitMQ的socket链接,它封装了socket协议相关部分逻辑。
Channel:是我们与RabbitMQ打交道的最重要的一个接口。如果每一次访问RabbitMQ都建立一个Connection,在消息量大的时候建立TCP Connection的开销将是巨大的,大减少了操作系统建立TCP connection的开销。
VirtualHost:管理各自的exchange,和bindings
Exchange:类似于数据通信网络中的交换机,提供消息路由策略。rabbitmq中,producer不是通过信道直接将消息发送给queue,而是先发送给Exchange。一个Exchange可以和多个Queue进行绑定,producer在传递消息的时候,会传递一个ROUTING_KEY,Exchange会根据这个ROUTING_KEY按照特定的路由算法,将消息路由给指定的queue。
Queue:用于存储消息队列,并将它们转发给消费者
Producer:生产者
consumer:消费者
- 工作模型
- 使用场景
应用解耦、异步、流量削锋、日志收集等等...
3 RabbitMQ核心部分
- 五大类型
helloWorld、work queues、publish/subscirbe(fanout)、routing(direct)、topic
- 代码示例
/**********准备工作:安装包RabbitMQ.Client************/
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using RabbitMQ.Client.Exceptions;
//创建连接对象
var factory = new ConnectionFactory()
{
UserName = "kkbm",
Password = "kkbm1234",
HostName = "172.18.10.189",
Port = 5672, //RabbitMQ默认的端口
};
using var conn = factory.CreateConnection();
var channel = conn.CreateModel();
//定义队列
channel.QueueDeclare("workqueue", true, false, false, null);
//发送消息
for (int i = 0; i < 10; i++)
{
var message = "发送消息:" + i;
var body = Encoding.UTF8.GetBytes(message);
var properties = channel.CreateBasicProperties();
properties.Persistent = true;
channel.BasicPublish(exchange: "",
routingKey: "workqueue",
basicProperties: properties,
body: body);
Thread.Sleep(i * 100);
}
/**********准备工作:安装包RabbitMQ.Client************/
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using RabbitMQ.Client.Exceptions;
//创建连接对象工厂
var factory = new ConnectionFactory()
{
UserName = "kkbm",
Password = "kkbm1234",
HostName = "172.18.10.189",
Port = 5672,
};
var conn = factory.CreateConnection();
var channel = conn.CreateModel();
//绑定队列
channel.QueueDeclare(queue: "workqueue",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);
channel.BasicQos(prefetchSize: 0, prefetchCount: 1, global: false);
var consumer = new EventingBasicConsumer(channel);
//接受消息
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body.ToArray());
WriteColorLine($"{DateTime.Now.ToString()}工作队列接收到的消息:【{message}】",ConsoleColor.Blue);
//消息确认
channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false);
};
channel.BasicConsume(queue: "workqueue",
autoAck: false,
consumer: consumer);
4 RabbitMQ进阶
- 消息丢失
1)、消息发出后,中途网络故障,服务器没收到;
2)、消息发出后,服务器收到了,还没持久化,服务器宕机;
解决方案:确认消息是否发到RabbitMQ Server,如果生产者收到ACK不需要处理,收到Nack需要重新发送消息。
- 消息重复
1)、消息消费成功,事务已提交,签收时结果服务器宕机或网络原因导致签收失败,消息状态会由unack转变为ready,重新发送给其他消费方;
2)、消息消费失败,由于retry重试机制,重新入队又将消息发送出去。
解决方案:消费者做好幂等性处理
- 消息积压
1)、消费方的服务挂掉,导致一直无法消费消息;
2)、消费方的服务节点太少,导致消费能力不足,从而出现积压,这种情况极可能就是生产方的流量过大导致。
解决方案:增加消费者节点;持久化到数据库后处理