一、前言
第一次接触消息队列,有很多不熟悉的地方,可能也有很多写的不对的,大家一起学习。
RabbitMQ是一个在AMQP基础上完成的,可复用的企业消息系统。
使用场景:在项目中,将一些无需即时返回且耗时的操作提取出来,进行了异步处理,而这种异步处理的方式大大的节省了服务器的请求响应时间,从而提高了系统的吞吐量。
二、名词解释
Exchange:消息交换机,它指定消息按照什么规则,路由到那个队列。(类型:Direct,Fanout,Topic)
Queue:消息队列载体,每个消息都会被投入到一个或者多个队列。
Binding:绑定,它的作用就是把exchange和queue按照路由规则绑定起来。
Routing Key:路由关键字,exchange会根据这个关键字进行消息投递。
producer:消息生成着,就是投递消息的程序。
consumer:消息消费着,就是接收消息的程序。
channel:消息通道,在客户端的每个连接里,可以建立多个channel,每个channel代表一个任务会话。
由exchange,queue,routingkey三个才能决定一个exchange到queue的唯一路线。
三、Exchange类型
类型有4种,direct,fanout,topic,headers。其中headers不常用,本篇不做介绍,其他三种类型,会做详细介绍。
Direct Exchange – 处理路由键。需要将一个队列绑定到交换机上,要求该消息与一个特定的路由键完全匹配。这是一个完整的匹配。如果一个队列绑定到该交换机上要求路由键 “dog”,则只有被标记为“dog”的消息才被转发,不会转发dog.puppy,也不会转发dog.guard,只会转发dog。
Fanout Exchange – 不处理路由键。你只需要简单的将队列绑定到交换机上。一个发送到交换机的消息都会被转发到与该交换机绑定的所有队列上。很像子网广播,每台子网内的主机都获得了一份复制的消息。Fanout交换机转发消息是最快的。
Topic Exchange – 将路由键和某模式进行匹配。此时队列需要绑定要一个模式上。符号“#”匹配一个或多个词,符号“*”匹配不多不少一个词。因此“audit.#”能够匹配到“audit.irs.corporate”,但是“audit.*” 只会匹配到“audit.irs”。我在RedHat的朋友做了一张不错的图,来表明topic交换机是如何工作的:
注:这种情况下队列会收到所有路由器中符合topic规则的消息
四、流程
发送端:
using RabbitMQ.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Client
{
class Send
{
static void Main(string[] args)
{
//1、创建连接实例
var factory = new ConnectionFactory()
{
HostName = "168.33.162.245",
UserName = "dirk",
Password = "wangjun1234"
};
//2、打开连接
using (var connection = factory.CreateConnection())
{
//3、打开一个通道,该通道可以用来发送和接收消息
using (var channel = connection.CreateModel())
{
//4、队列参数设置
channel.QueueDeclare(queue: "sqls",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);
//5、发送的消息
string message = "select * from dual where asdfsa";
var body = Encoding.UTF8.GetBytes(message);
//6、指定消息属性
var properties = channel.CreateBasicProperties();
//持久的
properties.Persistent = true;
properties.DeliveryMode = 2;
// 设置过期时间
properties.Expiration = "36000";
//7、发送消息
channel.BasicPublish(exchange: "topic",
routingKey: "sqls",
basicProperties: properties,
body: body);
Console.WriteLine(" Send {0}", message);
}
Console.WriteLine("消息发送成功!!!");
Console.ReadLine();
}
}
}
}
接收端:
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Receive
{
class Receive
{
static void Main(string[] args)
{
var factory = new ConnectionFactory() {
HostName = "168.33.162.245",
UserName = "dirk",
Password = "wangjun1234"
};
using (var connection = factory.CreateConnection())
{
using (IModel channel = connection.CreateModel())
{
channel.QueueDeclare(queue: "sql",
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);
Console.WriteLine(" [x] Received {0}", message);
Thread t = new Thread(new ParameterizedThreadStart(Listener));
t.Start(new par { channel = channel, ea = ea });
};
channel.BasicConsume(queue: "sql",
noAck: false,
consumer: consumer);
Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
}
}
}
private static void Listener(object p)
{
par pa = p as par;
Thread.Sleep(10000);
//pa.channel.BasicAck(deliveryTag: pa.ea.DeliveryTag, multiple: false);
pa.channel.BasicReject(deliveryTag: pa.ea.DeliveryTag, requeue: false);
}
private class par
{
public IModel channel { get; set; }
public BasicDeliverEventArgs ea { get; set; }
}
}
}