首先声明:
RPC调用会增加系统复杂度以及调试难度,
容易使代码混乱,系统缓慢。
尽量避免使用RPC,转而使用异步管道替代。
rabbitMQ实现的RPC主要流程:
- 客户端启动后,创建一个匿名唯一的回调队列
- 对于一个RPC请求,客户端发送一个消息和2个属性,一个是replyto用来设置回调队列,另一个是correlationId,每个请求的值都是唯一的
- RPC worker(也是被调用的服务方)从请求队列中获取值,每出现一个请求,就处理它,并把结果发送到回调队列返回给 客户端。使用replyto字段
- 客户端在回调队列中等待结果,检查correlationId,符合条件的返还给应用。
实现代码(服务方):
public static void main(String[] args) {
try(Connection connection=getConnection("guest","guest","localhost",5672,"/");
Channel channel=connection.createChannel()) {
channel.queueDeclare(queueName, false, false, false, null);
channel.basicQos(1);
System.out.println(" [x] Awaiting RPC requests");
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException, IOException {
AMQP.BasicProperties replyProps = new AMQP.BasicProperties
.Builder()
.correlationId(properties.getCorrelationId())
.build();
String response = "";
try {
String message = new String(body,"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( "", properties.getReplyTo(), replyProps, response.getBytes("UTF-8"));
channel.basicAck(envelope.getDeliveryTag(), false);
// RabbitMq consumer worker thread notifies the RPC server owner thread
synchronized(this) {
System.out.println(this.getClass().getName());
this.notify();
}
}
}
private int fib(int n) {
if (n == 0) return 0;
if (n == 1) return 1;
return fib(n-1) + fib(n-2);
}
};
channel.basicConsume(queueName, false, consumer);
while (true){
synchronized (consumer){
consumer.wait();
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
客户端:
public static void main(String[] args) {
try (Connection connection = getConnection("guest", "guest", "localhost", 5672, "/");
Channel channel = connection.createChannel()) {
String r=call("5",channel,"rpc_queue");
System.out.println(r);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static String call(String message,Channel channel,String requestQueueName) throws IOException, InterruptedException {
String replyQueueName=channel.queueDeclare().getQueue();
final String corrId= UUID.randomUUID().toString();
AMQP.BasicProperties properties=new AMQP.BasicProperties.Builder().correlationId(corrId)
.replyTo(replyQueueName)
.build();
channel.basicPublish("",requestQueueName,properties,message.getBytes("utf-8"));
final BlockingQueue<String> response=new ArrayBlockingQueue<String>(1);
channel.basicConsume(replyQueueName, true, new DefaultConsumer(channel){
@Override
public void handleDelivery(String s, Envelope envelope, AMQP.BasicProperties basicProperties, byte[] bytes) throws IOException {
if (properties.getCorrelationId().equals(corrId)){
response.offer(new String(bytes,"utf-8"));
}
}
});
return response.take();
}