RabbitMQ学习笔记(六) RPC
什么RPC?
这一段是从度娘摘抄的。
RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。
RPC采用客户机/服务器模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,客户机调用进程发送一个有进程参数的调用信息到服务进程,然后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用进程接收答复信息,获得进程结果,然后调用执行继续进行。
Callback Queue
之前在做学习笔记(二)工作队列的例子的时候,我们只是发送了任务给消息消费者实例去完成,但是对于消息生产者,它并不知道任务是否完成,所以这里缺少一个回调方法。
既然我们使用RabbitMQ, 所以很容易想到的就是我们可以互换一下消息消费者和消息生产者的角色,当消费生产者完成任务之后,通过创建一个新的消息队列,将完成任务的消息,发送给消息生产者。

那么如何让消息消费者,知道消费生产者关注的消息队列呢?
在我们发布消息的时候,会调用channel对象的BasicPublish方法,这个方法中有一个IBasicProperties的参数basicProperties。
在这对象中,有一个ReplyTo属性,我们可以将生产者监听的消息队列名称存放在里面。当消费者程序接收到这条消息的时候,就可以在Receive事件的ea对象中获取ReplyTo属性的值
var props = channel.CreateBasicProperties(); props.ReplyTo = replyQueueName; var messageBytes = Encoding.UTF8.GetBytes(message); channel.BasicPublish(exchange: "", routingKey: "rpc_queue", basicProperties: props, body: messageBytes);
Correlation Id
那么当消息生产者接收到消息消费者任务完成的消息之后,该如何确定完的是哪一个任务呢?
在现实情况,消息生产者通常会发出多个任务,多个消息消费者分别进行不同的任务,这时候我们就需要知道是哪个消息消费者完成了任务。
当消息生产者调用channel对象的BasicPublish方法发送消息时,IBasicProperties对象除了可以帮助我们传递消息生产者监听的消息队列名,还可以帮我们传递一个CorrelationId(相关Id),当发送任务消息的时候,我们给每个任务消息定义一个唯一的相关Id, 并存储在IBasicProperties对象的CorrelationId属性中。
var properties = channel.CreateBasicProperties(); properties.ReplyTo = replyQueueName; properties.CorrelationId = Guid.NewGuid().ToString();
这样消息消费者在接收到任务消息时,可以从Receive的ea参数中获取CorrelationId。当任务完成时,再将保存有这个CorrelationId的任务完成消息发送到消息生产者关注的消息队列中, 消息生产者就可以知道是哪个任务完成了
手动实现RabbitMQ的RPC
Send
static void Main(string[] args)
{
var factory = new ConnectionFactory()
{
HostName = "localhost"
};
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
var replyQueueName = channel.QueueDeclare().QueueName;
var consumer = new EventingBasicConsumer(channel);
channel.BasicConsume(queue: replyQueueName,
autoAck: true,
consumer: consumer);
channel.QueueDeclare(queue: "manual_rpc_queue",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);
string message = args[0];
var body = Encoding.UTF8.GetBytes(message);
var properties = channel.CreateBasicProperties();
properties.ReplyTo = replyQueueName;
properties.CorrelationId = Guid.NewGuid().ToString();
consumer.Received += (m, ea) =>
{
if (ea.BasicProperties.CorrelationId == properties.CorrelationId)
{
Console.WriteLine($"Task {properties.CorrelationId} completed.");
Console.WriteLine($"{Encoding.UTF8.GetString(ea.Body)}");
Environment.Exit(1);
}
};
channel.BasicPublish(exchange: "",
routingKey: "manual_rpc_queue",
basicProperties: properties,
body: body
);
Console.WriteLine("[x] Sent {0}", message);
Console.Read();
}
}
}
Receive
static void Main(string[] args)
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
channel.QueueDeclare(queue: "manual_rpc_queue",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);
channel.BasicQos(prefetchSize: 0, prefetchCount: 1, global: false);
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
Thread.Sleep(4000);
Console.WriteLine("[x] Received {0}", message);
var prop = channel.CreateBasicProperties();
prop.CorrelationId = ea.BasicProperties.CorrelationId;
channel.BasicPublish(
exchange: "",
routingKey: ea.BasicProperties.ReplyTo,
mandatory: false,
basicProperties: prop,
body: Encoding.UTF8.GetBytes($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} Task {prop.CorrelationId} Completed."));
channel.BasicAck(ea.DeliveryTag, false);
};
channel.BasicConsume(queue: "manual_rpc_queue", autoAck: false, consumer: consumer);
Console.Read();
}
}
}
最终效果

RabbitMQ提供的RPC实现
RabbitMQ提供了一个SimpleRpcClient类和SimpleRpcServer类,来简化我们的代码,代码不难理解
Send
static void Main(string[] args)
{
var factory = new ConnectionFactory()
{
HostName = "localhost"
};
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
SimpleRpcClient client = new SimpleRpcClient(channel, new PublicationAddress(exchangeType: ExchangeType.Direct, exchangeName: string.Empty, routingKey: "RpcQueue"));
var prop = channel.CreateBasicProperties();
prop.CorrelationId = Guid.NewGuid().ToString();
IBasicProperties outProp;
var msg = client.Call(prop, Encoding.UTF8.GetBytes(args[0]), out outProp);
if (prop.CorrelationId == outProp.CorrelationId)
{
Console.WriteLine($"Task {prop.CorrelationId} completed.");
Console.WriteLine(Encoding.UTF8.GetString(msg));
}
}
}
}
Receive
- 创建MySimpleRpcServer类,继承自SimpleRpcServer类
- HandleSimpleCall方法里添加回调返回值
- ProcessRequest方法为任务处理方法
MySimpleRpcServer.cs
public class MySimpleRpcServer : SimpleRpcServer
{
public MySimpleRpcServer(Subscription subscription) : base(subscription)
{
}
public override byte[] HandleSimpleCall(bool isRedelivered, IBasicProperties requestProperties, byte[] body, out IBasicProperties replyProperties)
{
replyProperties = null;
return Encoding.UTF8.GetBytes($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} Task {requestProperties.CorrelationId} Completed.");
}
/// <summary>
/// 进行处理
/// </summary>
/// <param name="evt"></param>
public override void ProcessRequest(BasicDeliverEventArgs evt)
{
Console.WriteLine("[x] Received {0}", Encoding.UTF8.GetString(evt.Body));
Thread.Sleep(4000);
base.ProcessRequest(evt);
}
}
Program.cs
static void Main(string[] args)
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
channel.QueueDeclare("RpcQueue", true, false, false, null);
SimpleRpcServer rpc = new MySimpleRpcServer(new Subscription(channel, "RpcQueue"));
rpc.MainLoop();
Console.ReadKey();
}
}
}
RabbitMQ学习笔记(六) RPC的更多相关文章
- rabbitMQ学习笔记(七) RPC 远程过程调用
关于RPC的介绍请参考百度百科里的关于RPC的介绍:http://baike.baidu.com/view/32726.htm#sub32726 现在来看看Rabbitmq中RPC吧!RPC的工作示意 ...
- rabbitMQ学习笔记(六) topic类型消息。
上一节中使用了消息路由,消费者可以选择性的接收消息. 但是这样还是不够灵活. 比如某个消费者要订阅娱乐新闻消息 . 包括新浪.网易.腾讯的娱乐新闻.那么消费者就需要绑定三次,分别绑定这三个网站的消息类 ...
- RabbitMQ学习笔记六:RabbitMQ之消息确认
使用消息队列,必须要考虑的问题就是生产者消息发送失败和消费者消息处理失败,这两种情况怎么处理. 生产者发送消息,成功,则确认消息发送成功;失败,则返回消息发送失败信息,再做处理. 消费者处理消息,成功 ...
- 官网英文版学习——RabbitMQ学习笔记(一)认识RabbitMQ
鉴于目前中文的RabbitMQ教程很缺,本博主虽然买了一本rabbitMQ的书,遗憾的是该书的代码用的不是java语言,看起来也有些不爽,且网友们不同人学习所写不同,本博主看的有些地方不太理想,为此本 ...
- # go微服务框架kratos学习笔记六(kratos 服务发现 discovery)
目录 go微服务框架kratos学习笔记六(kratos 服务发现 discovery) http api register 服务注册 fetch 获取实例 fetchs 批量获取实例 polls 批 ...
- java之jvm学习笔记六-十二(实践写自己的安全管理器)(jar包的代码认证和签名) (实践对jar包的代码签名) (策略文件)(策略和保护域) (访问控制器) (访问控制器的栈校验机制) (jvm基本结构)
java之jvm学习笔记六(实践写自己的安全管理器) 安全管理器SecurityManager里设计的内容实在是非常的庞大,它的核心方法就是checkPerssiom这个方法里又调用 AccessCo ...
- Learning ROS for Robotics Programming Second Edition学习笔记(六) indigo xtion pro live
中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 Learning ROS for Robotics Pr ...
- RabbitMQ学习笔记(五) Topic
更多的问题 Direct Exchange帮助我们解决了分类发布与订阅消息的问题,但是Direct Exchange的问题是,它所使用的routingKey是一个简单字符串,这决定了它只能按照一个条件 ...
- Typescript 学习笔记六:接口
中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...
- RabbitMQ学习笔记1-hello world
安装过程略过,一搜一大把. rabbitmq管理控制台:http://localhost:15672/ 默认账户:guest/guest RabbitMQ默认监听端口:5672 JAVA API地 ...
随机推荐
- 在VUE-CLI 3下的第一个Element-ui项目(菜鸟专用)
vue-cli3.0使用及配置 (https://www.cnblogs.com/xzqyun/p/10779891.html ) 以上是 vue-cli3.0使用及配置 这里我们来引用基于v ...
- mac中如何卸载pkg包
---恢复内容开始--- 参考地址.https://blog.csdn.net/play_fun_tech/article/details/27964861?utm_source=tuicool&am ...
- user-agent | what is the "user-agent" ?
User Agent(用户代理) UA是一个特殊字符串头,使得服务器能够识别客户使用的操作系统及版本.CPU 类型.浏览器及版本.浏览器渲染引擎.浏览器语言.浏览器插件等 通过抓包可以得到 下面是几个 ...
- ubuntu 14.04 rabbitmq集群部署
1.准备机器,我这边准备的是三台ubuntu14.04 机器主机名不能相同,不然节点冲突 2.安装rabbitmq 3.修改hosts文件 root@abc-web-04:~# vim /etc/ho ...
- vue将指定区域的表格数据或element-ui中el-table的数据单笔或多笔批量导出excel
公司在后台管理系统开发中用到了 vue+element-ui 组合的框架,但随着需求的越来越复杂,前端的工作难度也呈几何倍数递增,工作量随之增大.这不,在项目中增加一个将列表数据导出为excel的需求 ...
- 删除 id 列表 存进数据库
当图片写的传id的时候 用着方法存进数据库
- Github远程推送一直Everything up-to-date
问题描述: Github远程推送一直Everything up-to-date,但其实并没有推送成功,远程库中没有更新文件 可能原因分析及解决方法: "git push with no ad ...
- 链表加bfs求补图联通块
https://oj.neu.edu.cn/problem/1387 给一个点数N <= 100000, 边 <= 1000000的无向图,求补图的联通块数,以及每个块包含的点数 由于点数 ...
- zepto与jquery冲突的解决
一般是不会把zepto和jquery一起来用的.但有时候要引入一些插件,可能就会遇到这样的问题. jquery noConflict() jquery有一个方法叫noConflict() ,可以把jq ...
- 【分享】【原创开源应用第4期】给ili9488,RA8875类显示屏的emWin底层增加DMA加速方案
说明:1.emWin底层中最重要的一个优化就是16bpp绘制,特此为其增加DMA加速,已经支持RA8875和ili9488.2.使用中务必将emWin任务设置为除了空闲任务,统计任务以外的最低优先级, ...