我们知道现在市面上的RPC框架很多,但是如何用我们的RabbitMQ去实现一个RPC调用呢?这就是我们这篇文章所要讲解的内容。
如果有阅读过我写的博客的大兄弟们,可能会知道,我有个习惯就是学习技术喜欢去看官方文档,同样对于RabbitMQ如何去实现RPC调用,我们先来看看官方文档怎么说。
如上图,进入RabbitMQ官网,找到get Started,然后里面会有很多基本使用方式(其他几种使用方式在我的另一篇博文里面有详细介绍:https://blog.youkuaiyun.com/baomw/article/details/84847769),找到我们的RPC,然后点及java。
来看官网说的这句话,什么意思呢,就是在我们声明一个客户端,然后去通过call方法去调用我们的服务端,然后实时去接收我们服务端处理后返回的结果值,显示在控制台。
这里呢大致说了下使用rpc调用的一些注意的地方,以及rpc调用的方法的一些不足,容易造成系统混乱,等等。旨在提醒我猿们,在开发rpc调用实现的时候需要明确调用及依赖关系。
当然由于我们是需要接受服务端处理结果并返回的,所有客户端这边还需要声明一个接受回调信息的回调队列。
好了下面我们来实现一个具体的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[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
try (Connection connection = factory.newConnection();
Channel channel = connection.createChannel()) {
channel.queueDeclare(RPC_QUEUE_NAME, false, false, false, null);
channel.queuePurge(RPC_QUEUE_NAME);
channel.basicQos(1);
System.out.println(" [x] Awaiting RPC requests");
Object monitor = new Object();
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
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) {
System.out.println(" [.] " + e.toString());
} finally {
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
synchronized (monitor) {
monitor.notify();
}
}
};
channel.basicConsume(RPC_QUEUE_NAME, false, deliverCallback, (consumerTag -> { }));
// Wait and be prepared to consume the message from RPC client.
while (true) {
synchronized (monitor) {
try {
monitor.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
里面声明一个方法fib(int i),递归调用自己,有点算法基础的大兄弟们可能知道,这个是算我们的斐波那契数列求和。
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("localhost");
connection = factory.newConnection();
channel = connection.createChannel();
}
public static void main(String[] argv) {
try (RPCClient fibonacciRpc = new RPCClient()) {
for (int i = 0; i < 32; 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 (IOException | TimeoutException | InterruptedException e) {
e.printStackTrace();
}
}
public String call(String message) throws IOException, InterruptedException {
final String corrId = UUID.randomUUID().toString();
String replyQueueName = channel.queueDeclare().getQueue();
AMQP.BasicProperties props = new AMQP.BasicProperties
.Builder()
.correlationId(corrId)
.replyTo(replyQueueName)
.build();
channel.basicPublish("", requestQueueName, props, message.getBytes("UTF-8"));
final BlockingQueue<String> response = new ArrayBlockingQueue<>(1);
String ctag = channel.basicConsume(replyQueueName, true, (consumerTag, delivery) -> {
if (delivery.getProperties().getCorrelationId().equals(corrId)) {
response.offer(new String(delivery.getBody(), "UTF-8"));
}
}, consumerTag -> {
});
String result = response.take();
channel.basicCancel(ctag);
return result;
}
public void close() throws IOException {
connection.close();
}
}
客户端这边呢循环调用我们的call方法,在call方法中,实时去调用服务端并返回response,
然后String result = response.take();
获取到我们的返回结果。
到这里呢我们也就大致可以看出来,首先客户端发出一个数给服务端,服务端算出该数对应的斐波那契数并返回给我客户端打印到控制台,我们来看下具体实现效果。
可以看到,我们服务端处有31次打印,说明方法执行了31次调用,客户端对应有31个返回值打印出来。
由此,便利用我们的RabbitMQ实现了一个简单的rpc调用了!