RabbitMq入门与基本使用
这两天工作项目中用到了rabbitmq,顺便学习了一下。
RabbitMq主要的使用模式有三种:工作队列,发布订阅和RPC远程调用。
1.工作队列
生产者:
using System;
using RabbitMQ.Client;
using System.Text; class NewTask
{
public static void Main(string[] args)
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using(var connection = factory.CreateConnection())
using(var channel = connection.CreateModel())
{
//一定要声明队列,向队列发送消息
channel.QueueDeclare(queue: "task_queue",
durable: true, //队列是否持久化
exclusive: false,
autoDelete: false,
arguments: null); var message = GetMessage(args);
var body = Encoding.UTF8.GetBytes(message); var properties = channel.CreateBasicProperties();
properties.SetPersistent(true); //消息是否持久化 channel.BasicPublish(exchange: "", //没有定义exchange,会使用系统默认的exchange
routingKey: "task_queue",
basicProperties: properties,
body: body);
Console.WriteLine(" [x] Sent {0}", message);
} Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
} private static string GetMessage(string[] args)
{
return ((args.Length > ) ? string.Join(" ", args) : "Hello World!");
}
}
在方法
channel.BasicPublish("", "task_queue", null, bytes);
消费者:
using System;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System.Text;
using System.Threading; class Worker
{
public static void Main()
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using(var connection = factory.CreateConnection())
using(var channel = connection.CreateModel())
{
channel.QueueDeclare(queue: "task_queue",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null); //修改分发机制(原先是轮询分发), prefetchCount = 1 变为 不向正在处理的worker发发任务,谁先有空就给谁
//In order to defeat that we can use the basicQos method with the prefetchCount = 1 setting.
//This tells RabbitMQ not to give more than one message to a worker at a time.
//Or, in other words, don't dispatch a new message to a worker until it has processed and acknowledged the previous one.
//Instead, it will dispatch it to the next worker that is not still busy.
channel.BasicQos(prefetchSize: , prefetchCount: , global: false); Console.WriteLine(" [*] Waiting for messages."); 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); int dots = message.Split('.').Length - ;
Thread.Sleep(dots * ); Console.WriteLine(" [x] Done");
//当noAck为false起作用,手动告知应答处理完成
channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false);
};
channel.BasicConsume(queue: "task_queue",
noAck: false, //是否不要手动应答(no manual Ack),ture自动应答,自动删除处理消息;false手动应答,服务器的消息会等待应答结果才消除
consumer: consumer); Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
}
}
}
这里要注意的,如果没有宿主进程,比如一个Console的后台程序,这个 Console.ReadLine(); 不能少,而且一定要加在这里。否则:
1.程序自动退出。2.相关的变量出了生命周期范围,已经释放!笔者在这里吃过亏,找了半天才发现。
2.发布订阅
Exchange类型为四种:direct,fanout,topic,headers。此模式中,由于是通过exchange和routingkey发送给多个队列,所以Publish中不用声明队列,只需声明exchange。
1、Routing - Exchange类型direct
他是根据交换器名称与routingkey来找队列的。
Publish:
using System;
using System.Linq;
using RabbitMQ.Client;
using System.Text; class EmitLogDirect
{
public static void Main(string[] args)
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using(var connection = factory.CreateConnection())
using(var channel = connection.CreateModel())
{
channel.ExchangeDeclare(exchange: "direct_logs",
type: "direct"); var severity = (args.Length > ) ? args[] : "info";
var message = (args.Length > )
? string.Join(" ", args.Skip( ).ToArray())
: "Hello World!";
var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(exchange: "direct_logs",
routingKey: severity, //传来参数,指定的routekey
basicProperties: null,
body: body);
Console.WriteLine(" [x] Sent '{0}':'{1}'", severity, message);
} Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
}
}
subscribe
using System;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System.Text; class ReceiveLogsDirect
{
public static void Main(string[] args)
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using(var connection = factory.CreateConnection())
using(var channel = connection.CreateModel())
{
channel.ExchangeDeclare(exchange: "direct_logs",
type: "direct");
var queueName = channel.QueueDeclare().QueueName; if(args.Length < )
{
Console.Error.WriteLine("Usage: {0} [info] [warning] [error]",
Environment.GetCommandLineArgs()[]);
Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
Environment.ExitCode = ;
return;
} //同时绑定多个指定的routekey
foreach(var severity in args)
{
channel.QueueBind(queue: queueName,
exchange: "direct_logs",
routingKey: severity);
} Console.WriteLine(" [*] Waiting for messages."); var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
var routingKey = ea.RoutingKey;
Console.WriteLine(" [x] Received '{0}':'{1}'",
routingKey, message);
};
channel.BasicConsume(queue: queueName,
noAck: true,
consumer: consumer); Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
}
}
}
2、Publish/Subscribe - Exchange类型fanout
这个类型忽略Routingkey,他为广播模式。
广播式时,Publish可以不指定queue和routekey。
using System;
using RabbitMQ.Client;
using System.Text; class EmitLog
{
public static void Main(string[] args)
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using(var connection = factory.CreateConnection())
using(var channel = connection.CreateModel())
{
channel.ExchangeDeclare(exchange: "logs", type: "fanout"); var message = GetMessage(args);
var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(exchange: "logs",
routingKey: "",
basicProperties: null,
body: body);
Console.WriteLine(" [x] Sent {0}", message);
} Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
} private static string GetMessage(string[] args)
{
return ((args.Length > )
? string.Join(" ", args)
: "info: Hello World!");
}
}
subscribe可以只用临时队列接收
using System;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System.Text; class ReceiveLogs
{
public static void Main()
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using(var connection = factory.CreateConnection())
using(var channel = connection.CreateModel())
{
channel.ExchangeDeclare(exchange: "logs", type: "fanout"); //这里生成了一个随机队列(string queue = "", bool durable = false, bool exclusive = true, bool autoDelete = true)
//In the .NET client, when we supply no parameters to queueDeclare() we create a non-durable, exclusive,
//autodelete queue with a generated name:
var queueName = channel.QueueDeclare().QueueName;
channel.QueueBind(queue: queueName,
exchange: "logs",
routingKey: ""); Console.WriteLine(" [*] Waiting for logs."); var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
Console.WriteLine(" [x] {0}", message);
};
channel.BasicConsume(queue: queueName,
noAck: true,
consumer: consumer); Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
}
}
}
有一种简写的方式,用Subscription类
/// <summary>
/// 获取消息并处理
/// </summary>
/// <param name="queueName">队列名称</param>
/// <param name="action">接收到消息后的Action</param>
public void Receive(string queueName, Action<byte[]> action, bool multThread = true)
{
ConnectionFactory cf = new ConnectionFactory();
cf.UserName = this.UserName;
cf.Password = this.PassWord;
cf.HostName = this.HostName;
cf.Port = this.Port;
cf.VirtualHost = this.VitualHost; using (IConnection conn = cf.CreateConnection())
{
using (IModel ch = conn.CreateModel())
{
//声明交换器
ch.ExchangeDeclare(exchange: "e_linke1", type: "direct",durable: false);
ch.QueueDeclare(queue: queueName,
durable: false,
exclusive: false,
autoDelete: false,
arguments: null); //将队列绑定到交换器上
ch.QueueBind(queue: queueName,
exchange: "e_linke1",
routingKey: "elk");
using (Subscription sub = new Subscription(ch, queueName, true))
{
foreach (BasicDeliverEventArgs e in sub)
{
// handle the message contained in e ...
// ... and finally acknowledge it
if (multThread)
{
System.Threading.Tasks.Task.Factory.StartNew(() => { action(e.Body); });
}
else
{
action(e.Body);
} sub.Ack(e);
}
}
}
}
}
注:
如果有两个接收程序都是用了同一个的queue和相同的routingKey去绑定direct exchange的话,分发的行为是负载均衡的,也就是说第一个是程序1收到,第二个是程序2收到,以此类推。
如果有两个接收程序用了各自的queue,但使用相同的routingKey去绑定direct exchange的话,分发的行为是复制的,也就是说每个程序都会收到这个消息的副本。行为相当于fanout类型的exchange。
3、Exchange类型topic
这个类型的路由规则如果你掌握啦,那是相当的好用,与灵活。他是根据RoutingKey的设置,来做匹配的,其中这里还有两个通配符为:
*,代表任意的一个词。例如topic.zlh.*,他能够匹配到,topic.zlh.one ,topic.zlh.two ,topic.zlh.abc, ....
#,代表任意多个词。例如topic.#,他能够匹配到,topic.zlh.one ,topic.zlh.two ,topic.zlh.abc, ....
4、Headers Exchange
Headers类型的exchange使用的比较少,它也是忽略routingKey的一种路由方式。是使用Headers来匹配的。Headers是一个键值对,可以定义成Hashtable。发送者在发送的时候定义一些键值对,接收者也可以再绑定时候传入一些键值对,两者匹配的话,则对应的队列就可以收到消息。匹配有两种方式all和any。这两种方式是在接收端必须要用键值"x-mactch"来定义。all代表定义的多个键值对都要满足,而any则代码只要满足一个就可以了。之前的几种exchange的routingKey都需要要字符串形式的,而headers exchange则没有这个要求,因为键值对的值可以是任何类型。代码示例如下:
发送端:
channel.ExchangeDeclare("X1", "headers"); IBasicProperties properties = channel.CreateBasicProperties();
properties.Headers = new Hashtable();
properties.Headers.Add("Key1", );
properties.Headers.Add("Key2", ); XmlSerializer xs = new XmlSerializer(typeof(RequestMessage));
MemoryStream ms = new MemoryStream();
xs.Serialize(ms, message);
byte[] bytes = ms.ToArray(); channel.BasicPublish("X1", "", properties, bytes);
接收端:
channel.ExchangeDeclare("X1", "headers");
//随机创建一个队列
string queue_name = channel.QueueDeclare("headerssubscriber2", true, false, false, null);
//绑定
IDictionary ht = new Hashtable();
ht.Add("x-match", "any");
ht.Add("Key1", );
ht.Add("Key2", );
channel.QueueBind(queue_name, "X1", "", ht);
//定义这个队列的消费者
QueueingBasicConsumer consumer = new QueueingBasicConsumer(channel);
channel.BasicConsume(queue_name, true, consumer); while (true)
{
BasicDeliverEventArgs ea =
(BasicDeliverEventArgs)consumer.Queue.Dequeue(); byte[] bytes = ea.Body; XmlSerializer xs = new XmlSerializer(typeof(RequestMessage));
using (MemoryStream ms = new MemoryStream(bytes))
{
RequestMessage message = (RequestMessage)xs.Deserialize(ms);
Console.WriteLine("Receive a Message, Id:" + message.MessageId + " Message:" + message.Message);
}
}
3.RPC远程调用
参考链接:
.Net下RabbitMQ的使用(4) -- 订阅和发布 *
.Net下RabbitMQ的使用(7) -- 消息的传输控制 *
RabbitMQ Tutorials [官网]
RabbitMq入门与基本使用的更多相关文章
- 2.RABBITMQ 入门 - WINDOWS - 生产和消费消息 一个完整案例
关于安装和配置,见上一篇 1.RABBITMQ 入门 - WINDOWS - 获取,安装,配置 公司有需求,要求使用winform开发这个东西(消息中间件),另外还要求开发一个日志中间件,但是也是要求 ...
- RabbitMQ入门-从HelloWorld开始
从读者的反馈谈RabbitMQ 昨天发完<RabbitMQ入门-初识RabbitMQ>,我陆陆续续收到一些反馈.鉴于部分读者希望结合实例来讲 期待下篇详细,最好结合案例.谢谢! 哪都好,唯 ...
- RabbitMQ入门-高效的Work模式
扛不住的Hello World模式 上篇<RabbitMQ入门-从HelloWorld开始>介绍了RabbitMQ中最基本的Hello World模型.正如其名,Hello World模型 ...
- RabbitMQ入门-消息订阅模式
消息派发 上篇<RabbitMQ入门-消息派发那些事儿>发布之后,收了不少反馈,其中问的最多的还是有关消息确认以及超时等场景的处理. 楼主,有遇到消费者后台进程不在,但consumer连接 ...
- RabbitMQ入门-Topic模式
上篇<RabbitMQ入门-Routing直连模式>我们介绍了可以定向发送消息,并可以根据自定义规则派发消息.看起来,这个Routing模式已经算灵活的了,但是,这还不够,我们还有更加多样 ...
- RabbitMQ入门与使用篇
介绍 RabbitMQ是一个由erlang开发的基于AMQP(Advanced Message Queue)协议的开源实现.用于在分布式系统中存储转发消息,在易用性.扩展性.高可用性等方面都非常的优秀 ...
- [转]RabbitMQ入门教程(概念,应用场景,安装,使用)
原文地址:https://www.jianshu.com/p/dae5bbed39b1 RabbitMQ 简介 RabbitMQ是一个在AMQP(Advanced Message Queuing Pr ...
- RabbitMQ 入门指南——安装
RabbitMQ好文 Rabbitmq Java Client Api详解 tohxyblog-博客园-rabbitMQ教程系列 robertohuang-CSDN-rabbitMQ教程系列 Rabb ...
- RabbitMQ入门:总结
随着上一篇博文的发布,RabbitMQ的基础内容我也学习完了,RabbitMQ入门系列的博客跟着收官了,以后有机会的话再写一些在实战中的应用分享,多谢大家一直以来的支持和认可. RabbitMQ入门系 ...
- RabbitMQ入门:主题路由器(Topic Exchange)
上一篇博文中,我们使用direct exchange 代替了fanout exchange,这次我们来看下topic exchange. 一.Topic Exchange介绍 topic exchan ...
随机推荐
- Android四大组件全然解析(一)---Activity
本文參考\android\android\frameworks\base\core\java\android\app\Activity.java文件里的类凝视.以及android/frameworks ...
- 基于jQuery左侧小图滚动右侧大图显示代码
今天给大家分享一款 jQuery左侧小图滚动右侧大图显示代码是一款基于jQuery实现的左侧滚动图片点击大图查看效果代码.该实例适用浏览器:IE8.360.FireFox.Chrome.Safari. ...
- Odoo 8.0 new API 概述
相对于7来说,8的api改进了不少,用官方的话来说就是更加面向对象了. 下面探究一下具体的改动. 准备知识:python装饰器的使用 http://blog.csdn.net/thy38/articl ...
- vsftpd 服务移植出现 500 oops : socket 解决
一开始, 在vsftpd 打印的错误是 500 oops : socket 在 vsftpd 源码里面找到 buildroot-2016.05/output/build/vsftpd-3.0.3/sy ...
- [usb]usb otg和host
USB OTG 设备既能做主机,又能做设备.USB HOST是指主机.当OTG 插到 HOST 上,OTG 的角色 就是 device.当device 插到 OTG 上,OTG 的角色就是 HOST. ...
- Unity3D 5.0版本+注册工具分享
Unity3D引擎5.0正式版本发布也有一段时间了.笔者今天下载了新版本顺便分享一下资源. 主要有两个资源,一个是5.0f4的官方客户端,另外一个是vs的调试插件.有需要的盆友就拿去.都在下面的连接地 ...
- Android——android weight 属性(百度)
LinearLayout 在androidUI布局中使用非常多,它其中有个很方便又很有意思的属性 weight ,这个属性理解起来不是那么简单的,而真正理解了又觉得非常简单! 下面就通过一个例子来说明 ...
- m4--宏处理器
m4 是 POSIX 标准中的一部分,所有版本的 UNIX 下都可用.虽然这种语言可以单独使用,但大多数人需要 m4 仅仅是因为 GNU autoconf 中的 “configure” 脚本依赖它.宏 ...
- jrtplib编译指南
The library offers support for the Real-time Transport Protocol (RTP), The library uses the JThread ...
- dp + 状态压缩 - Codeforces 580D Kefa and Dishes
Kefa and Dishes Problem's Link Mean: 菜单上有n道菜,需要点m道.每道菜的美味值为ai. 有k个规则,每个规则:在吃完第xi道菜后接着吃yi可以多获得vi的美味值. ...