【RabbitMQ】6.RabbitMQ 远程过程调用RPC

假如我们想要调用远程的一个方法或函数并等待执行结果,也就是我们通常说的远程过程调用(Remote Procedure Call)。怎么办?

今天我们就用RabbitMQ来实现一个简单的RPC系统:客户端发送一个请求消息,服务端以一个响应消息回应。为了能够接收到响应,客户端在发送消息的同时发送一个回调队列用来告诉服务端响应消息发送到哪个队列里面。也就是说每个消息一个回调队列,在此基础上我们变下,将回调队列定义成类的属性,这个每个客户端一个队列,同一个客户端的请求共用一个队列。那么接下来有个问题,怎么知道这个队列里面的响应消息是属于哪个队列的呢?

我们会用到关联标识(correlationId),每个请求我们都会生成一个唯一的值作为correlationId,这样每次有响应消息来的时候,我们就去看correlationId来确定到底是哪个请求的响应消息,将请求和响应关联起来。如果收到一个不知道的correlationId,就可以确定不是这个客户端的请求的响应,可以直接丢弃掉。

一、工作模型

  1. 客户端发送启动后,会创建独特的回调队列。对于一个请求发送配置了两个属性的消息:一个是回调队列(图中的replay_to),一个是correlation_id。 这个请求会发送到rpc_queue队列,然后到达服务端处理。
  2. 服务端等待rpc_queue队列的请求。当有请求到来时,它就会开始干活并将结果通过发送消息来返回,该返回消息发送到replay_to指定的队列。
  3. 客户端将等待回调队列返回数据。当返回的消息到达时,它将检查correlation_id属性。如果该属性值和请求匹配,就将响应返回给程序。

二、代码实现

客户端:发送请求的时候,它是生产者;接受响应的时候,它是消费者。

package com.bj.rabbitmq.study.five;

import java.io.IOException;
import java.util.UUID;
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;

public class RPCSend {
	//用于客户端发送消息到服务器端的队列
	private static final String RPC_QUEUE_NAME = "rpc_queue"; 
	public static void main(String[] args) throws IOException, ShutdownSignalException, ConsumerCancelledException, InterruptedException {
		ConnectionFactory factory = new ConnectionFactory();
		factory.setHost("localhost");
		Connection connection = factory.newConnection();
		Channel channel = connection.createChannel();
		//回调队列,服务器端给客户端返回响应的队列
		String responseQueue  = channel.queueDeclare().getQueue();
		//每个请求的唯一标志,请求ID,让客户端区分得到的响应属于哪次请求
		String correlationId = UUID.randomUUID().toString() ;
		//给服务端指定回调队列名称和请求ID
		BasicProperties props = new BasicProperties.Builder().replyTo(responseQueue)
		         .correlationId(correlationId).build();
		
		String msg="RPC";
		channel.basicPublish("", RPC_QUEUE_NAME, props, msg.getBytes());
		System.out.println(" [client] Sent '"+msg+"'");
		
		QueueingConsumer consumer = new QueueingConsumer(channel);
		//客户端在回调队列上监听消息
	    channel.basicConsume(responseQueue, consumer); 
 
		while (true) {
			QueueingConsumer.Delivery delivery = consumer.nextDelivery();
			if (delivery.getProperties().getCorrelationId().equals(correlationId)) {
				String result = new String(delivery.getBody());
				System.out.println(" [client] Got '"+result+"'");
			}
		}
	}
}

 

服务端:接受请求的时候,它是消费者;发送响应的时候,它是生产者。

package com.bj.rabbitmq.study.five;

import java.io.IOException;

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.QueueingConsumer.Delivery;
import com.rabbitmq.client.ShutdownSignalException;

public class RPCRecv {
	//用于客户端发送消息到服务器端的队列
	private static final String RPC_QUEUE_NAME = "rpc_queue"; 
	public static String sayHello(String name){
		return "hello "+name;
	}
	public static void main(String[] args) throws IOException, 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) ;
		
		QueueingConsumer consumer = new QueueingConsumer(channel);
		channel.basicConsume(RPC_QUEUE_NAME, false , consumer) ;
		
		while(true){
			System.out.println("服务器端等待接收消息...");
			Delivery delivery = consumer.nextDelivery();
			String result=new String(delivery.getBody());
			System.out.println("服务器端成功接收到消息:"+result);
			
			BasicProperties props = delivery.getProperties();
			BasicProperties responseProps  = new BasicProperties.Builder() //给服务端指定回调队列名称和请求ID
	         .correlationId(props.getCorrelationId()) //请求ID
	         .build();
			
			//将结果返回到客户端监听的Queue
			channel.basicPublish("", props.getReplyTo() , responseProps , sayHello(result).getBytes() ) ;
			System.out.println(" [SERVER] SENT '"+sayHello(result)+"'");
		}
	}
}

启动服务端,再启动客户端,输出记录如下

客户端-------------------------------------

 [client] Sent 'RPC'
 [client] Got 'hello RPC'

服务端-----------------------------------------

服务器端等待接收消息...
服务器端成功接收到消息:RPC
 [SERVER] SENT 'hello RPC'
服务器端等待接收消息...
 

参考:

https://www.cnblogs.com/sam-uncle/category/1218141.html

https://blog.csdn.net/xiaoxian8023/column/info/rabbitmq-arron

https://blog.csdn.net/liangwenmail/article/category/6774157

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RabbitMQ是一个开源的消息队列中间件,其主要功能是实现不同应用程序之间的异步通信。它使用AMQP(高级消息队列协议)来传输和路由消息,允许系统间的解耦和可伸缩性。 RabbitMQ 3.8.14版本是RabbitMQ的最新版本,它带来了一些改进和修复,增强了性能和稳定性。它提供了一种可靠的消息传递机制,允许应用程序或服务之间进行高效的通信。 RabbitMQ支持多种消息传递模式,包括点对点、发布/订阅、工作队列和RPC远程过程调用)。它使用队列来保存消息,并将消息通过交换机进行路由,以达到特定的目标。 RabbitMQ还提供了一些高级功能,例如消息确认、队列持久化和消息优先级,可确保消息传递的可靠性和灵活性。 与网盘有关,RabbitMQ 3.8.14版本并不是一个专门用于构建网盘的解决方案。然而,人们可以使用RabbitMQ来构建可靠的文件传输系统,以实现文件在不同服务之间的安全传递。 举例来说,如果我们有一个分布式的网盘系统,其中包括多个存储节点和用户端,RabbitMQ可以在存储节点之间实现可靠的文件传递。当用户上传文件时,它可以将文件发送到一个队列中,并由RabbitMQ负责将文件传递给相应的存储节点。这种方式可以确保文件传递的可靠性,并提供灵活的扩展性。 总而言之,RabbitMQ是一个强大的消息队列中间件,可以用于构建各种异步通信系统,包括文件传输系统。而RabbitMQ 3.8.14版本是其最新版本,带来了改进和修复,提供更好的性能和稳定性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值