RabbitMQ中的RPC实现
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实现的更多相关文章
- RabbitMQ中RPC的实现及其通信机制
RabbitMQ中RPC的实现:客户端发送请求消息,服务端回复响应消息,为了接受响应response,客户端需要发送一个回调队列的地址来接受响应,每条消息在发送的时候会带上一个唯一的correlati ...
- RabbitMQ中 exchange、route、queue的关系
从AMQP协议可以看出,MessageQueue.Exchange和Binding构成了AMQP协议的核心,下面我们就围绕这三个主要组件 从应用使用的角度全面的介绍如何利用Rabbit MQ构建 ...
- RabbitMQ学习之RPC(6)
在第二个教程中,我们了解到如何在多个worker中使用Work Queues分发费时的任务. 但是,如果我们需要在远程运行一个函数并且等待结果该怎么办呢?这个时候,我们需要另外一个模式了.这种模式通常 ...
- 深入理解RabbitMQ中的prefetch_count参数
前提 在某一次用户标签服务中大量用到异步流程,使用了RabbitMQ进行解耦.其中,为了提高消费者的处理效率针对了不同节点任务的消费者线程数和prefetch_count参数都做了调整和测试,得到一个 ...
- 3 weekend110的hadoop中的RPC框架实现机制 + hadoop中的RPC应用实例demo
hadoop中的RPC框架实现机制 RPC是Remotr Process Call, 进程间的远程过程调用,不是在一个jvm里. 即,Controller拿不到Service的实例对象. hadoop ...
- golang中的rpc包用法
RPC,即 Remote Procedure Call(远程过程调用),说得通俗一点就是:调用远程计算机上的服务,就像调用本地服务一样. 我所在公司的项目是采用基于Restful的微服务架构,随着微服 ...
- 分布式系统中的RPC请求经常出现乱序的情况 写一个算法来将一个乱序的序列保序输出
分布式系统中的RPC请求经常出现乱序的情况. 写一个算法来将一个乱序的序列保序输出.例如,假设起始序号是1,对于(1, 2, 5, 8, 10, 4, 3, 6, 9, 7)这个序列,输出是: 1 ...
- RabbitMQ中交换机的消息分发机制
RabbitMQ是一个消息代理,它接受和转发消息,是一个由 Erlang 语言开发的遵循AMQP协议的开源实现.在RabbitMQ中生产者不会将消息直接发送到队列当中,而是将消息直接发送到交换机(ex ...
- 带着新人学springboot的应用06(springboot+RabbitMQ 中)
上一节说了这么多废话,看也看烦了,现在我们就来用鼠标点点点,来简单玩一下这个RabbitMQ. 注意:这一节还是不用敲什么代码,因为上一节我们设置了那个可视化工具,我们先用用可视化工具熟悉一下流程. ...
随机推荐
- CMD命令:不是内部或者外部命令也不是可运行的程序或批处理文件
[本文转自:https://blog.csdn.net/l_mloveforever/article/details/79513681] 前言: 相信有很多小伙伴都比较喜欢使用Command命令来 ...
- 在docker中快速创建包含ip相关tool的ubuntu镜像
在docker学习中需要创建轻量级的,包含ip相关工具的容器,支持ping,ip,ethtool,brctrl等相关指令. 下面就是快速创建一个满足需求的ubunut镜像的过程: 1) 在docker ...
- C# Timer 定时任务
C#中,Timer是一个定时器,它可以按照指定的时间间隔或者指定的时间执行一个事件. 指定时间间隔是指按特定的时间间隔,如每1分钟.每10分钟.每1个小时等执行指定事件: 指定时间是指每小时的第30分 ...
- pytorch使用总结
loss的获取 在看别人代码的时候发现都是 loss=net.loss train_loss+=loss.data[0]#train_loss用于累加梯度 在想为什么不直接使用loss呢,因为pyto ...
- Linux三种网络连接模式
https://www.cnblogs.com/linjiaxin/p/6476480.html 三种模式的区别:https://www.cnblogs.com/itxiaok/p/10358055. ...
- MS17-010漏洞复现
攻击机:192.168.148.132 kali linux2018.2 x64 靶机:192.168.1.129 win7 x64 首先用msfconsole的smb模块扫描,看看是 ...
- HDFS的上传与下载(put & get)
最近在做一个小任务,将一个CDH平台中Hive的部分数据同步到另一个平台中.毕竟我也刚开始工作,在正式开始做之前,首先进行了一段时间的练习,下面的内容就是练习时写的文档中的内容.如果哪里有错误或者疏漏 ...
- Linux桌面系统常用软件和笔记(更新)
(一).下文涉及到的环境 manjaro.deepin.Arch等 有些可以通用 有些不可以通用 (二).常用软件下载 一.桌面美化软件 1.桌面壁纸下载软件: varirety 下载方式:可以在多个 ...
- Linux 问题
Loaded plugins: fastestmirror cd /etc/yum.repos.d mv CentOS-Base.repo CentOS-Base.repo.backup wget h ...
- jq鼠标事件
鼠标事件是在用户移动鼠标光标或者使用任意鼠标键点击时触发的. (1)click单击鼠标事件:click事件于用户在元素敲击鼠标左键,并在相同元素上松开左键时触发. $('p').cl ...