本身使用RpcClient发送消息与同步接收消息的代码是很简单的,如下:
RpcClient client = new RpcClient(channel, exchange, routingKey);
String msg = "hello world!";
byte[] result = client.primitiveCall(msg.getBytes());
这里的primitiveCall调用后,当前线程会进行同步等待,等待消息接收端给自己的回复消息
一个完整的发送消息与接收回复消息的图例:
整个流程详解:
RpcClient client = new RpcClient(channel, exchange, routingKey);
创建RpcClient时会做两件事:
A:创建一个回复queue,接收当前RpcClient发送的消息的消息接收人会将回复消息发到这个replyQueue上供当前RpcClient去接收回复消息
_replyQueue = setupReplyQueue();
protected String setupReplyQueue() throws IOException {
return _channel.queueDeclare("", false, false, true, true, null).getQueue();
//这里实际上是由rabbitmq server去定义一个唯一的queue(因为queueName是空的,所以是由server去生成queueName),最后返回这个queueName,queueName是由server生成的,使用的是以下这个方法:
Queue.DeclareOk queueDeclare(String queueName, boolean passive, boolean durable, boolean exclusive, boolean autoDelete,
Map<String, Object> arguments)
}
B:创建一个接收回复消息的consumer
_consumer = setupConsumer();
protected DefaultConsumer setupConsumer() throws IOException {
//创建一个接收消息的DefaultConsumer实例
DefaultConsumer consumer = new DefaultConsumer(_channel) {
@Override //发生shutdown的时候回调
public void handleShutdownSignal(String consumerTag,
ShutdownSignalException signal) {
synchronized (_continuationMap) {
for (Entry<String, BlockingCell<Object>> entry : _continuationMap.entrySet()) {
entry.getValue().set(signal);
}
_consumer = null;
}
}
@Override //处理消息交付
public void handleDelivery(String consumerTag,
Envelope envelope,
AMQP.BasicProperties properties,
byte[] body)
throws IOException {
//这部分就是和下面的代码一起协作来实现将异步接收强制变成同步接收
synchronized (_continuationMap) {
String replyId = properties.getCorrelationId();
BlockingCell<Object> blocker = _continuationMap.get(replyId);
_continuationMap.remove(replyId);
blocker.set(body);
}
}
};
//让接收消息的consumer去replyQueue上去接收消息,这个过程对于主线程来说是异步进行的,只要replyQueue上有消息了,consumer就会去replyQueue上去接收消息,并回调它的handleDelivery方法
_channel.basicConsume(_replyQueue, true, consumer);
return consumer;
}
byte[] result = rpcClient.primitiveCall(msg.getBytes());
使用rpcClient的primitiveCall发送消息,看看是怎么做的
public byte[] primitiveCall(byte[] message) throws IOException, ShutdownSignalException {
return primitiveCall(null, message);
}
继续跟踪,核心方法是这个
public byte[] primitiveCall(AMQP.BasicProperties props, byte[] message) throws IOException, ShutdownSignalException{
//检查consumer是否为空,若为空,抛出异常
checkConsumer();
BlockingCell<Object> k = new BlockingCell<Object>();
synchronized (_continuationMap) {
_correlationId++;
String replyId = "" + _correlationId;
//如果props不为空,则将上一步骤创建的replyQueue设置到props上去,还有replyId
if (props != null) {
props.setCorrelationId(replyId);
props.setReplyTo(_replyQueue);
}
else {
//如果props为空,则创建一个,并将replyId和replyQueue都设置到props上
props = new AMQP.BasicProperties(null, null, null, null,
null, replyId,
_replyQueue, null, null, null,
null, null, null, null);
}
_continuationMap.put(replyId, k);
}
//使用上面的props发送消息,这样replyQueue和replyId就跟着传递到了接收消息的那一方去了,接收消息的client去props上去取到replyQueue,它就知道了它接收的消息的回复queue,然后它会将回复消息发送到replyQueue上去,而在上一步骤我们已经指定了一个consumer去replyQueue上去取消息,所以整个发送和接收消息的所有client是有条不紊的进行着
publish(props, message); //这行代码执行完后,只是将消息发送出去了,接收回复消息是异步的,由上一步骤的consumer去接收回复消息
//这里就是进行同步等待接收回复消息,将异步接收变成同步回复接收的核心就在这里
Object reply = k.uninterruptibleGet();
if (reply instanceof ShutdownSignalException) {
ShutdownSignalException sig = (ShutdownSignalException) reply;
ShutdownSignalException wrapper =
new ShutdownSignalException(sig.isHardError(),
sig.isInitiatedByApplication(),
sig.getReason(),
sig.getReference());
wrapper.initCause(sig);
throw wrapper;
} else {
return (byte[]) reply;
}
}