1.声明
当前内容主要用于本人学习和复习之用,当前内容为使用RabbitMQ的生产者、消费者、队列实现RPC调用
当前内容来源:RabbitMQ官方文档
分析RPC调用
- 客户端发送参数请求服务端
- 服务端处理发送的请求并得到结果
- 服务端将结果返回到客户端
通过上面的RPC分析可以发现
- 数据信息可以看作消息
- 客户端即是消费者又是生产者(获得了消息,发送了消息)
- 服务端即是消费者又是生产者(处理了消息,返回了消息)
2.创建客户端和服务端
客户端(为它服务发送和接收消息的能力)
public class RPCMClientain {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory=new ConnectionFactory();
Connection conn = factory.newConnection();
Channel channel = conn.createChannel();
UUID randomUUID = UUID.randomUUID();
BasicProperties props = new BasicProperties
.Builder()
.correlationId(randomUUID.toString())
.replyTo("rpc_queue")
.build();
channel.basicPublish("", "rpc_queue", props, ("10").getBytes());
System.out.println("发送消息成功===>");
/*
* try { Thread.sleep(5000l); } catch (InterruptedException e) { // TODO
* Auto-generated catch block e.printStackTrace(); }
*/
System.out.println("开始获取结果===>");
channel.basicConsume("rpc_queue",true,new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
System.out.println("获取结果===>"+StringUtils.toString(body));
System.out.println(consumerTag);
System.out.println(properties);
System.out.println(new String(body,"utf-8"));
//conn.close();
}
});
}
}
服务端(使用官方的例子)
public class RPCServer {
private static final String RPC_QUEUE_NAME = "rpc_queue";
private static int doubleNum(int n) {
return 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 += doubleNum(n);
System.out.println("执行完毕后的结果为===>" + response);
} catch (RuntimeException e) {
e.printStackTrace();
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
System.out.println("唤醒工作线程====>");
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 {
System.out.println("rpcserver开始阻塞工作线程====>");
monitor.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
3.测试RPC
先启动客户端然后启动服务端并查看结果
客户端执行的结果
服务端执行的结果
执行操作成功
4.分析该RPC操作
1.由于两个都是用了""这个默认的exchange,该exchange类型为Direct类型,如果出现两个相同的客户端默认使用轮询方式,这也是为什么发送消息的时候会发送给对方的原因
2.客户端发送了消息到server,那么server对结果进行处理(一条一条的处理)basicQos(1),最后将结果通过basicPublish发送给replyTo的queue中(即“rpc-queue”里面),然后进行应答,表示该消息处理完毕
3.由于消息发送给了“rpc-queue”这个queue中,那么通过Direct的特性,这个消息会发送给另外一个消费者,从而完成消息回应
注意:
由于上面的这个方法使用了相同的队列,所以会导致一个问题,如果客户端发起多个请求,那么这个回调的结果就是错误的
解决办法:直接将replayTo的结果放入另外一个队列中,然后客户端直接消费replayTo队列的消息即可完成
5.总结
1.使用消费者和生产者模拟RPC调用的时候其实实现起来还是很容易的
2.只需要在发送的时候指定replayTo为一个队列,然后服务器将结果发送的replayTo中就可以完成RPC调用
以上纯属个人见解,如有问题请联本人!