Spring整合rabbitmq实践(一):基础使用配置
Spring整合rabbitmq实践(二):扩展功能
spring-rabbit消费过程解析及AcknowledgeMode选择
4. 源码解析
4.1. 通过RabbitTemplate获取消息
从RabbitTemplate中只有queueName入参的方法开始:
@Override
public Message receive(String queueName) {
if (this.receiveTimeout == 0) {
return doReceiveNoWait(queueName);
}
else {
return receive(queueName, this.receiveTimeout);
}
}
receiveTimeOut参数为0,直接获取消息,不等待,获取不到返回null;否则会等待一段时间。
进入带有receiveTimeout的receive方法:
@Override
public Message receive(final String queueName, final long timeoutMillis) {
Message message = execute(channel -> {
Delivery delivery = consumeDelivery(channel, queueName, timeoutMillis);
if (delivery == null) {
return null;
}
else {
if (isChannelLocallyTransacted(channel)) {
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
channel.txCommit();
}
else if (isChannelTransacted()) {
ConnectionFactoryUtils.registerDeliveryTag(getConnectionFactory(), channel,
delivery.getEnvelope().getDeliveryTag());
}
else {
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
return buildMessageFromDelivery(delivery);
}
});
logReceived(message);
return message;
}
看到Message是通过调用execute方法得到的,进到execute方法:
@Override
public <T> T execute(ChannelCallback<T> action) {
return execute(action, getConnectionFactory());
}
@SuppressWarnings("unchecked")
private <T> T execute(final ChannelCallback<T> action, final ConnectionFactory connectionFactory) {
if (this.retryTemplate != null) {
try {
return this.retryTemplate.execute(
(RetryCallback<T, Exception>) context -> doExecute(action, connectionFactory),
(RecoveryCallback<T>) this.recoveryCallback);
}
catch (Exception e) {
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
}
throw RabbitExceptionTranslator.convertRabbitAccessException(e);
}
}
else {
return doExecute(action, connectionFactory);
}
}
这里能看到配置RetryTemplate的作用,具体就不管了,找到doExecute方法,Message是从这里得到的:
private <T> T doExecute(ChannelCallback<T> action, ConnectionFactory connectionFactory) {
...
if (channel == null) {
if (isChannelTransacted()) {
resourceHolder = ConnectionFactoryUtils.
getTransactionalResourceHolder(connectionFactory, true, this.usePublisherConnection);
channel = resourceHolder.getChannel();
if (channel == null) {
ConnectionFactoryUtils.releaseResources(resourceHolder);
throw new IllegalStateException("Resource holder returned a null channel");
}
}
else {
connection = ConnectionFactoryUtils.createConnection(connectionFactory,
this.usePublisherConnection); // NOSONAR - RabbitUtils closes
if (connection == null) {
throw new IllegalStateException("Connection factory returned a null connection");
}
try {
channel = connection.createChannel(false);
if (channel == null) {
throw new IllegalStateException("Connection returned a null channel");
}
}
catch (RuntimeException e) {
RabbitUtils.closeConnection(connection);
throw e;
}
}
}
...
try {
...
return action.doInRabbit(channel);
}
catch (Exception ex) {
...
}
finally {
if (!invokeScope) {
if (resourceHolder != null) {
ConnectionFactoryUtils.releaseResources(resourceHolder);
}
else {
RabbitUtils.closeChannel(channel);
RabbitUtils.closeConnection(connection);
}
}
}
}
这个方法比较长,大体可以了解到,在这个方法里创建了Connection和Channel,执行action.doInRabbit()方法得到Message,关闭Channel和Connection。
当然,这里Connection和Channel的创建和关闭都不一定是真的创建和关闭,与具体的实现有关,比如CachingConnectionFactory,它的实现就是有缓存的,后面详述。
action.doInRabbit()方法的实现逻辑就要再回到上面的receive方法,这里的action就是在那个receive方法传入的一个ChannelCallback的匿名内部实现类。
可以看到最后返回的消息是从Delivery中得到的,那么看下Delivery是怎么来的:
private Delivery consumeDelivery(Channel channel, String queueName, long timeoutMillis) throws Exception {
Delivery delivery = null;
RuntimeException exception = null;
CompletableFuture<Delivery> future = new CompletableFuture<>();
DefaultConsumer consumer = createConsumer(queueName, channel, future,
timeoutMillis < 0 ? DEFAULT_CONSUME_TIMEOUT : timeoutMillis);
try {
if (timeoutMillis < 0) {
delivery = future.get();
}
else {
delivery = future.get(timeoutMillis, TimeUnit.MILLISECONDS);
}
}
catch (ExecutionException e) {
Throwable cause = e.getCause();
this.logger.error("Consumer fai