RabbitMQ学习系列(五): RPC 远程过程调用
前面讲过一些RabbitMQ的安装和用法,也说了说RabbitMQ在一般的业务场景下如何使用。不知道的可以看我前面的博客,http://www.cnblogs.com/zhangweizhong/category/855479.html
不过,最近有朋友问我,RabbitMQ RPC 是干嘛的,有什么用。
其实,RabbitMQ RPC 就是通过消息队列(Message Queue)来实现rpc的功能,就是,客户端向服务端发送定义好的Queue消息,其中携带的消息就应该是服务端将要调用的方法的参数 ,并使用Propertis告诉服务端将结果返回到指定的Queue。
1.RabbitMQ RPC的特点
- Message Queue把所有的请求消息存储起来,然后处理,和客户端解耦。
- Message Queue引入新的结点,系统的可靠性会受Message Queue结点的影响。
- Message Queue是异步单向的消息。发送消息设计成是不需要等待消息处理的完成。
所以对于有同步返回需求,Message Queue是个不错的方向。
2.普通PRC的特点
- 同步调用,对于要等待返回结果/处理结果的场景,RPC是可以非常自然直觉的使用方式。当然RPC也可以是异步调用。
- 由于等待结果,客户端会有线程消耗。
如果以异步RPC的方式使用,客户端线程消耗可以去掉。但不能做到像消息一样暂存消息请求,压力会直接传导到服务端。
3.适用场合说明
- 希望同步得到结果的场合,RPC合适。
- 希望使用简单,则RPC;RPC操作基于接口,使用简单,使用方式模拟本地调用。异步的方式编程比较复杂。
- 不希望客户端受限于服务端的速度等,可以使用Message Queue。
4.RabbitMQ RPC工作流程:

基本概念:
Callback queue 回调队列,客户端向服务器发送请求,服务器端处理请求后,将其处理结果保存在一个存储体中。而客户端为了获得处理结果,那么客户在向服务器发送请求时,同时发送一个回调队列地址reply_to。
Correlation id 关联标识,客户端可能会发送多个请求给服务器,当服务器处理完后,客户端无法辨别在回调队列中的响应具体和那个请求时对应的。为了处理这种情况,客户端在发送每个请求时,同时会附带一个独有correlation_id属性,这样客户端在回调队列中根据correlation_id字段的值就可以分辨此响应属于哪个请求。
流程说明:
- 当客户端启动的时候,它创建一个匿名独享的回调队列。
- 在 RPC 请求中,客户端发送带有两个属性的消息:一个是设置回调队列的 reply_to 属性,另一个是设置唯一值的 correlation_id 属性。
- 将请求发送到一个 rpc_queue 队列中。
- 服务器等待请求发送到这个队列中来。当请求出现的时候,它执行他的工作并且将带有执行结果的消息发送给 reply_to 字段指定的队列。
- 客户端等待回调队列里的数据。当有消息出现的时候,它会检查 correlation_id 属性。如果此属性的值与请求匹配,将它返回给应用
5.完整代码:
1. 创建两个控制台程序,作为RPC Server和RPC Client, 引用 RabbitMQ.Client,
2. RPC Server
class Program
{
static void Main(string[] args)
{
var factory = new ConnectionFactory() { HostName = "localhost", VirtualHost = "OrderQueue", UserName = "zhangweizhong", Password = "weizhong1988", Port = };
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
channel.QueueDeclare(queue: "rpc_queue",
durable: false,
exclusive: false,
autoDelete: false,
arguments: null);
channel.BasicQos(, , false);
var consumer = new QueueingBasicConsumer(channel);
channel.BasicConsume(queue: "rpc_queue",
noAck: false,
consumer: consumer);
Console.WriteLine(" [x] Awaiting RPC requests"); while (true)
{
string response = null;
var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue(); var body = ea.Body;
var props = ea.BasicProperties;
var replyProps = channel.CreateBasicProperties();
replyProps.CorrelationId = props.CorrelationId; try
{
var message = Encoding.UTF8.GetString(body);
int n = int.Parse(message);
Console.WriteLine(" [.] fib({0})", message);
response = fib(n).ToString();
}
catch (Exception e)
{
Console.WriteLine(" [.] " + e.Message);
response = "";
}
finally
{
var responseBytes = Encoding.UTF8.GetBytes(response);
channel.BasicPublish(exchange: "",
routingKey: props.ReplyTo,
basicProperties: replyProps,
body: responseBytes);
channel.BasicAck(deliveryTag: ea.DeliveryTag,
multiple: false);
}
}
}
} /// <summary>
/// Assumes only valid positive integer input.
/// Don't expect this one to work for big numbers,
/// and it's probably the slowest recursive implementation possible.
/// </summary>
private static int fib(int n)
{
if (n == || n == )
{
return n;
} Thread.Sleep( * ); return n;
}
}
3. RPC Client
class Program
{
static void Main(string[] args)
{
for (int i = ; i < ; i++)
{
Stopwatch watch = new Stopwatch(); watch.Start(); var rpcClient = new RPCClient(); Console.WriteLine(string.Format(" [x] Requesting fib({0})", i)); var response = rpcClient.Call(i.ToString()); Console.WriteLine(" [.] Got '{0}'", response); rpcClient.Close(); watch.Stop(); Console.WriteLine(string.Format(" [x] Requesting complete {0} ,cost {1} ms", i, watch.Elapsed.TotalMilliseconds));
} Console.WriteLine(" complete!!!! "); Console.ReadLine();
}
} class RPCClient
{
private IConnection connection;
private IModel channel;
private string replyQueueName;
private QueueingBasicConsumer consumer; public RPCClient()
{
var factory = new ConnectionFactory() { HostName = "localhost", VirtualHost = "OrderQueue", UserName = "zhangweizhong", Password = "weizhong1988", Port = };
connection = factory.CreateConnection();
channel = connection.CreateModel();
replyQueueName = channel.QueueDeclare().QueueName;
consumer = new QueueingBasicConsumer(channel);
channel.BasicConsume(queue: replyQueueName,
noAck: true,
consumer: consumer);
} public string Call(string message)
{
var corrId = Guid.NewGuid().ToString();
var props = channel.CreateBasicProperties();
props.ReplyTo = replyQueueName;
props.CorrelationId = corrId; var messageBytes = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(exchange: "",
routingKey: "rpc_queue",
basicProperties: props,
body: messageBytes); while (true)
{
var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue();
if (ea.BasicProperties.CorrelationId == corrId)
{
return Encoding.UTF8.GetString(ea.Body);
}
}
} public void Close()
{
connection.Close();
}
}
4.分别运行Server和Client


6.最后
1.参照RabbitMQ官方教程的RPC,地址:http://www.rabbitmq.com/tutorials/tutorial-six-dotnet.html
2.本文源代码下载,http://files.cnblogs.com/files/zhangweizhong/Weiz.RabbitMQ.RPC.rar
RabbitMQ学习系列(五): RPC 远程过程调用的更多相关文章
- rabbitMQ学习笔记(七) RPC 远程过程调用
关于RPC的介绍请参考百度百科里的关于RPC的介绍:http://baike.baidu.com/view/32726.htm#sub32726 现在来看看Rabbitmq中RPC吧!RPC的工作示意 ...
- RabbitMQ入门教程(八):远程过程调用RPC
原文:RabbitMQ入门教程(八):远程过程调用RPC 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.cs ...
- RPC远程过程调用
什么是RPC: 将一个函数运行在远程计算机上并且等待获取那里的结果,这个称作RPC: (Remote Procedure Call远程过程调用) RPC是一个计算机通信协议. rpc指的是在计算机A上 ...
- (扫盲)RPC远程过程调用
https://blog.csdn.net/mindfloating/article/details/39473807 https://blog.csdn.net/mindfloating/artic ...
- Redis总结(五)缓存雪崩和缓存穿透等问题 Web API系列(三)统一异常处理 C#总结(一)AutoResetEvent的使用介绍(用AutoResetEvent实现同步) C#总结(二)事件Event 介绍总结 C#总结(三)DataGridView增加全选列 Web API系列(二)接口安全和参数校验 RabbitMQ学习系列(六): RabbitMQ 高可用集群
Redis总结(五)缓存雪崩和缓存穿透等问题 前面讲过一些redis 缓存的使用和数据持久化.感兴趣的朋友可以看看之前的文章,http://www.cnblogs.com/zhangweizhon ...
- RabbitMQ学习系列(三): C# 如何使用 RabbitMQ
上一篇已经讲了Rabbitmq如何在Windows平台安装,还不了解如何安装的朋友,请看我前面几篇文章:RabbitMQ学习系列一:windows下安装RabbitMQ服务 , 今天就来聊聊 C# 实 ...
- RabbitMQ学习系列(四): 几种Exchange 模式
上一篇,讲了RabbitMQ的具体用法,可以看看这篇文章:RabbitMQ学习系列(三): C# 如何使用 RabbitMQ.今天说些理论的东西,Exchange 的几种模式. AMQP协议中的核心思 ...
- RabbitMQ学习系列二-C#代码发送消息
RabbitMQ学习系列二:.net 环境下 C#代码使用 RabbitMQ 消息队列 http://www.80iter.com/blog/1437455520862503 上一篇已经讲了Rabbi ...
- RabbitMQ学习系列一安装RabbitMQ服务
RabbitMQ学习系列一:windows下安装RabbitMQ服务 http://www.80iter.com/blog/1437026462550244 Rabbit MQ 是建立在强大的Erla ...
随机推荐
- docker通过iptables修改或新增镜像映射端口
443 8088 22 端口是初始映射端口 [root@SERVER ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAM ...
- AngularJS HTML DOM& 事件
AngularJS 为 HTML DOM 元素的属性提供了绑定应用数据的指令. ng-disabled 指令直接绑定应用程序数据到 HTML 的 disabled 属性 <div ng-app= ...
- VMware中CPU分配不合理以及License限制引起的SQL Scheduler不能用于查询处理
有一台SQL Server(SQL Server 2014 标准版)服务器中的scheduler_count与cpu_count不一致,如下截图所示: SELECT cpu_count , ...
- EF里查看/修改实体的当前值、原始值和数据库值以及重写SaveChanges方法记录实体状态
本文目录 查看实体当前.原始和数据库值:DbEntityEntry 查看实体的某个属性值:GetValue<TValue>方法 拷贝DbPropertyValues到实体:ToObject ...
- 常见的高可用MySQL解决方案
MySQL数据库作为最基础的数据存储服务之一,在整个系统中有着非常重要的地位,因此要求其具备高可用性是无可厚非的.有很多解决方案能实现不同的SLA(服务水平协定),这些方案可以保证数据库服务器在硬件或 ...
- 腾讯云CentOS系统配置apache和tomcat
本文使用yum软件包管理工具基于CentOS7.2版本配置apache和tom. 云服务器选购完毕后,安装Xshell软件,输入用户名密码即可远程登陆登录(centos用户名默认是root). 1,下 ...
- spring HttpInvoker相关学习资料
官方文档 spring支持的几种RPC 用Http Invoker实现RCP客户端与后台的交互 Java HttpInvoker小试 Spring注解发布RMI/HTTPInvoker/Hessian ...
- 初识Android Studio
刚开始接触Android Studio,很多不适应的地方,自己慢慢摸索,记录下了一些问题和解决途径. 为了能使用android虚拟机,需要下载镜像,镜像有基于arm架构的也有基于intelx86.x6 ...
- Nginx+PHP On windows
前期准备 Nginx 下载 http://nginx.org/ PHP下载 PHP Windows binaries 问题 问题1: opened a DOS window to la ...
- Hibernate入门学习-安装hibernate插件
最近一直在学习ssh框架,为了更方便hibernate的使用,所以在eclipse下载hibernate插件,安装插件有两种方法 第一种:本地安装 1).插件的官网下载地址:http://tools. ...