一、RPC远程?
MQ需要实现,客户端通过其发送消息,服务端处理完成后,执行回调将结果返回给客户端,那么应该如何实现呢?
PRC远程调用:我们需要通过远程服务器帮我们计算某个结果然后返回给我们,我们与远程服务器形成客户端与服务端关系的这种模式成为远程调用RPC!
二、RPC远程调用的原理?
注意:基本的技术流程如上,本处只是基于基础简单的介绍,对于其防止出错或者异常处理流程这里不做设置~~
二、程序代码块?
(0) :什么是回调队列?
(1):客户端的设置?
package MQ.RPC;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.QueueingConsumer;
import com.rabbitmq.client.AMQP.BasicProperties;
import java.util.Scanner;
import java.util.UUID;
public class RPCClient {
private Connection connection;
private Channel channel;
private String requestQueueName = "rpc_queue";
private String replyQueueName;
private QueueingConsumer consumer;
public RPCClient() throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
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 Exception {
String response = null;
String corrId = UUID.randomUUID().toString();
//回调队列:
/*
* correlationId:RPC响应请求的相关编号
* replyTo : 回调队列的名称
* contentType():用来描述编码的MIME类型,如请求中经常使用的applicaiton/json
* deliveryMode:配置一个消息是否持久化。(2表示持久化),传递模型,持久/临时
*
* */
BasicProperties props = new BasicProperties.Builder().correlationId(corrId).replyTo(replyQueueName).build();
//推送消息
/*
* 按照当前顺序做注:
* "":交换器名称
* requestQueueName:路由键
* props:回调队列
* message:消息体
* */
channel.basicPublish("", requestQueueName, props, message.getBytes("UTF-8"));
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();//获取修饰过的携带客户标签的信息
System.out.println("传送的数据:"+delivery);
if (delivery.getProperties().getCorrelationId().equals(corrId)) {//匹配任务ID
response = new String(delivery.getBody(), "UTF-8");
break;
}
}
return response;
}
public void close() throws Exception {
connection.close();
}
public static void main(String[] argv) {
RPCClient fibonacciRpc = null;
String response = null;
try {
fibonacciRpc = new RPCClient();//服务端
System.out.println("我是客户端,我开始发送参数:");
while(true){
Scanner scanner=new Scanner(System.in);//获取数据
String words=scanner.next();
System.out.println("输入为:"+words);
response = fibonacciRpc.call(words);
System.out.println("RPCClient [.] Got '" + response + "'");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fibonacciRpc != null) {
try {
fibonacciRpc.close();
} catch (Exception ignore) {
}
}
}
}
}
注意:客户端在申报完正常的交换机和消息队列后,再设置一个回调队列,用于服务端的信息回调~,并在本地设置一个监控状态以监控服务端通过回调队列发送过来的消息!
===========================================================================================================
(2):服务端核心代码模块?
package MQ.RPC;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.QueueingConsumer;
import com.rabbitmq.client.AMQP.BasicProperties;
public class RPCServer {
private static final String RPC_QUEUE_NAME = "rpc_queue";
private static int fib(int n) {
if (n == 0)
return 0;
if (n == 1)
return 1;
if (n <=0 )
return -1;
return fib(n - 1) + fib(n - 2);
}
public static void main(String[] argv) {
Connection connection = null;
Channel channel = null;
try {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
connection = factory.newConnection();
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);//关闭自动ACK确认机制
System.out.println("RPCServer [x] Awaiting RPC requests");//关闭ACK自动确认
while (true) {
String response = null;
QueueingConsumer.Delivery delivery = consumer.nextDelivery();//用户队列
BasicProperties props = delivery.getProperties();//获取请求的属性
BasicProperties replyProps = new BasicProperties.Builder().correlationId(props.getCorrelationId()).build();
try {
String message = new String(delivery.getBody(), "UTF-8");//传递过来的内容
int n = Integer.parseInt(message);
System.out.println("RPCServer [.] fib(" + message + ")");
response = "" + fib(n);//调用
} catch (Exception e) {
System.out.println(" [.] " + e.toString());
response = "";
} finally {
channel.basicPublish("", props.getReplyTo(), replyProps, response.getBytes("UTF-8"));//返回
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);//返回队列,确认机制被关闭
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (connection != null) {
try {
connection.close();
} catch (Exception ignore) {
}
}
}
}
}
注意:服务端在连接向渠道后,获取用户对象,通过用户对象监控,消息队列的信息,处理完成后进行返回,并手动进行ACK消息确认!