rabbitMQ第四篇:远程调用
前言:前面我们讲解的都是本地服务器,现在如果需要远程计算机上运行一个函数,等待结果。这就是一个不同的故事了,这种模式通常被称为远程过程调用或者RPC。
本章教程我们使用RabbitMQ搭建一个RPC系统,一个客户端和一个可扩展的RPC服务器,现在我们开始吧。
Callback queue
一般做rpc在RabbitMQ是比较容易的,一个客户端发送一个请求信息和一个响应信息的服务器回复,为了得到一个响应,我们需要发送一个回调队列地址请求。如下

Message属性:
AMQP协议一共预定义了14个属性,但是大多数属性很少使用,下面几个可能用的比较多
deliveryMode:有2个值,一个是持久,另一个表示短暂(第二篇说过)
contentType:内容类型:用来描述编码的MIME类型。例如,经常使用JSON编码是将此属性设置为一个很好的做法:application/json。
replyTo:经常使用的是回调队列的名字
correlationid:RPC响应请求的相关应用
Correlation Id
在队列上接收到一个响应,但它并不清楚响应属于哪一个,当我们使用CorrelationId属性的时候,我们就可以将它设置为每个请求的唯一值,稍后当我们在回调队列中接收消息的时候,我们会看到这个属性,如果我们看到一个未知的CorrelationId,我们就可以安全地忽略信息-它不属于我们的请求。为什么我们应该忽略未知的消息在回调队列中,而不是失败的错误?这是由于服务器端的一个竞争条件的可能性。比如还未发送了一个确认信息给请求,但是此时RPC服务器挂了。如果这种情况发生,将再次重启RPC服务器处理请求。这就是为什么在客户端必须处理重复的反应。
需求

我们的rpc工作方式如下:
1:当客户端启动时,它创建一个匿名的独占回调队列。
2:对于rpc请求,客户端发送2个属性,一个是replyTo设置回调队列,另一是correlationId为每个队列设置唯一值
3:请求被发送到一个rpc_queue队列中
4:rpc服务器是等待队列的请求,当收到一个请求的时候,他就把消息返回的结果返回给客户端,使请求结束。
5:客户端等待回调队列上的数据,当消息出现的时候,他检查correlationId,如果它和从请求返回的值匹配,就进行响应。
编码
RPCServer.Java
public class RPCServer {
private static final String RPC_QUEUE_NAME = "rpc_queue";
private static int fib(int n) {
if (n == 0) {
return 0;
}
if (n == 1) {
return 1;
}
return fib(n - 1) + fib(n - 1);
}
public static void main(String[] args) throws IOException, InterruptedException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(RPC_QUEUE_NAME, false, false, false, null);
channel.basicQos(1);
QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(RPC_QUEUE_NAME, false, consumer);
System.out.println("RPCServer Awating RPC request");
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
BasicProperties props = delivery.getProperties();
BasicProperties replyProps = new AMQP.BasicProperties.Builder().
correlationId(props.getCorrelationId()).build();
String message = new String(delivery.getBody(), "UTF-8");
int n = Integer.parseInt(message);
System.out.println("RPCServer fib(" + message + ")");
String response = "" + fib(n);
channel.basicPublish( "", props.getReplyTo(), replyProps, response.getBytes());
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
}
服务器代码比较简单
1:建立连接,通道,队列
2:我们可能运行多个服务器进程,为了分散负载服务器压力,我们设置channel.basicQos(1);
3:我们用basicconsume访问队列。然后进入循环,在其中我们等待请求消息并处理消息然后发送响应。
RPCClient.java
public class RPCClient {
private Connection connection;
private Channel channel;
private String requestQueueName = "rpc_queue";
private String replyQueueName;
private QueueingConsumer consumer;
public RPCClient() throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
connection = factory.newConnection();
channel = connection.createChannel();
replyQueueName = channel.queueDeclare().getQueue();
consumer = new QueueingConsumer(channel);
channel.basicConsume(replyQueueName, true, consumer);
}
public String call(String message) throws IOException, InterruptedException {
String response;
String corrID = UUID.randomUUID().toString();
AMQP.BasicProperties props = new AMQP.BasicProperties().builder()
.correlationId(corrID).replyTo(replyQueueName).build();
channel.basicPublish("", requestQueueName, props, message.getBytes("UTF-8"));
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
if (delivery.getProperties().getCorrelationId().equals(corrID)) {
response = new String(delivery.getBody(), "UTF-8");
break;
}
}
return response;
}
public void close() throws Exception {
connection.close();
}
public static void main(String[] args) throws Exception {
RPCClient rpcClient = null;
String response;
try {
rpcClient = new RPCClient();
System.out.println("RPCClient Requesting fib(20)");
response = rpcClient.call("20");
System.out.println("RPCClient Got '" + response + "'");
} catch (Exception e) {
e.printStackTrace();
} finally {
if (rpcClient != null) {
rpcClient.close();
}
}
}
}
客户端代码解读
1:建立一个连接和通道,并声明了一个唯一的“回调”队列的答复
2:我们订阅回调队列,这样就可以得到RPC的响应
3:定义一个call方法用于发送当前的回调请求
4:生成一个唯一的correlationid,然后通过while循环来捕获合适的回应
5:我们请求信息,发送2个属性,replyTo 和correlationId
6:然后就是等待直到有合适的回应到达
7:while循环是做一个非常简单的工作,对于每一个响应消息,它检查是否有correlationid然后进行匹配。然后是就进行响应。
8:最后把响应返回到客户端。
rabbitMQ第四篇:远程调用的更多相关文章
- RabbitMQ第四篇:Spring集成RabbitMQ
前面几篇讲解了如何使用rabbitMq,这一篇主要讲解spring集成rabbitmq. 首先引入配置文件org.springframework.amqp,如下 <dependency> ...
- Spring的远程调用
Spring远程支持是由普通(Spring)POJO实现的,这使得开发具有远程访问功能的服务变得相当容易 四种远程调用技术: ◆ 远程方法调用(RMI) ◆ Caucho的Hessian和Burlap ...
- (转)RabbitMQ消息队列(七):适用于云计算集群的远程调用(RPC)
在云计算环境中,很多时候需要用它其他机器的计算资源,我们有可能会在接收到Message进行处理时,会把一部分计算任务分配到其他节点来完成.那么,RabbitMQ如何使用RPC呢?在本篇文章中,我们将会 ...
- RabbitMQ消息队列(七):适用于云计算集群的远程调用(RPC)
在云计算环境中,很多时候需要用它其他机器的计算资源,我们有可能会在接收到Message进行处理时,会把一部分计算任务分配到其他节点来完成.那么,RabbitMQ如何使用RPC呢?在本篇 ...
- RabbitMQ 适用于云计算集群的远程调用(RPC)
在云计算环境中,很多时候需要用它其他机器的计算资源,我们有可能会在接收到Message进行处理时,会把一部分计算任务分配到其他节点来完成.那么,RabbitMQ如何使用RPC呢?在本篇文章中,我们将会 ...
- RabbitMQ学习总结 第四篇:发布/订阅 Publish/Subscribe
目录 RabbitMQ学习总结 第一篇:理论篇 RabbitMQ学习总结 第二篇:快速入门HelloWorld RabbitMQ学习总结 第三篇:工作队列Work Queue RabbitMQ学习总结 ...
- [置顶] android调用第三方库——第四篇——调用多个第三方库
0:前言: 在前面三篇中我们介绍了android调用第三方库的形式,在这一篇中我们介绍调用多个第三方库的Android.mk的写法,由于其他三篇介绍的很详细,这里只给出Android.mk的内容. [ ...
- RabbitMQ远程调用测试用例
RabbitMQ远程调用测试,使用外部机器192.168.174.132上的RabbitMQ,使用之前需要对远程调用进行配置,操作过程见博文“解决RabbitMQ远程不能访问的问题”. SendTes ...
- 深入浅出Alljoyn——实例分析之远程调用(Method)篇
深入浅出就是很深入的学习了很久,还是只学了毛皮,呵呵! 服务端完整代码: #include <qcc/platform.h> #include <assert.h> #incl ...
随机推荐
- 我的iOS之路2
第一篇果然没人看呢...好吧...那就当笔记做吧...第一篇主要写的是生成了一个helloworld,可能也是和别人撞了,所以有点厌烦,连名字都改掉吧 好,开始写第二篇 之前做第一个应用程序的时候没有 ...
- Teamcenter 2007 "由于某些意外的 DBMS 错误失败"
PLM系统实施过程中经常需要做整个系统的克隆备份.Teamcenter2007在做基于Oracel的系统克隆时,用exp功能导出数据表,部署在目标机器上时会遇到DBMS错误,导致后续DB写操作无法进行 ...
- Android 把电话保存到现有联系人 已有联系人
搜索了很长时间,想找个把电话保存到现有联系人的代码,就是打开选中的联系人编辑界面,然后自动添加电话,再手动保存,就跟手机上的一样,功夫不负有心人,终于给搜到了,很不容易啊,现分享如下, // 保存至现 ...
- 从RAM新建QIcon对象 / Create a QIcon from binary data
一般,QIcon是通过png或ico等图标文件来初始化的,但是如果图标资源已经在内存里了,或者一个zip压缩文件内,可以通过QPixmap作为桥梁,转换为图标. zf = zipfile.ZipFil ...
- TCP Fast Open
We know that Web services use the TCP protocol at the transport layer. Standard TCP protocol to thre ...
- PDO vs. MySQLi 选择哪一个?(PDO vs. MySQLi: Which Should You Use?)-转载
用Php访问数据的时候,你选择MySQLi和PDO,在选择之前,你应该知道些什么呢? 这篇文章将会介绍这两种方式的不同点,数据库的支持.稳定性.性能等问题. 概述 PDO MySQLi Datab ...
- android 退出机制
android sdk 退出机制的研究 有多种, 方法一.用list保存activity实例,然后逐一干掉 上代码: import java.util.LinkedList; import java. ...
- SQLServer 获取第几周开始日期
不多说直接上code DECLARE @CurrDay DATETIME=GETDATE() --SET @CurrDay=CAST(('2013-01-10')AS DATETIME) --SET ...
- 如何基于RabbitMQ实现优先级队列
概述 由于种种原因,RabbitMQ到目前为止,官方还没有实现优先级队列,只实现了Consumer的优先级处理. 但是,迫于种种原因,应用层面上又需要优先级队列,因此需求来了:如何为RabbitMQ加 ...
- Oracle Enterprise Manager打不开的解决方法
之前OEM一直可以打开,但今天上班发现打不开了,输入http://localhost:1158/em 提示该网页无法打开. 那么检查一下: cmd进命令行 C:\Documents and Setti ...