远程过程调用
1.创建Maven项目
项目目录
2.导入rabbitmq依赖
<!--rabbitmq依赖-->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.7.3</version>
</dependency>
3.RPC模式队列-服务器Server
/**
* RPC模式队列-服务器
*/
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;
return fib(n - 1) + fib(n - 2);
}
public static void main(String[] args) {
// 创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.68.152");
factory.setPort(5672);
factory.setUsername("lwx");
factory.setPassword("lwx");
factory.setVirtualHost("/lwx");
try {
// 通过工厂创建连接
final Connection connection = factory.newConnection();
// 获取信道
final Channel channel = connection.createChannel();
// 声明队列
channel.queueDeclare(RPC_QUEUE_NAME, false, false, false, null);
channel.queuePurge(RPC_QUEUE_NAME);
/**
* 限制RabbitMQ只发不超过1条的消息给同一消费者。
* 当消息处理完毕后,有了反馈,才会进行第二次发送。
*/
int prefetchCount = 1;
channel.basicQos(prefetchCount);
System.out.println("[x] Awaiting RPC requests");
Object monitor = new Object();
// 获取消息
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
// 获取replyTo队列和correlationId请求标识
AMQP.BasicProperties replyProps = new AMQP.BasicProperties
.Builder()
.correlationId(delivery.getProperties().getCorrelationId())
.build();
String response = "";
try {
// 接收客户端消息
String message = new String(delivery.getBody(), "UTF-8");
int n = Integer.parseInt(message);
System.out.println("[.] fib(" + message + ")");
// 服务端根据业务需求处理
response += fib(n);
} catch (RuntimeException e) {
e.printStackTrace();
} finally {
// 将处理结果发送至replyTo队列同时携带correlationId属性
channel.basicPublish("", delivery.getProperties().getReplyTo(),
replyProps, response.getBytes("UTF-8"));
// 手动回执消息
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
// RabbitMq consumer worker thread notifies the RPC server owner thread
// RabbitMq消费者工作线程通知RPC服务器其他所有者线程运行
synchronized (monitor) {
monitor.notify();
}
}
};
// 监听队列
/**
* autoAck = true 代表自动确认消息
* autoAck = false 代表手动确认消息
*/
boolean autoAck = false;
channel.basicConsume(RPC_QUEUE_NAME, autoAck, deliverCallback, consumerTag -> {
});
// Wait and be prepared to consume the message from RPC client.
// 线程等待并准备接收来自RPC客户端的消息。
while (true) {
synchronized (monitor) {
try {
monitor.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
4.RPC模式队列-客户端Client
/**
* RPC模式队列-客户端
*/
public class RPCClient implements AutoCloseable {
private Connection connection;
private Channel channel;
// 队列名称
private String requestQueueName = "rpc_queue";
// 初始化连接
public RPCClient() throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.68.152");
factory.setPort(5672);
factory.setUsername("lwx");
factory.setPassword("lwx");
factory.setVirtualHost("/lwx");
connection = factory.newConnection();
channel = connection.createChannel();
}
public static void main(String[] args) {
try (RPCClient fibonacciRpc = new RPCClient()) {
for (int i = 0; i < 10; i++) {
String i_str = Integer.toString(i);
System.out.println("[x] Requesting fib(" + i_str + ")");
// 请求服务器
String response = fibonacciRpc.call(i_str);
System.out.println("[.] Got '" + response + "'");
}
} catch (TimeoutException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
//请求服务端
private String call(String message) throws IOException, InterruptedException {
// correlationId请求标识ID
final String corrId = UUID.randomUUID().toString();
// 获取队列名称
String replyQueueBame = channel.queueDeclare().getQueue();
// 设置replyTo队列和correlationId请求标识
AMQP.BasicProperties props = new AMQP.BasicProperties
.Builder()
.correlationId(corrId)
.replyTo(replyQueueBame)
.build();
// 发送消息至队列
channel.basicPublish("", requestQueueName, props, message.getBytes("UTF-8"));
// 设置线程等待,每次只接收一个响应结果
final BlockingQueue<String> response = new ArrayBlockingQueue<>(1);
// 接受服务器返回结果
String ctag = channel.basicConsume(replyQueueBame, true, (consumerTag, delivery) -> {
if (delivery.getProperties().getCorrelationId().equals(corrId)) {
// 将给定的元素在给定的时间内设置到线程队列中,如果设置成功返回true,否则返回false
response.offer(new String(delivery.getBody(), "UTF-8"));
}
}, consumerTag -> {
});
// 从线程队列中获取值,如果线程队列中没有值,线程会一直阻塞,直到线程队列中有值,并且取得该值
String result = response.take();
// 从消息队列中丢弃该值
channel.basicCancel(ctag);
return result;
}
// 关闭连接
@Override
public void close() throws Exception {
connection.close();
}
}
5.运行
- 先运行服务器Server
- 再运行客户端Client
运行结果: