private static IConnection Connection = null;
/// <summary>
/// 发送消息到指定队列
/// </summary>
/// <param name="queueName">队列名称, 如果没有则自动创建</param>
/// <param name="obj">消息主体,建议使用Json</param>
/// <param name="TTL">过期时间,到时会转换到不带.TTL的队列中,在那个队列中消费处理</param>
public static void Send(RabbitQueue queueName, string Suffix, object obj, int TTL = 0)
{
try
{
Task.Run(delegate
{
Config config = new Rabbit.Config();
//从队列枚举的描述中获取队列名,并附加队列子名称
string QueueName_Receive = config.Rabbit_QueueNameFixed + queueName.GetDesc() + (string.IsNullOrEmpty(Suffix) ? string.Empty : "." + Suffix);
//判断当前消息是否要延时,如果延时则附加.TTL作为暂存队列
string QueueName = QueueName_Receive + (TTL == 0 ? string.Empty : ".TTL");
try
{
//将客户端和连接对象作为全局变量,只有在关闭了之后再重新连接,能有效的提高入站效率
if (Connection == null || !Connection.IsOpen) { Connection = Config.InitFactory().CreateConnection(); }
using (var channel = Connection.CreateModel())
{
//创建一个队列 队列名称
bool durable = true; //设置队列持久化 服务器重启时,该队列依然能够存活 如果使用Exchange转发器,则需要转发器也是支持本地化的
bool exclusive = false; //是否为当前连接的专用队列,在连接断开后,会自动删除该队列,生产环境中应该不会用到
bool autodelete = false; //当没有任何消费者使用时,自动删除该队列, 否,其实无所谓,因为一般下次添加队列或者处理队列的时候都会先创建队列
#region //设置消息的基本参数 持久化等
//设置性能
var properties = channel.CreateBasicProperties();
//设置消息持久化
//需要注意的是,将消息设置为持久化并不能完全保证消息不丢失。
//虽然他告诉RabbitMQ将消息保存到磁盘上,但是在RabbitMQ接收到消息和将其保存到磁盘上这之间仍然有一个小的时间窗口。
//RabbitMQ 可能只是将消息保存到了缓存中,并没有将其写入到磁盘上。持久化是不能够一定保证的,但是对于一个简单任务队列来说已经足够。
//如果需要消息队列持久化的强保证,可以使用publisher confirms
properties.Persistent = true;
//1:不持久化 2:持久化 这里指的是消息的持久化,配合channel(durable=true),queue(durable)可以实现,即使服务器宕机,消息仍然保留
properties.DeliveryMode = 2;
#endregion
if (TTL == 0) //如果不需要设置过期 则直接加入队列
{
channel.QueueDeclare(QueueName_Receive, durable, exclusive, autodelete, null);
}
else
{
string ExchangeName = QueueName + ".Exchange"; //转发器名称
string RouteKey = QueueName + ".RouteKey"; //转向路由名称
//如果要实现过期.. 实现原理是将消息设置过期时间之后放在队列中..而此队列一直没有消费者会去消费.. 让消息过期..变成死信(Dead Letter)
//消息会根据队列上配置的转发器Exchange 来转发消息.. 消息会被推送到其他绑定了此转发器的队列上.. 那个队列有实时消费者..这样就形成了消息的延时处理..延时的时长是灵活设置的
//需要设置过期..要创建对应的过期结构.
//将消息加入到一个会过期的队列中..本方法中将原队列名后缀加上.TTL.. 实际转发消息队列为枚举获取到的队列名,方便后期消费处理
//可以在队列上设置过期时间,参数名:x-message-ttl 单位毫秒 在下方的键值对Dic中设置
//也可以在消息上设置过期时间..更下方的properties 的 Expiration 属性..单位也是毫秒 在消息上设置过期时间更加灵活.. 消息会比较多个过期时间取最小值
//此处需要注意的是
Dictionary<string, object> dic = new Dictionary<string, object>();
dic.Add("x-dead-letter-exchange", ExchangeName);//过期消息转发器
dic.Add("x-dead-letter-routing-key", RouteKey);//过期消息转向路由相匹配routingkey
//创建ExChange转发器
//设置转发器也是可以本地化 不自动删除的
channel.ExchangeDeclare(exchange: ExchangeName, type: "direct", durable: true, autoDelete: false, arguments: null);
//声明虚假队列 此队列没有消费端 会按消息上设置的时间过期
channel.QueueDeclare(QueueName, durable, exclusive, autodelete, dic);
//通过当前 Channel 绑定QueryName Exchange 和 routingKey
channel.QueueBind(QueueName, ExchangeName, RouteKey);
//声明真实的队列
channel.QueueDeclare(QueueName_Receive, durable, exclusive, autodelete, null);
channel.QueueBind(QueueName_Receive, ExchangeName, RouteKey);
//设置消息的过期时间
properties.Expiration = TTL.ToString();
}
//内容
channel.BasicPublish("", QueueName, false, properties, obj.ToJson().ToBytes(Encoding.UTF8));
}
}
catch (System.Exception ex)
{
Console.WriteLine(ex);
Console.WriteLine("{0} - {1} - {2} - {3}", DateTime.Now.Format_yyyyMMddHHmmssfff(), QueueName, ex.Message, obj.ToJson());
}
});
}
catch (System.Exception ex)
{
System.Console.WriteLine(ex);
}
}