前面讲过一些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

  3.博客原地址:http://fpeach.com/post/2016/12/01/RabbitMQ%E5%AD%A6%E4%B9%A0%E7%B3%BB%E5%88%97%EF%BC%88%E4%BA%94%EF%BC%89-RPC-%E8%BF%9C%E7%A8%8B%E8%BF%87%E7%A8%8B%E8%B0%83%E7%94%A8.aspx

RabbitMQ学习系列(五): RPC 远程过程调用的更多相关文章

  1. rabbitMQ学习笔记(七) RPC 远程过程调用

    关于RPC的介绍请参考百度百科里的关于RPC的介绍:http://baike.baidu.com/view/32726.htm#sub32726 现在来看看Rabbitmq中RPC吧!RPC的工作示意 ...

  2. RabbitMQ入门教程(八):远程过程调用RPC

    原文:RabbitMQ入门教程(八):远程过程调用RPC 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.cs ...

  3. RPC远程过程调用

    什么是RPC: 将一个函数运行在远程计算机上并且等待获取那里的结果,这个称作RPC: (Remote Procedure Call远程过程调用) RPC是一个计算机通信协议. rpc指的是在计算机A上 ...

  4. (扫盲)RPC远程过程调用

    https://blog.csdn.net/mindfloating/article/details/39473807 https://blog.csdn.net/mindfloating/artic ...

  5. Redis总结(五)缓存雪崩和缓存穿透等问题 Web API系列(三)统一异常处理 C#总结(一)AutoResetEvent的使用介绍(用AutoResetEvent实现同步) C#总结(二)事件Event 介绍总结 C#总结(三)DataGridView增加全选列 Web API系列(二)接口安全和参数校验 RabbitMQ学习系列(六): RabbitMQ 高可用集群

    Redis总结(五)缓存雪崩和缓存穿透等问题   前面讲过一些redis 缓存的使用和数据持久化.感兴趣的朋友可以看看之前的文章,http://www.cnblogs.com/zhangweizhon ...

  6. RabbitMQ学习系列(三): C# 如何使用 RabbitMQ

    上一篇已经讲了Rabbitmq如何在Windows平台安装,还不了解如何安装的朋友,请看我前面几篇文章:RabbitMQ学习系列一:windows下安装RabbitMQ服务 , 今天就来聊聊 C# 实 ...

  7. RabbitMQ学习系列(四): 几种Exchange 模式

    上一篇,讲了RabbitMQ的具体用法,可以看看这篇文章:RabbitMQ学习系列(三): C# 如何使用 RabbitMQ.今天说些理论的东西,Exchange 的几种模式. AMQP协议中的核心思 ...

  8. RabbitMQ学习系列二-C#代码发送消息

    RabbitMQ学习系列二:.net 环境下 C#代码使用 RabbitMQ 消息队列 http://www.80iter.com/blog/1437455520862503 上一篇已经讲了Rabbi ...

  9. RabbitMQ学习系列一安装RabbitMQ服务

    RabbitMQ学习系列一:windows下安装RabbitMQ服务 http://www.80iter.com/blog/1437026462550244 Rabbit MQ 是建立在强大的Erla ...

随机推荐

  1. Ubuntu搭建NFS

    NFS全称是Network File System,网络文件系统.它可以通过网络实现文件共享.其结构图大概是这样的: 在机器E上开启NFS服务,机器ABCD都挂载NFS,这样可以实现机器ABCD共享文 ...

  2. 树莓派:raspberry pi 3b - NOOBS

    NOOBS - 多操作系统安装器,可以将不同支持树莓派的流行操作系统安装在一张SD卡中并提供一个启动管理工具,安装的不同操作系统相互独立,互不影响,是一种比较有意思的玩法. 从版本1.3.1开始到1. ...

  3. asp.net mvc 各版本区别

    MVC 6 ASP.NET MVC and Web API has been merged in to one. Dependency injection is inbuilt and part of ...

  4. 织梦Dedecms使用Nginx的安全设置

    首先需要说明的是,任何程序都是有漏洞的,我们需要做好一些必要的防范,来减少由于程序漏洞造成的损失.织梦的漏洞多,这个是很多人的想法.不过大家如果做好了织梦系统的文件夹权限什么的设置,很多漏洞也是用不上 ...

  5. 2-部署phpmyadmin

    软件下载地址:https://files.phpmyadmin.net/phpMyAdmin/4.5.5.1/phpMyAdmin-4.5.5.1-all-languages.zip 解压软件 [ro ...

  6. __dbg.h

    #ifndef __HSS_DBG_HSS__ #define __HSS_DBG_HSS__ /*************************************************** ...

  7. node 异步回调解决方法之yield

    先看如何使用 使用的npm包为genny,npm 安装genny,使用 node -harmony 文件(-harmony 为使用es6属性启动参数) 启动项目 var genny= require( ...

  8. GDB 完全教程

    一.简介 GDB是一个功能强大的调试器,它是一个自由软件,能够用在许多UNIX平台上.它同时也是Linux系统中的默认调试器.GDB已被移植到许多其他的计算机平台上,并且能够用于调试嵌入式实时系统.一 ...

  9. linux命令-系统命令

    1.查看Linux磁盘空间大小 df -lh Filesystem 容量 已用 可用 已用% 挂载点 /dev/hda8 11G 6.0G 4.4G 58% / /dev/shm 236M 0 236 ...

  10. [转载]fullPage.js中文api 配置参数~

    fullPage.js中文api 配置参数 选项 类型 默认值 说明 verticalCentered 字符串 true 内容是否垂直居中 resize 布尔值 false 字体是否随着窗口缩放而缩放 ...