前面讲的几个交换器,我们都是单向的消息发送,生产者将消息发送给消费者就不管了。在实际业务中,我们有时候要等耐消费者将结果返回给我们,或者说我么需要消费者上的一个功能,一个方法,一个接口返回给我们的值。但是往往我们的系统是不同的子系统,分布在不同的电脑,不能直接通过方法来调用,所以需要使用到RPC(Remote Procedure Call)远程过程调用模式。
RabbitMQ实现RPC的方式很简单,生产者发送一条带有标签(消息ID(correlation_id)+回调队列名称)的消息到发送队列,消费者(也称RPC服务端)从发送队列获取消息并处理业务,解析标签的信息将业务结果发送到指定的回调队列,生产者从回调队列中根据标签的信息获取发送消息的返回结果。
sample:
生产者
package com.enjoy.rabbitmqtest;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.TimeoutException;
public class RpcProducer {
public static final String EXCHANGE = "exchange_rpc";
public static final String URL = "localhost";
public static final String USER = "test";
public static final String PASS_WORD = "test";
public static final String VIRTUL = "/test";
public static final String ROUTING_KEY ="routekey.rpc";
public static final String QUEUE = "queue_rpc";
public static void main(String[] args) {
//连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setHost(URL);
factory.setUsername(USER);
factory.setPassword(PASS_WORD);
factory.setVirtualHost(VIRTUL);
//获取连接
try {
Connection conn = factory.newConnection();
//获取信道
Channel channel = conn.createChannel();
//创建交换器
channel.exchangeDeclare(EXCHANGE,BuiltinExchangeType.DIRECT.getType());
//声明队列
channel.queueDeclare(QUEUE, false, false, false, null);
//将信道和交换器、路由键绑定
channel.queueBind(QUEUE, EXCHANGE, ROUTING_KEY);
//此处注意:我们声明了要回复的队列。队列名称由RabbitMQ自动创建。
//这样做的好处是:每个客户端有属于自己的唯一回复队列,生命周期同客户端
String replyQueue = channel.queueDeclare().getQueue();
//发送消息到交换器
for(int i=0;i<3;i++) {
//消息相关属性
String correlationId = UUID.randomUUID().toString();
String message = "hello rpc-"+i;
//属性
AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
.replyTo(replyQueue)//回复队列
.correlationId(correlationId)//回复correlationId
.build();
System.out.println("Producer发送消息[correlationId="+correlationId
+ ",mesage="+message+"]");
channel.basicPublish(EXCHANGE, ROUTING_KEY, false, props, message.getBytes());
Thread.sleep(1000);
}
//声明消费者
DefaultConsumer consumer = new DefaultConsumer(channel){
public void handleDelivery(String consumerTag,
Envelope envelope,
AMQP.BasicProperties properties,
byte[] body){
System.out.println(System.currentTimeMillis()+",Producer收到回执消息[correlationId="+properties.getCorrelationId()
+ "mesage="+new String(body)+"]");
}
};
//消费队列消息
channel.basicConsume(replyQueue,true,consumer);
// channel.close();
//// conn.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
消费者
package com.enjoy.rabbitmqtest;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class RpcConsumer {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost(RpcProducer.URL);
factory.setUsername(RpcProducer.USER);
factory.setPassword(RpcProducer.PASS_WORD);
factory.setVirtualHost(RpcProducer.VIRTUL);
Connection conn = factory.newConnection();
//获取信道
final Channel channel = conn.createChannel();
/*绑定,将队列和交换器通过路由键进行绑定*/
channel.queueBind(RpcProducer.QUEUE, RpcProducer.EXCHANGE,RpcProducer.ROUTING_KEY);
System.out.println("waiting for message........");
//声明消费者
DefaultConsumer consumer = new DefaultConsumer(channel){
//这是一个回到函数,服务器端获取到消息,就会调用此方法处理消息
public void handleDelivery(String consumerTag,
Envelope envelope,
AMQP.BasicProperties properties,
byte[] body) throws IOException {
// System.out.println("consumerTag="+consumerTag+",envelope="+envelope
// +",properties="+properties);
String message = new String(body);
System.out.println("Consumer收到消息[correlationId="+properties.getCorrelationId()
+",message="+message+"]");
AMQP.BasicProperties.Builder builder = new AMQP.BasicProperties.Builder();
//我们在将要回复的消息属性中,放入从客户端传递过来的correlateId
builder.correlationId(properties.getCorrelationId());
AMQP.BasicProperties props = builder.build();
//发送给回复队列的消息,exchange="",routingKey=回复队列名称
//因为RabbitMQ对于队列,始终存在一个默认exchange="",routingKey=队列名称的绑定关系
channel.basicPublish("",properties.getReplyTo(),
false,props,("OK-"+properties.getCorrelationId()).getBytes());
}
};
/*消费者正式开始在指定队列上消费消息*/
channel.basicConsume(RpcProducer.QUEUE,true,consumer);
// channel.close();
// conn.close();
}
}
结果:
生产者:
Producer发送消息[correlationId=cecf5421-0dd5-4bb1-bc4a-de84b2fdd898,mesage=hello rpc-0]
Producer发送消息[correlationId=3819cd8e-16e3-4899-a772-2846a14cef09,mesage=hello rpc-1]
Producer发送消息[correlationId=1502de20-4377-4ba0-b4e8-592c71d82197,mesage=hello rpc-2]
1571543302869,Producer收到回执消息[correlationId=cecf5421-0dd5-4bb1-bc4a-de84b2fdd898mesage=OK-cecf5421-0dd5-4bb1-bc4a-de84b2fdd898]
1571543302869,Producer收到回执消息[correlationId=3819cd8e-16e3-4899-a772-2846a14cef09mesage=OK-3819cd8e-16e3-4899-a772-2846a14cef09]
1571543302869,Producer收到回执消息[correlationId=1502de20-4377-4ba0-b4e8-592c71d82197mesage=OK-1502de20-4377-4ba0-b4e8-592c71d82197]
消费者:
waiting for message........
Consumer收到消息[correlationId=cecf5421-0dd5-4bb1-bc4a-de84b2fdd898,message=hello rpc-0]
Consumer收到消息[correlationId=3819cd8e-16e3-4899-a772-2846a14cef09,message=hello rpc-1]
Consumer收到消息[correlationId=1502de20-4377-4ba0-b4e8-592c71d82197,message=hello rpc-2]