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. 第一章 Java语言概述1

    1.人机交互有两种方法:一种是图形化界面,一种是命令行方式 2.如何打开命令行:开始-在运行命令行中输入cmd 3.常用的DOS命令: dir(directory):列出当前目录下文件及文件夹 md( ...

  2. react props与render成员函数

    props是组件固有的属性集合,其数据由外部传入,一般在整个组件的生命周期中都是只读的,React的API顶层设计也决定了这一点.属性初值通常由React.createElement函数或者JSX中标 ...

  3. Rails6使用tailwind CSS

    tailwind的特色(在Bootstrap 和vanilla Css之间) https://tailwindcss.com/docs/what-is-tailwind/ Rails6 six版本的R ...

  4. background——背景属性

    一.背景属性 1.1.背景颜色background-color <style> /*浮动,横向排列*/ div{float: left;} /*background-color属性值支持三 ...

  5. YouTuboba视频搬运~哔哩哔哩

    将YouTube上面的视频搬运到哔哩哔哩上面教程 1.首先选择YouTube上面一个视频,需要谷歌登录,然后保存这个视频播放链接. 2.在浏览器中输入这个网址:en.savefrom.net,点击En ...

  6. play framework 笔记

    @main("Hello")调用 main.scala.hmtl,页面名称 Hello conf/routes 文件定义了映射规则 参数的传递入口在 controller 的方法中 ...

  7. rtmp详解

    文件下载地址: 中文:https://files.cnblogs.com/files/bugutian/rtmp_specification_1.0_cn.zip 英文:http://www.adob ...

  8. C++基础学习_01

    C++基础学习_01 基础知识:1.命名空间,2.IO流(输入输入),3.参数缺省,4.函数重载 1.命名空间 作用:对标识符的名称进行本地化,避免命名冲突 定义:namaspace space_na ...

  9. Cognos集成至portal平台查看报表报错RSV-BBP-0022

    1. 问题描述 绝对密切性请求“asynchWait_Request”失败,所请求的会话不存在. 2. 问题分析 Session会话传递失败. 3. 解决方案 将cognos所在服务器地址IP添加进I ...

  10. 简述 JVM 垃圾回收算法

    经典垃圾回收 标记-清除(Mark-Sweep) 研发园开了家新餐厅,餐厅老板在考虑如何回收餐盘时首先使用了最简单的方式,那就是服务员在顾客用餐的过程中,不定时的观察餐厅,针对用完餐的顾客记录他们的位 ...