RabbitMQ第四篇:远程调用
来源:互联网 发布:python 给字典赋值 编辑:程序博客网 时间:2024/05/21 05:06
前言:前面我们讲解的都是本地服务器,现在如果需要远程计算机上运行一个函数,等待结果。这就是一个不同的故事了,这种模式通常被称为远程过程调用或者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工作方式如下:
- 当客户端启动时,它创建一个匿名的独占回调队列。
- 对于rpc请求,客户端发送2个属性,一个是replyTo设置回调队列,另一是correlationId为每个队列设置唯一值
- 请求被发送到一个rpc_queue队列中
- rpc服务器是等待队列的请求,当收到一个请求的时候,他就把消息返回的结果返回给客户端,使请求结束。
- 客户端等待回调队列上的数据,当消息出现的时候,他检查correlationId,如果它和从请求返回的值匹配,就进行响应。
编码
RPCServer.Java
/** * june.mq:com.june.mq.rabbit.rpc.RPCServer.java * 日期:2017年7月13日 */package com.june.mq.rabbit.rpc;import static com.june.mq.rabbit.Consts.*;import java.io.IOException;import java.util.concurrent.TimeoutException;import com.rabbitmq.client.AMQP;import com.rabbitmq.client.AMQP.BasicProperties;import com.rabbitmq.client.Channel;import com.rabbitmq.client.Connection;import com.rabbitmq.client.ConnectionFactory;import com.rabbitmq.client.ConsumerCancelledException;import com.rabbitmq.client.QueueingConsumer;import com.rabbitmq.client.ShutdownSignalException;/** * RPCServer <br> * * @author 王俊伟 wjw.happy.love@163.com * @blog https://www.github.com/junehappylove * @date 2017年7月13日 下午3:18:15 * @version 1.0.0 */public class RPCServer { /** * @param args * @throws TimeoutException * @throws IOException * @throws InterruptedException * @throws ConsumerCancelledException * @throws ShutdownSignalException * @date 2017年7月13日 下午3:18:15 * @writer junehappylove */ public static void main(String[] args) throws IOException, TimeoutException, ShutdownSignalException, ConsumerCancelledException, InterruptedException { 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); } } private static int fib(int n) { if (n == 0) { return 0; } if (n == 1) { return 1; } return fib(n - 1) + fib(n - 1); }}
服务器代码比较简单
- 建立连接,通道,队列
- 我们可能运行多个服务器进程,为了分散负载服务器压力,我们设置channel.basicQos(1);
- 我们用basicconsume访问队列。然后进入循环,在其中我们等待请求消息并处理消息然后发送响应。
RPCClient.java
/** * june.mq:com.june.mq.rabbit.rpc.RPCClient.java * 日期:2017年7月13日 */package com.june.mq.rabbit.rpc;import static com.june.mq.rabbit.Consts.*;import java.io.IOException;import java.util.UUID;import java.util.concurrent.TimeoutException;import com.rabbitmq.client.AMQP;import com.rabbitmq.client.Channel;import com.rabbitmq.client.Connection;import com.rabbitmq.client.ConnectionFactory;import com.rabbitmq.client.QueueingConsumer;/** * RPCClient <br> * * @author 王俊伟 wjw.happy.love@163.com * @blog https://www.github.com/junehappylove * @date 2017年7月13日 下午3:22:10 * @version 1.0.0 */public class RPCClient { private Connection connection; private Channel channel; private String requestQueueName = RPC_QUEUE_NAME; private String replyQueueName; private QueueingConsumer consumer; public RPCClient() throws IOException, TimeoutException { ConnectionFactory factory = new ConnectionFactory(); factory.setHost(host); factory.setUsername(username); factory.setPassword(password); factory.setPort(port); factory.setVirtualHost(virtualHost); 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(); } /** * @param args * @throws Exception * @date 2017年7月13日 下午3:22:10 * @writer junehappylove */ 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(); } } }}
客户端代码解读
- 建立一个连接和通道,并声明了一个唯一的“回调”队列的答复
- 我们订阅回调队列,这样就可以得到RPC的响应
- 定义一个call方法用于发送当前的回调请求
- 生成一个唯一的correlationid,然后通过while循环来捕获合适的回应
- 我们请求信息,发送2个属性,replyTo 和correlationId
- 然后就是等待直到有合适的回应到达
- while循环是做一个非常简单的工作,对于每一个响应消息,它检查是否有correlationid然后进行匹配。然后是就进行响应。
- 最后把响应返回到客户端。
运行结果:
首先启动 RPCServer
,之后会提示:
RPCServer Awating RPC request
然后启动RPCClient
,输出:
RPCClient Requesting fib(20)
随即服务端打印:
RPCServer fib(20)
而后客户端输出:
RPCClient Got '524288'
客户端运行结束!而服务端等待客户端的下一次rpc调用。
RPCServer Awating RPC requestRPCServer fib(20)---------------------RPCClient Requesting fib(20)RPCClient Got '524288'
- rabbitMQ第四篇:远程调用
- RabbitMQ第四篇:远程调用
- rabbitMQ第四篇:远程调用
- rabbitMQ第四篇:远程调用
- rabbitMQ:远程调用
- RabbitMQ 远程过程调用RPC
- 11-rabbitmq-远程过程调用
- java远程连接调用Rabbitmq
- RabbitMQ (六)远程调用RPC
- RabbitMQ (六) 远程过程调用(RPC)
- RabbitMQ学习小结(六)----远程调用
- RabbitMQ消息队列(五):RPC远程调用
- 12-rabbitmq-远程过程调用-spring
- RabbitMQ (八) 远程调用(RPC)
- RabbitMQ入门教程(八):远程过程调用RPC
- RabbitMQ学习之基于spring-rabbitmq的RPC远程调用
- RabbitMQ学习之基于spring-rabbitmq的RPC远程调用
- RabbitMQ学习之基于spring-rabbitmq的RPC远程调用
- js-apply()
- 欢迎使用CSDN-markdown编辑器
- 二叉树
- mybatis批量新增,存在就更新(mysql数据库)
- IO流详细学习笔记
- RabbitMQ第四篇:远程调用
- hdu 1166 敌兵布阵 树状数组的运用
- 【Java】数组拷贝的三种方式
- 图像离散傅里叶变化幅度谱检测---在条形码识别中的作用
- Java过滤器与SpringMVC拦截器之间的关系与区别
- crontab处理java定时任务
- 基本类型与包装类型
- 栈和队列的相关面试题
- 谈谈这几天加班的感受