试着用RabbitMQ进行RPC。

其实用RabbitMQ搞RPC也没什么特别的。
只是我们需要在请求中再加入一个callback queue。
比如这样:

callbackQueueName = channel.queueDeclare().getQueue();

BasicProperties props = new BasicProperties
.Builder()
.replyTo(callbackQueueName)
.build(); channel.basicPublish("", "rpc_queue", props, message.getBytes());

剩下的工作就是等待对方处理完成再从callback队列中读取响应消息。

上面用到了BasicProperties。
(注意:是com.rabbitmq.client.AMQP.BasicProperties 不是 com.rabbitmq.client.BasicProperties)
关于Message properties,AMQP协议为消息预定义了14种属性。

        private String contentType;
private String contentEncoding;
private Map<String,Object> headers;
private Integer deliveryMode;
private Integer priority;
private String correlationId;
private String replyTo;
private String expiration;
private String messageId;
private Date timestamp;
private String type;
private String userId;
private String appId;
private String clusterId;

通常我们只需要使用其中一小部分:
·deliveryMode: 将消息设置为持久或者临时,2为持久,其余为临时。
·contentType: 指定mime-type,比如要使用JSON就是application/json
·replyTo: 指定callback queue的名字
·correlationId: 用来关联RPC请求和响应的标识。
上面那段代码中就是用到了correlationId。

另外需要说明这个correlationId。
其实在上面的代码中我们为每一个RPC请求都创建了一个回调队列。
但这样明显不效率,我们可以为每一个客户端只创建一个回调队列。

但这样我们又需要考虑另一个问题:<当我们将收到的消息放到队列时,如何确定该消息是属于哪个请求?>

这时我们可以使用correlationId解决这个问题。
我们可以用它来为每一个请求加上标识,获取信息时对比这个标识,以对应请求和响应。
如果我们收到了无法识别的correlationId,即该响应不与任何请求匹配,那么这个消息将会废除。

好了,代码比较简单。

class RPCServer{
private static final String RPC_QUEUE_NAME = "rpc_queue";
public static void main(String[] args) throws Exception { 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(" [x] Awaiting RPC requests"); while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery(); BasicProperties props = delivery.getProperties();
BasicProperties replyProps = new BasicProperties
.Builder()
.correlationId(props.getCorrelationId())
.build(); String message = new String(delivery.getBody());
int n = Integer.parseInt(message); System.out.println(" [.] fib(" + message + ")");
String response = "" + fib(n); channel.basicPublish( "", props.getReplyTo(), replyProps, response.getBytes()); channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
} } private static int fib(int n) throws Exception {
if (n == 0) return 0;
if (n == 1) return 1;
return fib(n-1) + fib(n-2);
}
}

由于是共享队列,这里我们就不用exchange和routing了。
另外,有时我们可能需要运行多个服务,为了让多个服务端负载均衡,我们可以使用prefetchCount。
这个属性在之前任务队列的例子里也用过,也就是

workerChannel.basicQos(1);

即让多个worker一次获取一个任务。
用basicConsume方法进入队列后循环等待请求,发现有请求到达时根据队列和CorrelationId对相应请求作出响应。

另外需要注意的一点,server中basicConsume的第二个参数是false。
其意义为是否自动作出回应,即:
true if the server should consider messages acknowledged once delivered; false if the server should expect explicit acknowledgements
于是循环时需要显示调用basicAck进行回应。

class RPCClient{

    private Connection connection;
private Channel channel;
private String requestQueueName = "rpc_queue";
private String replyQueueName;
private QueueingConsumer consumer; public RPCClient() throws Exception {
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 Exception {
String response = null;
String corrId = java.util.UUID.randomUUID().toString(); BasicProperties props = new BasicProperties
.Builder()
.correlationId(corrId)
.replyTo(replyQueueName)
.build(); channel.basicPublish("", requestQueueName, props, message.getBytes()); while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
if (delivery.getProperties().getCorrelationId().equals(corrId)) {
response = new String(delivery.getBody());
break;
}
} return response;
} public void close() throws Exception {
connection.close();
}
}

callback队列只是一个匿名队列,但切记需要将其设置到BasicProperties中。
corrId的生成方法有很多种,在这里使用UUID。
call方法中通过调用basicPublish进行RPC请求,参数中带着BasicProperties。

RabbitMQ - 远程过程调用的更多相关文章

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

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

  2. Thrift架构~从图中理解thrift,它事实上是一种远程过程调用

    thrift为我们简化了tcp通讯,它可以使用我们方便的建立各种语言的服务端与客户端,并实现客户端对服务器的远程过程调用,简单的说就是服务器通过thrift架构对外开放一些接口,并自己实现这些接口,如 ...

  3. rabbitMQ学习3-RPC远程过程调用

    将一个函数运行在远程计算机上并且等待获取那里的结果,这个称作远程过程调用(Remote Procedure Call)或者 RPC. RPC是一个计算机通信协议. 比喻 将计算机服务运行理解为厨师做饭 ...

  4. RabbitMQ九:远程过程调用RPC

    定义 RPC(Remote Procedure Call Protocol)——远程过程调用协议:它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议.RPC协议假定某些传输协议 ...

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

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

  6. RabbitMQ学习系列(五): RPC 远程过程调用

    前面讲过一些RabbitMQ的安装和用法,也说了说RabbitMQ在一般的业务场景下如何使用.不知道的可以看我前面的博客,http://www.cnblogs.com/zhangweizhong/ca ...

  7. RabbitMQ 实现远程过程调用RPC

    RPC调用的顺序1. 在客户端初始化的时候,也就是SimpleRpcClient类初始化的时候,它会随机的创建一个callback队列,用于存放服务的返回值,这个队列是exclusive的.连接断开就 ...

  8. python中RabbitMQ的使用(远程过程调用RPC)

    在RabbitMQ消息队列中,往往接收者.发送者不止是一个身份.例如接接收者收到消息并且需要返回给发送者. 此时接收者.发送者的身份不再固定! 我们来模拟该情形: 假设有客户端client,服务端se ...

  9. RPC远程过程调用

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

随机推荐

  1. 性能测试工具Locust的使用----TaskSet类~~task任务嵌套

    内容来自网络 http://blog.sina.com.cn/s/blog_a7ace3d80102w9r0.html TaskSet类 正如字面意思,TaskSet类定义了每个用户的任务集合,测试任 ...

  2. OCP认证052考试最新考试题库和答案整理-33

    33.Where Is backup metadata stored for use by Recovery Manager (RMAN)? A) In the control file B) In ...

  3. go语言实战教程:Redis实战项目应用

    项目Redis配置 在实战项目中使用Redis功能,首先需要进行Redis配置.本实战项目中,关与Redis的配置项包含:连接类型.地址.端口.公共前缀.以上配置项被定义包含在Iris框架的redis ...

  4. POJ - 2421 Constructing Roads (最小生成树)

    There are N villages, which are numbered from 1 to N, and you should build some roads such that ever ...

  5. mysql主从同步错误,提示The server quit without updating PID file

    在安装完lnmp后,启动mysqld失败,提示 [root@centos-6 ~]# service mysqld start Starting MySQL [确定][root@centos-6 ~] ...

  6. Runas命令巧用

    Runas,从字面上看就知道,以谁的身份去run一个程序,那么他就是在当前登陆的Windows帐号下,以本地或者是域中的其他帐号的身份去运行一个程序.简明语法如下: Runas /user:usern ...

  7. SELinux 引起的 Docker 启动失败

    问题描述 Linux OS 版本 CentOS Linux release 7.2.1511 (Core) 启动Docker service docker start 启动失败信息 原因分析 Erro ...

  8. javascipt中数组的常见操作

    1.与lua不同,即使数组中间有null或者undefined,仍然能求出正确的长度 2.length 改变.length也会改变数组的大小 3. indexOf arr.indexOf(a)会返回第 ...

  9. php 图片上传 并返回上传文件位置 支持多文件上传

    <?php /** * Created by PhpStorm. * User: DY040 * Date: 2018/4/26 * Time: 13:23 */ echo '<pre&g ...

  10. PZ73H-PZ73X刀闸阀厂家,PZ73H-PZ73X刀闸阀价格 - 专题栏目 - 无极资讯网

    无极资讯网 首页 最新资讯 最新图集 最新标签   搜索 PZ73H-PZ73X刀闸阀 无极资讯网精心为您挑选了(PZ73H-PZ73X刀闸阀)信息,其中包含了(PZ73H-PZ73X刀闸阀)厂家,( ...