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地 ...
随机推荐
- 服务器、IP地址和域名之间有什么关系?
一.服务器 服务器其实就像我们的家用电脑一样,也有主板.CPU.内存.硬盘.电源等,但是由于它们处理问题的不同,服务器更像一台加强的家用电脑,服务器是为展网络业务而存放.处理数据的,所以服务器一般是存 ...
- php字符串与数组的特殊情况
来看一个有趣的实验 实验1 <?php $arr = array('a','b','c'); var_dump(isset($arr[1][0])); var_dump($arr[0][0]); ...
- 使用GitHub作为Maven仓库并引用
网上太多的博客都是那些傻逼抄袭,然后直接复制粘贴然后就成为自己的博客了,这种人,真的很欠揍,我在网上查了一个下午的资料,终于找到一个写得非常详细的兄弟 链接如下:https://blog.csdn.n ...
- VMware手动添加centos7硬盘图文操作及分区超详细
先设置虚拟机 启动的虚拟机,新关机再设置 1.选择指定虚拟机,点击硬盘 2.虚拟机设置,点击左下角“添加” 3.硬件类型选择硬盘,点击下一步 4.添加硬件向导默认就行,下一步 5.选择磁盘,默认选中, ...
- 对Jpa中Entity关系映射中mappedBy的理解
mappedBy 单向关系不需要设置该属性,双向关系必须设置,避免双方都建立外键字段数据库中1对多的关系,关联关系总是被多方维护的即外键建在多方,我们在单方对象的@OneToMany(mappedby ...
- AWSS3异步等待上传成功返回结果
/// <summary> /// 流上传文件 /// </summary> /// <param name="data">流内容</pa ...
- Spring源码Gradle
Microsoft Windows [版本 10.0.17134.590](c) 2018 Microsoft Corporation.保留所有权利. D:\Workspaces\idea\sprin ...
- 文本在div中始终垂直居中
如果是文本字数固定,在div中垂直居中,相信大家都会 这边分享个不固定文本在div中垂直居中的方法 html代码 <div class="box"> <div c ...
- [LeetCode] Shifting Letters 漂移字母
We have a string S of lowercase letters, and an integer array shifts. Call the shift of a letter, th ...
- mybatis逆向工程的注意事项,以及数据库表
1.选择性更新,如果有新参数就更换成新参数,如果参数是null就不更新,还是原来的参数 2.mybatis使用逆向工程,数据库建表的字段user_id必须用下滑线隔开,这样生成的对象private L ...