1、RPC简述

RPC,Remote Procedure Call 远程过程调用。通俗讲,两段程序不在同一个内存空间,无法直接通过方法名调用,就需要通过网络通信方式调用。对于RabbitMQ,本身就是用于消息通信。简单的RabbitMQ是,生产端发送消息,经由交换器,到达队列。消费端不需要知道生产端,消费端订阅队列,消费队列中的消息。而对于RPC,希望消费端消费消息后,返回一个结果,结果经由网络,再返回给生产端。

不考虑RabbitMQ针对RPC的特有设计。最简单的设计是,生产端和消费端共同约定消费队列和回复队列,同时生产端每次发送消息时指定一个唯一ID。生产端将消息和唯一ID发送给消费队列,消费者从消费队列获取消息。处理后,将结果和生产端发送过来的唯一ID,发送给回复队列。生产端从回复队列获取消息和ID,判断ID是否匹配,匹配,则此消息为回复消息。

以上实现的RPC存在问题:生产端和消费端需要约定回复队列,这就要求生产端和消费端互相知道,这无法实现解耦。解决方案:生产端在发送消息时,也将回复队列名称随消息一起发送给队列。

2、举例说明RabbitMQ中的RPC实现

相关要点,见源码注释

pom依赖

   <dependencies>
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>4.6.0</version>
</dependency>
</dependencies>

生产端,也就是客户端代码

 package test;

 import java.io.IOException;
import java.util.UUID; import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope; import utils.ChannelUtils; public class RPCClient { public static void main(String[] args) throws IOException {
RPCClient rpcClient = new RPCClient();
rpcClient.client();
} public void client() throws IOException {
//此方法封装了如何连接RabbitMQ和创建connection,channel.源码见附录
Channel channel = ChannelUtils.getChannelInstance("client");
channel.exchangeDelete("exchange_rpc");
channel.exchangeDeclare("exchange_rpc", "direct", false, false, null); channel.queueDelete("queue_rpc");
channel.queueDeclare("queue_rpc", false, false, false, null); channel.queueBind("queue_rpc", "exchange_rpc", "rpc"); //此处注意:我们声明了要回复的队列。队列名称由RabbitMQ自动创建。
//这样做的好处是:每个客户端有属于自己的唯一回复队列,生命周期同客户端
String replyQueue = channel.queueDeclare().getQueue();
final String corrID = UUID.randomUUID().toString(); //这里我们设计三类消息。
//消息1:指定回复队列和ID
//消息2:仅指定回复队列
//消息3:不指定回复队列和ID
AMQP.BasicProperties.Builder builder = new AMQP.BasicProperties.Builder();
// 指定回复队列和回复correlateId
builder.replyTo(replyQueue).correlationId(corrID);
AMQP.BasicProperties properties = builder.build();
for (int i = 0; i < 2; i++) {
channel.basicPublish("exchange_rpc", "rpc", properties,
(System.currentTimeMillis() + "-rpc发送消息1").getBytes());
} AMQP.BasicProperties.Builder builder2 = new AMQP.BasicProperties.Builder();
// 指定回复队列,未指定回复correlateId
builder2.replyTo(replyQueue);
AMQP.BasicProperties properties2 = builder2.build();
for (int i = 0; i < 2; i++) {
channel.basicPublish("exchange_rpc", "rpc", properties2,
(System.currentTimeMillis() + "-rpc发送消息2").getBytes());
} for (int i = 0; i < 2; i++) {
// 未指定回复队列和correlateId
channel.basicPublish("exchange_rpc", "rpc", null, (System.currentTimeMillis() + "-rpc发送消息3").getBytes());
} DefaultConsumer c = new DefaultConsumer(channel) {
//这是一个回调函数,客户端获取消息,就调用此方法,处理消息
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
if (corrID.equals(properties.getCorrelationId())) {
System.out.println("correlationID对应上的消息:" + new String(body));
} else {
System.out.println("correlationID未对应上的消息:" + new String(body));
}
}
};
channel.basicConsume(replyQueue, true, c);
} }

消费端,也就是服务器端代码

 package test;

 import java.io.IOException;

 import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties; import utils.ChannelUtils; public class RPCServer { public static void main(String[] args) throws IOException {
RPCServer rpcServer = new RPCServer();
rpcServer.Server();
} public void Server() throws IOException {
final Channel channel = ChannelUtils.getChannelInstance("server"); DefaultConsumer c = new DefaultConsumer(channel) { //这是一个回到函数,服务器端获取到消息,就会调用此方法处理消息
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
System.out.println(new String(body)); AMQP.BasicProperties.Builder builder = new AMQP.BasicProperties.Builder();
//我们在将要回复的消息属性中,放入从客户端传递过来的correlateId
builder.correlationId(properties.getCorrelationId());
AMQP.BasicProperties prop = builder.build(); //发送给回复队列的消息,exchange="",routingKey=回复队列名称
//因为RabbitMQ对于队列,始终存在一个默认exchange="",routingKey=队列名称的绑定关系
channel.basicPublish("", properties.getReplyTo(), prop, (new String(body) + "-回复").getBytes()); }
};
channel.basicConsume("queue_rpc", true, c);
} }

附录:ChannelUtils 的源码

package utils;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory; public class ChannelUtils { // AMQP的连接其实是对Socket做的封装, 注意以下AMQP协议的版本号,不同版本的协议用法可能不同。
public static Channel getChannelInstance(String ConnectionDescription) {
try {
ConnectionFactory connectionFactory = getConnectionFactory();
Connection connection = connectionFactory.newConnection(ConnectionDescription); return connection.createChannel();
} catch (Exception e) {
throw new RuntimeException("获取Channel连接失败");
} } public static ConnectionFactory getConnectionFactory() {
ConnectionFactory connectionFactory = new ConnectionFactory(); connectionFactory.setHost("192.168.1.111");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setUsername("drs");
connectionFactory.setPassword("123456"); return connectionFactory;
} }

RabbitMQ中的RPC实现的更多相关文章

  1. RabbitMQ中RPC的实现及其通信机制

    RabbitMQ中RPC的实现:客户端发送请求消息,服务端回复响应消息,为了接受响应response,客户端需要发送一个回调队列的地址来接受响应,每条消息在发送的时候会带上一个唯一的correlati ...

  2. RabbitMQ中 exchange、route、queue的关系

    从AMQP协议可以看出,MessageQueue.Exchange和Binding构成了AMQP协议的核心,下面我们就围绕这三个主要组件    从应用使用的角度全面的介绍如何利用Rabbit MQ构建 ...

  3. RabbitMQ学习之RPC(6)

    在第二个教程中,我们了解到如何在多个worker中使用Work Queues分发费时的任务. 但是,如果我们需要在远程运行一个函数并且等待结果该怎么办呢?这个时候,我们需要另外一个模式了.这种模式通常 ...

  4. 深入理解RabbitMQ中的prefetch_count参数

    前提 在某一次用户标签服务中大量用到异步流程,使用了RabbitMQ进行解耦.其中,为了提高消费者的处理效率针对了不同节点任务的消费者线程数和prefetch_count参数都做了调整和测试,得到一个 ...

  5. 3 weekend110的hadoop中的RPC框架实现机制 + hadoop中的RPC应用实例demo

    hadoop中的RPC框架实现机制 RPC是Remotr Process Call, 进程间的远程过程调用,不是在一个jvm里. 即,Controller拿不到Service的实例对象. hadoop ...

  6. golang中的rpc包用法

    RPC,即 Remote Procedure Call(远程过程调用),说得通俗一点就是:调用远程计算机上的服务,就像调用本地服务一样. 我所在公司的项目是采用基于Restful的微服务架构,随着微服 ...

  7. 分布式系统中的RPC请求经常出现乱序的情况 写一个算法来将一个乱序的序列保序输出

    分布式系统中的RPC请求经常出现乱序的情况.  写一个算法来将一个乱序的序列保序输出.例如,假设起始序号是1,对于(1, 2, 5, 8, 10, 4, 3, 6, 9, 7)这个序列,输出是:  1 ...

  8. RabbitMQ中交换机的消息分发机制

    RabbitMQ是一个消息代理,它接受和转发消息,是一个由 Erlang 语言开发的遵循AMQP协议的开源实现.在RabbitMQ中生产者不会将消息直接发送到队列当中,而是将消息直接发送到交换机(ex ...

  9. 带着新人学springboot的应用06(springboot+RabbitMQ 中)

    上一节说了这么多废话,看也看烦了,现在我们就来用鼠标点点点,来简单玩一下这个RabbitMQ. 注意:这一节还是不用敲什么代码,因为上一节我们设置了那个可视化工具,我们先用用可视化工具熟悉一下流程. ...

随机推荐

  1. [Windows]卸载Office 2016密钥

    cscript "C:\Program Files (x86)\Microsoft Office\Office16\ospp.vbs" /dstatus BTDRB

  2. 转发 ----> 2018年阿里巴巴重要开源项目汇总(持续更新中)

    转发自segmentfault  https://segmentfault.com/a/1190000017346799 前端 1.数据驱动的高交互可视化图形语法 AntV - G2 G2 是一套基于 ...

  3. 小程序tab切换 点击左右滑动

    wxml <scroll-view scroll-x="true" class="navbar-box"> <block wx:for=&qu ...

  4. vue中<select>绑定事件

    <div id="app"> <select v-model="selectItem" @change="selectFn($eve ...

  5. mac电脑上不能用移动硬盘的原因和方法

    原因: 一般性是因为这个移动硬盘的格式是 NTFS 格式的,对于这种格式的磁盘格式,在苹果系统中却是不支持往硬盘里写入数据的 解决方法: 方法一: ntfs的格式分区,这种格式分区与我们的苹果电脑自身 ...

  6. 对mybatis的Handler 从使用角度介绍

    最近在开发中,涉及到了讲数据库查询的类型,直接转为java需要的类型. 由于对handler 理解不到位 和 使用不当.躺了一些坑. 主要涉及的有2种. 1.varchar 转 List<T&g ...

  7. spark mllib prefixspan demo

    ./bin/spark-submit ~/src_test/prefix_span_test.py source code: import os import sys from pyspark.mll ...

  8. 中文乱码 URLEncode之后,后台获取仍是乱码问题详解

    java中获取到参数的时候,这时候默认使用的是iso8859-1进行解码的,那么就再使用URLEncode的encoe方法对其进行编码一次,编码格式使用iso8859-1,这样我们就获得最初使用utf ...

  9. Chromedriver executable needs to be in path 解决办法

    执行webdriver.Chrome()时报错:Chromedriver executable needs to be in path. 原因可能是为有安装Chromedriver 可能是Chrome ...

  10. windows系统,boost编译安装

    windows系统,boost编译安装vs2017 1.下载https://www.boost.org/users/download/下载windows对应的zip包解压 2.配置vc环境变量我的是: ...