生产者端代码
首先用Visual Studio 2015 创建一个控制台程序,添加如下代码
ConnectionFactory factory = new ConnectionFactory()
{
HostName = "192.168.3.101",
VirtualHost = "/",
UserName = "admin",
Password = "admin"
};
using (var conn = factory.CreateConnection())
{
using (IModel channel = conn.CreateModel())
{
//声名交换
channel.ExchangeDeclare("textexchange", ExchangeType.Direct, durable: true, autoDelete: false, arguments: null);
//声名队列
channel.QueueDeclare("testqueue", durable: true, exclusive: false, autoDelete: false, arguments: null);
//绑定队列
channel.QueueBind("testqueue", "textexchange", routingKey: "routekey");
var prop = channel.CreateBasicProperties();
//2.表示持久化消息
prop.DeliveryMode = 2;
var body = Encoding.UTF8.GetBytes("生产第一条消息");
channel.BasicPublish(exchange: "textexchange", routingKey: "routekey", basicProperties: prop, body: body);
}
}
消费者端代码
消费者端我们采用订阅获取消息模式,首先创建Window服务,创建服务过程不在赘述。
public partial class MQService : ServiceBase
{
private static ILog log = LogManager.GetLogger(typeof(MQService));
IConnection conn;
IModel channel;
private const ushort qosCount = 100;
public MQService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
StartRabbitMQ();
}
private bool StartRabbitMQ()
{
try
{
ConnectionFactory factory = new ConnectionFactory()
{
HostName = "192.168.3.101",
VirtualHost = "/",
UserName = "admin",
Password = "admin",
RequestedHeartbeat = 60, //健康检查时间间隔
AutomaticRecoveryEnabled = true, //是否开启自动重连
NetworkRecoveryInterval = TimeSpan.FromSeconds(20) //网络恢复时间间隔
};
conn = factory.CreateConnection();
channel = conn.CreateModel();
channel.ExchangeDeclare("textexchange", ExchangeType.Direct, durable: true, autoDelete: false, arguments: null);
channel.QueueDeclare("testqueue", durable: true, exclusive: false, autoDelete: false, arguments: null);
channel.QueueBind("testqueue", "textexchange", "routekey");
channel.BasicQos(0, qosCount, false);
var consumer = new EventingBasicConsumer(channel);
channel.BasicConsume("testqueue", noAck: false, consumer: consumer);
consumer.Received += Consumer_Received;
return true;
}
catch (Exception ex)
{
CloseConnection();
log.Error("工作队列声名失败:" + ex.Message + "|" + ex.StackTrace);
return false;
}
}
private void Consumer_Received(object sender, BasicDeliverEventArgs e)
{
Task.Run(() => SendMessage(e));
}
public void SendMessage(BasicDeliverEventArgs e)
{
var message = Encoding.UTF8.GetString(e.Body);
MessageSub sub = null;
try
{
sub = JsonConvert.DeserializeObject<MessageSub>(message);
}
catch (Exception ex)
{
channel.BasicAck(e.DeliveryTag, false);
log.Error("消息反序列化失败:" + ex.Message + "|" + message);
return;
}
try
{
//currentService.Push(sub); 这里写业务逻辑
channel.BasicAck(e.DeliveryTag, false);
log.Info("消费成功");
}
catch (Exception ex)
{
channel.BasicAck(e.DeliveryTag, false);
log.Error("消息发送失败" + ex.Message);
}
}
protected override void OnStop()
{
CloseConnection();
log.Info("服务关闭");
}
private void CloseConnection()
{
if (channel != null)
{
if (channel.IsOpen)
{
channel.Close();
}
channel.Dispose();
}
if (conn != null)
{
if (conn.IsOpen)
{
conn.Close();
}
conn.Dispose();
}
channel = null;
conn = null;
}
}
注意事项
1、消息持久化必须将exchange、queue和message全部设置为持久化,否则服务器重启消息会丢失。
2、因为本程序使用Window服务来处理消息,遇到问题时排查不太方便所以一定要多记录一些日志,以便快速的解决问题,本程序日志框架使用Log4net。
3、BasicQos为流量控制,在开启消费端确认消息的情况下,根据当前消费端的处理能力来设置大小。
4、本程序推送消息使用Task.Run()方法实现多线程推送,由于是demo程序,并没有控制线程数量,如果需要控制线程数量,可以使用TaskScheduler任务调度类实现。
耗时操作
假如用MQ推送邮件的情况下,发一份邮件很有可能耗时1至2秒钟,这种情况单消费端来发送邮件很明显效率低下,所以我们应该使用多个消费端,并且每个消费端可以多任务并行发送,可以有效的提高推送效率。如下图
-------------------------------------------------
技术交流QQ群:588273396
-------------------------------------------------