C# .net 使用rabbitmq消息队列——EasyNetQ插件介绍
EasyNetQ 是一个简洁而适用的RabbitMQ .NET类库,本质上是一个在RabbitMQ.Client之上提供服务的组件集合。
应用使用rabbitmq需要经过总线接口IBus或者IAdvanceBus,大部分时候我们使用的是IBus,它提供了三种消息模式:Publish/Subscribe, Request/Response和 Send/Receive,可以满足大多数需求。
EasyNetQ规定,每一个你想发送的消息必须用一个class表示,简单说就是一个实体类。默认情况下,当你发布一个消息,EasyNetQ会检查消息类型,基于类型名称、命名空间和程序集创建交换机和队列,当然也可以使用指定的名称去创建交换机和队列,而消息默认使用Newtonsoft.Json 序列化成JSON格式,当然我们也可以替换成自己序列化方式,实现很简单,比如我们要使用xml格式序列化,因为EasyNetQ内部使用Ioc容器组织在一起,那么我们只需要实现ISerializer接口并注册到DI容器中即可。
1、EasyNetQ安装
使用Nuget搜索EasyNetQ直接安装即可:

2、EasyNetQ连接
using System;
namespace EasyNetQ.Demo
{
public class TextMessage
{
public string Text { get; set; }
}
class Program
{
static void Main(string[] args)
{
//方式1
var connectionString = "host=192.168.18.129;virtualHost=/;username=admin;password=123456;timeout=60";
var bus1 = RabbitHutch.CreateBus(connectionString);
//方式2
HostConfiguration host = new HostConfiguration();
host.Host = "192.168.18.129";
host.Port = 5672;
ConnectionConfiguration connection = new ConnectionConfiguration();
connection.Port = 5672;
connection.Password = "123456";
connection.UserName = "admin";
connection.VirtualHost = "/";
connection.Timeout = 60;
connection.Hosts = new HostConfiguration[] { host };
var bus2 = RabbitHutch.CreateBus(connection, services => { });
//使用bus实现业务
//关闭连接字需要调用bus的Dispose方法
Console.ReadKey();
}
}
}
其实EasyNetQ连接过程其实就是IBus的创建过程,IBus的创建有两种方式,一种是以连接字符串的形式,类似数据库的连接,一种是通过ConnectionConfiguration来创建,其实连接字符串最终也是构建这个对象来连接rabbitmq,而连接字符串的连接类型主要有一下几个:
host 这个字段是必选的。如要具体指定你要连接服务器端口,你用标准格式 host:port。假如你省略了端口号,AMQP默认端口是5672。如果连接到RabbitMQ集群,需要指定每一个集群节点用逗号(.)分隔
virtualhost 默认虚拟主机是’/’
username 默认是'guest'
password 默认为'guest'
requestedHearbeat 默认为10秒钟。没有心跳设置为0
prefetchcount 默认为50.这个值是在EasyNetQ发送ack之前发送给RabbitMQ的消息数。不限制设置为0(不推荐). 为了在消费者之间保持公平和平衡设置为1.
persistentMessages 默认为true。这个决定了在发送消息时采用什么样的delivery_mode。设置为true,RabbitMQ将会把消息持久化到磁盘,并且在服务器重启后仍会存在。设置为false可以提高性能收益。
timeout 模式值为10秒。不限制超时时间设置为0.当超时事时抛出System.TimeoutException.
关闭连接字需要调用Dispose方法即可,所以,在使用IBus时,切记不要无脑的使用using
3、EasyNetQ模式:Publish/Subscribe
using System;
namespace EasyNetQ.Demo
{
public class TextMessage
{
public string Text { get; set; }
}
class Program
{
static void Main(string[] args)
{
var connectionString = "host=192.168.18.129;virtualHost=/;username=admin;password=123456;timeout=60";
//订阅/Subscribe
{
var bus = RabbitHutch.CreateBus(connectionString);
bus.Subscribe<TextMessage>("subscriptionId", tm =>
{
Console.WriteLine("Recieve Message: {0}", tm.Text);
});
}
//发布/Publish
using (var bus = RabbitHutch.CreateBus(connectionString, registerServices => { }))
{
var input = "";
Console.WriteLine("Please enter a message. 'q'/'Q' to quit.");
while ((input = Console.ReadLine()).ToLower() != "q")
{
bus.Publish(new TextMessage
{
Text = input
});
}
}
}
}
}
这个例子是EasyNetQ三种模式中的Publish/Subscribe,上面的代码执行后,在rabbitmq中会创建一个名为 EasyNetQ.Demo.TextMessage, EasyNetQ.Demo 的交换机和一个名为 EasyNetQ.Demo.TextMessage, EasyNetQ.Demo_subscriptionId 的队列,当然,交换机和队列的名称也可以自定义。
发布订阅模式有主要有以下特点:
1、发布消息使用Publish方法
bus.Publish(new TextMessage
{
Text = input
});
一个消息类型对应一个交换机,如果交换机不存在,会自动创建一个topic类型的交换机,默认情况下,交换机名称为消息类型的全限定名(命名空间.类型,程序集名),例如上面的例子将会创建名为EasyNetQ.Demo.TextMessage, EasyNetQ.Demo 的交换机,当然,名称可以自定义。
2、订阅消息调用Subscribe方法
bus.Subscribe<TextMessage>("subscriptionId", tm =>
{
Console.WriteLine("Recieve Message: {0}", tm.Text);
});
每次调用Subscribe方法都会去创建一个新的队列(如果不存在), Subscribe方法有一个subscriptionId参数,默认情况下,队列名称为全限定名(命名空间.类型,程序集名)+subscriptionId,而且队列将绑定到对应的交换机,默认绑定的路由是#,即队列可以接受所有发布到交换机的消息,例如上面的例子会创建名为 EasyNetQ.Demo.TextMessage, EasyNetQ.Demo_subscriptionId 的队列,绑定的路由是#。队列名和路由均可自定义。
因为Subscribe方法的subscriptionId参数,在未明确指定队列的情况下,如果使用同一个subscriptionId参数,则表示从同一个队列消费,因此可以使用subscriptionId来区分队列。
注:因为队列是在Subscribe方法执行后创建的,如果未调用Subscribe方法,即队列还未创建,那么调用Publish方法时,只会生成一个交换机,而且发布的消息将会丢失。
3、队列名有两种方式自定义。
一种是使用QueueAttribute特性标识消息类型,指定队列名和交换机名,这个也是交换机自定义名称的方式
[Queue("my_queue_name", ExchangeName = "my_exchange_name")]
public class TextMessage
{
public string Text { get; set; }
}
当使用特性声明队列名称时,真实创建的队列名其实是:my_queue_name + subscriptionId,如上面的例子会创建名为my_queue_name_subscriptionId的队列。
另一种是在使用Subscribe方法订阅消息是指定,当在这里指定时,subscriptionId就无效了,生成的队列名就是自定义的名称,不带subscriptionId。
bus.Subscribe<TextMessage>("subscriptionId", tm =>
{
Console.WriteLine("Recieve Message: {0}", tm.Text);
}, cfg =>
{
cfg.WithQueueName("my_queue_name")//生成的队列名是my_queue_name,不带subscriptionId
.WithTopic("a#");//路由匹配以a开头的路由,WithTopic是可以多次调用,让队列可以以不同的路由绑定到交换机
});
另外,这种方式还可以指定队列绑定到交换机的路由,当订阅处指定路由后,需要在发布消息时也指定对应路由才能将消息发布到队列。
bus.Publish(new TextMessage
{
Text = input
}, "a#");//以a#为路由发布消息
或者
bus.Publish(new TextMessage
{
Text = input
}, cfg =>
{
cfg.WithTopic("a#");//以a#为路由发布消息
});
4、可以订阅消息,当然也可以取消订阅,当使用Subscribe订阅消息时,会返回一个ISubscriptionResult,取消订阅只需要调用ISubscriptionResult的Dispose方法即可
subscriptionResult.Dispose();
// 这个等价与 subscriptionResult.ConsumerCancellation.Dispose();
调用Dispose方法将停止EasyNetQ对队列的消费,并且关闭这个消费者的channel。
注意:IBus和IAndvancedBus的dispose,能够取消所有消费者,并关闭对RabbitMQ的连接。
5、消息的发布订阅也提供了异步操作,我们可以根据自己的需求来决定是否使用
//异步发布
bus.PublishAsync(new TextMessage
{
Text = input
});
//异步订阅
bus.SubscribeAsync<TextMessage>("subscriptionId", async tm =>
{
await Task.Run(() =>
{
Console.WriteLine("Recieve Message: {0}", tm.Text);
});
});
6、消息的发布与订阅过程不必再同一个项目中,也不需要使用同一个IBus,只需要注意发布订阅过程中的交换机和队列即可,因为默认情况下,交换机名和队列名都是根据消息类型生成,所以此时最好具体指明交换机名和队列名。
4、EasyNetQ模式:Request/Response
using System;
namespace EasyNetQ.Demo
{
public class MyRequest
{
public string Text { get; set; }
}
public class MyResponse
{
public string Text { get; set; }
}
class Program
{
static void Main(string[] args)
{
var connectionString = "host=192.168.18.129;virtualHost=/;username=admin;password=123456;timeout=60";
//响应/Respond
{
var bus = RabbitHutch.CreateBus(connectionString);
bus.Respond<MyRequest, MyResponse>(request =>
{
return new MyResponse { Text = "Respond: " + request.Text };
});
}//请求/Request
using (var bus = RabbitHutch.CreateBus(connectionString))
{
var input = "";
Console.WriteLine("Please enter a message. 'q'/'Q' to quit.");
while ((input = Console.ReadLine()).ToLower() != "q")
{
var response = bus.Request<MyRequest, MyResponse>(new MyRequest
{
Text = input
}, cfg => { });
Console.WriteLine(response.Text);
}
}
}
}
}
Request/Response模式类似于请求响应的过程——发送一个请求到服务器,服务器然后处理请求后返回一个响应。上述代码执行后,会在rabbitmq中创建一个名为easy_net_q_rpc的交换机,但是类型是direct,另外还会创建两个队列,名为EasyNetQ.Demo.MyRequest, EasyNetQ.Demo和easynetq.response.xxxxxxxxxxxxxxx。
请求响应模式的特点:
1、通过Request方法发送请求
var response = bus.Request<MyRequest, MyResponse>(new MyRequest
{
Text = input
}, cfg => { });
请求之后会创建一个名为easy_net_q_rpc的交换机(如果不存在),类型是direct,同时,当前线程会阻塞,等待消息被消费处理,或者等待一段时间后(如例子中连接字符串中的timeout=60),会抛出System.TimeoutException异常。另外这里还会生成一个easynetq.response.xxxxxxxxxxxxxxx的队列,这是一个auto-delete类型的队列,同时与当前连接绑定,当连接关闭后,这个队列就会自动删除,而且这个队列将会自动绑定到easy_net_q_rpc交换机,路由名就是队列名。。
2、通过Respond方法处理请求发送的消息
bus.Respond<MyRequest, MyResponse>(request =>
{
return new MyResponse { Text = "Respond: " + request.Text };
});
Respond方法执行后,会生成一个队列,队列名是请求消息的全限定名(命名空间.类型,程序集名),同时队列将自动绑定到easy_net_q_rpc交换机,路由名就是队列名,之后所有该请求类型的消息都将发送到这个队列。而且Respond方法也将从这个队列获取消息消费。另外,我们是可以指定这个队列名:
bus.Respond<MyRequest, MyResponse>(request =>
{
return new MyResponse { Text = "Respond: " + request.Text };
}, cfg =>
{
cfg.WithQueueName("my_queue_name");
});
只有当请求发送的消息被Respond方法中的处理过程处理后,Request才能得到结果继续执行,所以,尽可能的不要在Respond方法中写过多的耗时操作。
注:因为队列是在Respond方法执行后创建的,如果在未调用Respond方法时,即队列未创建,那么Request方法发送的消息将会丢失,而Request方法发送消息后将会造成线程阻塞,那么最终的结果就是一直等待到System.TimeoutException异常抛出。
3、Request方法和Respond方法都提供了异步操作
//异步请求
var task = bus.RequestAsync<MyRequest, MyResponse>(new MyRequest
{
Text = input
}, cfg => { });
task.Wait();
//异步响应
bus.RespondAsync<MyRequest, MyResponse>(async request =>
{
return await Task.Run(() => new MyResponse { Text = "Respond: " + request.Text });
});
4、同样的,请求和响应过程可以在不同项目中,但是要注意所对应的队列,因为队列名和交换机转发路由都是根据请求消息生成的,所以此时建议使用自定义队列名的方式。
5、EasyNetQ模式:Send/Receive
using System;
namespace EasyNetQ.Demo
{
public class MyMessage
{
public string Text { get; set; }
}
class Program
{
static void Main(string[] args)
{
var connectionString = "host=192.168.18.129;virtualHost=/;username=admin;password=123456;timeout=60";
//接收/Receive
{
var bus = RabbitHutch.CreateBus(connectionString);
bus.Receive<MyMessage>("my_queue_name", r => Console.WriteLine("Receive:" + r.Text));
}
//发送/Send
using (var bus = RabbitHutch.CreateBus(connectionString))
{
var input = "";
Console.WriteLine("Please enter a message. 'q'/'Q' to quit.");
while ((input = Console.ReadLine()).ToLower() != "q")
{
bus.Send("my_queue_name", new MyMessage
{
Text = input
});
}
}
}
}
}
上述代码执行完成后,会创建一个名为my_queue_name的队列,但是不会创建交换机,也就是说Send/Receive不是基于交换机,为什么有了Publish/Subsrcibe和Request/Response模式,还要添加一个Send/Receive模式?发送接收模式主要是针对队列,前面说了,EasyNetQ规定每个发送的消息必须是一个实体类型,消息经过序列化之后放到rabbitmq中去的,而Publish/Subsrcibe和Request/Response模式默认都是一个队列对应一个实体,也就是说,一个队列中只能存放一个类型序列化后的数据。而Send/Receive模式允许一个队列存放多种类型格式化后的数据,在接收时再根据类型匹配。
发送接收模式特点:
1、发送消息使用Send方法
bus.Send("my_queue_name", new MyMessage
{
Text = input
});
Send方法执行后会创建队列(如果不存在),而且可以往同一队列中发送不同类型的消息
bus.Send("my_queue_name", new CatMessage
{
Miao = "Miao"
});
bus.Send("my_queue_name", new DogMessage
{
Wang = "Wang"
});
2、接收消息使用Receive方法
bus.Receive<MyMessage>("my_queue_name", r =>
{
Console.WriteLine("Receive:" + r.Text);
});
Receive方法也会创建队列(如果不存在),当队列中有多种类型的消息时,可以在Receive方法中的hander中添加不同类型的接收者
bus.Receive("my_queue_name", hander =>
{
hander.Add<CatMessage>(r =>
{
Console.WriteLine("Cat:" + r.Miao);
});
hander.Add<DogMessage>(r =>
{
Console.WriteLine("Dog:" + r.Wang);
});
});
3、每次调用Receive方法都会创建一个消费者,多次调用Receive方法后,同一队列的消息会分发到不同消费者中。对于一个消费者,如果消息送达了消费者的接收队列,会根据添加的类型进行匹配,如果匹配成功,则用对应的逻辑处理,如果没有匹配到任何接收者,EasyNetQ将会把消息带着一个异常“No handler for message type写进EasyNetQ的错误队列”。
4、同样的,发送和接收可以在不同项目中,但是由于接收过程是根据类型匹配的,那么就需要发送和接收中使用的是同一类型,而且,每一个Receive方法中添加的接收器要尽可能的覆盖所接收的队列中所有的消息类型,这样才能保证不会导致消息匹配不到接受者。
5、Send方法和Receive方法也有异步操作。
bus.SendAsync("my_queue_name", new MyMessage
{
Text = input
});
bus.Receive<MyMessage>("my_queue_name", async r =>
{
await Task.Run(() =>
{
Console.WriteLine("Receive:" + r.Text);
});
});
C# .net 使用rabbitmq消息队列——EasyNetQ插件介绍的更多相关文章
- rabbitmq - (消息队列) 的基本原理介绍
介绍 MQ全称为Message Queue, 是一种分布式应用程序的的通信方法,它是消费-生产者模型的一个典型的代表,producer往消息队列中不断写入消息,而另一端consumer则可以读取或者订 ...
- RabbitMQ消息队列+安装+工具介绍
1.MQ为Message Queue,消息队列是应用程序和应用程序之间的通信方法 2. 多种开发语言支持,其实就是一个驱动,如连接数据库的mysql驱动,oracle驱动等. 3. 4.采用以下语言开 ...
- 使用EasyNetQ组件操作RabbitMQ消息队列服务
RabbitMQ是一个由erlang开发的AMQP(Advanved Message Queue)的开源实现,是实现消息队列应用的一个中间件,消息队列中间件是分布式系统中重要的组件,主要解决应用耦合, ...
- .net core使用rabbitmq消息队列
看博文的朋友,本文有些过时了,还有些BUG,如果想了解更多用法,看看这篇吧:.net core使用rabbitmq消息队列 (二) 首先,如果你还没有安装好rabbitmq,可以参考我的博客: Ubu ...
- C# .net 环境下使用rabbitmq消息队列
消息队列的地位越来越重要,几乎是面试的必问问题了,不会使用几种消息队列都显得尴尬,正好本文使用C#来带你认识rabbitmq消息队列 首先,我们要安装rabbitmq,当然,如果有现成的,也可以使用, ...
- RabbitMQ消息队列应用
RabbitMQ消息队列应用 消息通信组件Net分布式系统的核心中间件之一,应用与系统高并发,各个组件之间解耦的依赖的场景.本框架采用消息队列中间件主要应用于两方面:一是解决部分高并发的业务处理:二是 ...
- 【框架学习与探究之消息队列--EasyNetQ(2)】
声明 本文欢迎转载,系博主原创,本文原始链接地址:http://www.cnblogs.com/DjlNet/p/7654902.html 前言 此文章,是承接上篇:[框架学习与探究之消息队列--Ea ...
- RabbitMQ消息队列(四)-服务详细配置与日常监控管理
RabbitMQ服务管理 启动服务:rabbitmq-server -detached[ /usr/local/rabbitmq/sbin/rabbitmq-server -detached ] 查看 ...
- Python RabbitMQ消息队列
python内的队列queue 线程 queue:不同线程交互,不能夸进程 进程 queue:只能用于父进程与子进程,或者同一父进程下的多个子进程,进行交互 注:不同的两个独立进程是不能交互的. ...
随机推荐
- Mysql的行级锁
我们首先需要知道的一个大前提是:mysql的锁是由具体的存储引擎实现的.所以像Mysql的默认引擎MyISAM和第三方插件引擎 InnoDB的锁实现机制是有区别的. Mysql有三种级别的锁定:表级锁 ...
- SpringAOP浅析
1.问题 问题:想要添加日志记录.性能监控.安全监测 2.最初解决方案 2.1.最初解决方案 缺点:太多重复代码,且紧耦合 2.2.抽象类进行共性设计,子类进行个性设计,此处不讲解,缺点一荣俱荣,一损 ...
- Spring Boot中使用Servlet与Filter
在Spring Boot中使用Servlet,根据Servlet注册方式的不同,有两种使用方式.若使用的是Servlet3.0+版本,则两种方式均可使用:若使用的是Servlet2.5版本,则只能使用 ...
- 【Java 调优】Java性能优化
Java性能优化的50个细节(珍藏版) 1. 尽量在合适的场合使用单例 使用单例可以减轻加载的负担,缩短加载的时间,提高加载的效率,但并不是所有地方都适用于单例,简单来说,单例主要适用于以下三个方面: ...
- 4、Linux下安装tomcat
Linux系统下安装tomcat 一.安装JDK 安装Tomcat之前需要安装JDk,安装JDk请参考:JDK安装. 二.Linux安装Tomcat 1.官网上下载Tomcat Apach ...
- 有了代码变更分解提交工具SmartCommit,再也不担心复合提交了
摘要:文将介绍一个代码提交辅助工具SmartCommit,其主要功能是通过杂糅变更分解算法自动生成分组提交方案,接受开发者的反馈和交互式调整,渐进式地引导和辅助开发者做出符合最佳实践的原子提交. 本文 ...
- Istio在Rainbond Service Mesh体系下的落地实践
两年前Service Mesh(服务网格)一出来就受到追捧,很多人认为它是微服务架构的最终形态,因为它可以让业务代码和微服务架构解耦,也就是说业务代码不需要修改就能实现微服务架构,但解耦还不够彻底,使 ...
- 利用 trap 在 docker 容器优雅关闭前执行环境清理
当一个运行中的容器被终止时,如何能够执行一些预定义的操作,比如在容器彻底退出之前清理环境.这是一种类似于 pre stop 的钩子体验.但 docker 本身无法提供这种能力,本文结合 Linux 内 ...
- 添加备注信息(Project)
<Project2016 企业项目管理实践>张会斌 董方好 编著 就在任务信息的[高级]选项卡隔壁,还有一个[备注]选项卡,可别拿备注不当回事,因为任务名称的字数不能太多. 好吧,张同学也 ...
- std::function介绍 -转载
类模版std::function是一种通用.多态的函数封装.std::function的实例可以对任何可以调用的目标实体进行存储.复制.和调用操作,这些目标实体包括普通函数.Lambda表达式.函数指 ...