springboot-rabbitmq-starter容器启动关闭主要源码分析

博客详细解析了Spring Boot应用中RabbitMQ消费者在启动和关闭时的主要流程。在启动时,通过SimpleRabbitListenerContainerFactory创建并管理consumer线程,监听消息队列。在关闭过程中,通过反射操作清空消费者线程的阻塞队列,以实现更优雅的关闭并避免消息丢失。博主还提供了一段改造代码来改善关闭流程。
摘要由CSDN通过智能技术生成

  

一:容器主要启动消费流程

默认创建SimpleRabbitListenerContainerFactory

	@Bean(name = "rabbitListenerContainerFactory")
	@ConditionalOnMissingBean(name = "rabbitListenerContainerFactory")
	@ConditionalOnProperty(prefix = "spring.rabbitmq.listener", name = "type", havingValue = "simple", matchIfMissing = true)
    //默认创建SimpleRabbitListenerContainerFactory
	public SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory(
			SimpleRabbitListenerContainerFactoryConfigurer configurer,
			ConnectionFactory connectionFactory) {
		SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
		configurer.configure(factory, connectionFactory);
		return factory;
	}

SimpleRabbitListenerContainerFactory工厂类创建SimpleMessageListenerContainer对象。

SimpleMessageListenerContainer对象的作用是管理consumer线程运行。

以下是启动容器主要代码:

@Override
	protected void doStart() throws Exception {
        //一些校验逻辑
		...
        //设置标识位
		super.doStart();
		synchronized (this.consumersMonitor) {
			if (this.consumers != null) {
				throw new IllegalStateException("A stopped container should not have consumers");
			}
			int newConsumers = initializeConsumers();
			if (this.consumers == null) {
				logger.info("Consumers were initialized and then cleared " +
						"(presumably the container was stopped concurrently)");
				return;
			}
			if (newConsumers <= 0) {
				if (logger.isInfoEnabled()) {
					logger.info("Consumers are already running");
				}
				return;
			}
			Set<AsyncMessageProcessingConsumer> processors = new HashSet<AsyncMessageProcessingConsumer>();
            //消费者对象,已代理业务逻辑
			for (BlockingQueueConsumer consumer : this.consumers) {
                //封装到执行器中,实现了Runnable接口,见下文
				AsyncMessageProcessingConsumer processor = new AsyncMessageProcessingConsumer(consumer);
				processors.add(processor);
                //线程池启动
				getTaskExecutor().execute(processor);
				if (getApplicationEventPublisher() != null) {
					getApplicationEventPublisher().publishEvent(new AsyncConsumerStartedEvent(this, consumer));
				}
			}

            //阻塞延时判断消费者线程是否正常启动
			for (AsyncMessageProcessingConsumer processor : processors) {
				FatalListenerStartupException startupException = processor.getStartupException();
				if (startupException != null) {
					throw new AmqpIllegalStateException("Fatal exception on listener startup", startupException);
				}
			}
		}
	}

AsyncMessageProcessingConsumer的run方法:

关键点在于this.consumer.hasDelivery()方法为判断该消费者中的阻塞队列是否为空,结论中说明。

@Override
public void run() {
		//一些校验
		。。。
		//注册事务管理
		if (getTransactionManager() != null) {
			/*
			 * Register the consumer's channel so it will be used by the transaction manager
			 * if it's an instance of RabbitTransactionManager.
			 */
			ConsumerChannelRegistry.registerConsumerChannel(this.consumer.getChannel(), getConnectionFactory());
		}
		//while循环处理消息,this.consumer.hasDelivery()方法为判断该消费者中的阻塞队列是否为空,队列长度默认250
		while (isActive(this.consumer) || this.consumer.hasDelivery() || !this.consumer.cancelled()) {
			try {
				//此方法获取一个阻塞队列中的消息,处理,执行业务逻辑,向rabbitmq发送确认消息
				boolean receivedOk = receiveAndExecute(this.consumer); // At least one message received
				if (SimpleMessageListenerContainer.this.maxConcurrentConsumers != null) {
					if (receivedOk) {
						if (isActive(this.consumer)) {
							consecutiveIdles = 0;
							if (consecutiveMessages++ > SimpleMessageListenerContainer.this.consecutiveActiveTrigger) {
								considerAddingAConsumer();
								consecutiveMessages = 0;
							}
						}
					}
					else {
						consecutiveMessages = 0;
						if (consecutiveIdles++ > SimpleMessageListenerContainer.this.consecutiveIdleTrigger) {
							considerStoppingAConsumer(this.consumer);
							consecutiveIdles = 0;
						}
					}
				}
				//根据条件发布空闲事件,这里idleEventInterval默认为0
				long idleEventInterval = getIdleEventInterval();
				if (idleEventInterval > 0) {
					if (receivedOk) {
						updateLastReceive();
					}
					else {
						long now = System.currentTimeMillis();
						long lastAlertAt = SimpleMessageListenerContainer.this.lastNoMessageAlert.get();
						long lastReceive = getLastReceive();
						if (now > lastReceive + idleEventInterval
								&& now > lastAlertAt + idleEventInterval
								&& SimpleMessageListenerContainer.this.lastNoMessageAlert
								.compareAndSet(lastAlertAt, now)) {
							publishIdleContainerEvent(now - lastReceive);
						}
					}
				}
			}
			//异常处理
			。。。
		}

	} // while循环结束
	//一堆异常处理
	。。。

	// In all cases count down to allow container to progress beyond startup
	//countdownlatch减1
	this.start.countDown();
	//容器触发stop时或者上面逻辑产生异常
	if (!isActive(this.consumer) || aborted) {
		logger.debug("Cancelling " + this.consumer);
		try {
			//信道关闭,标志位处理,清除队列
			this.consumer.stop();
			//释放容器中的一个锁,用于容器stop时监听所有consumer线程stop情况
			SimpleMessageListenerContainer.this.cancellationLock.release(this.consumer);
			//发布spring事件
			if (getApplicationEventPublisher() != null) {
				getApplicationEventPublisher().publishEvent(
						new AsyncConsumerStoppedEvent(SimpleMessageListenerContainer.this, this.consumer));
			}
		}
		catch (AmqpException e) {
			logger.info("Could not cancel message consumer", e);
		}
		//异常中止处理
		if (aborted && SimpleMessageListenerContainer.this.containerStoppingForAbort
				.compareAndSet(null, Thread.currentThread())) {
			logger.error("Stopping container from aborted consumer");
			stop();
			SimpleMessageListenerContainer.this.containerStoppingForAbort.set(null);
			ListenerContainerConsumerFailedEvent event = null;
			do {
				try {
					event = SimpleMessageListenerContainer.this.abortEvents.poll(5, TimeUnit.SECONDS);
					if (event != null) {
						SimpleMessageListenerContainer.this.publishConsumerFailedEvent(
								event.getReason(), event.isFatal(), event.getThrowable());
					}
				}
				catch (InterruptedException e) {
					Thread.currentThread().interrupt();
				}
			}
			while (event != null);
		}
	}
	//重启该消费者线程
	else {
		logger.info("Restarting " + this.consumer);
		restart(this.consumer);
	}
}

		

二:容器主要关闭流程

容器实现了spring的Lifecycle接口会在springboot关机过程中调用。

最终调用逻辑(在此之前已经active=false):

@Override
protected void doShutdown() {
	//并发安全处理
	Thread thread = this.containerStoppingForAbort.get();
	if (thread != null && !thread.equals(Thread.currentThread())) {
		logger.info("Shutdown ignored - container is stopping due to an aborted consumer");
		return;
	}

	try {
		List<BlockingQueueConsumer> canceledConsumers = new ArrayList<>();
		synchronized (this.consumersMonitor) {
			if (this.consumers != null) {
				//循环处理消费者线程
				Iterator<BlockingQueueConsumer> consumerIterator = this.consumers.iterator();
				while (consumerIterator.hasNext()) {
					BlockingQueueConsumer consumer = consumerIterator.next();
					//通知rabbitmq停止发送消息道队列中
					consumer.basicCancel(true);
					canceledConsumers.add(consumer);
					consumerIterator.remove();
					//如果消费者线程正在创建过程中,则立即中断
					if (consumer.declaring) {
						consumer.thread.interrupt();
					}
				}
			}
			else {
				logger.info("Shutdown ignored - container is already stopped");
				return;
			}
		}
		logger.info("Waiting for workers to finish.");
		//阻塞等待一定时间,等待所有消费者线程处理完成,如已处理完,会提前退出。如未处理完,则timeout后返回false。timeout默认5s
		boolean finished = this.cancellationLock.await(getShutdownTimeout(), TimeUnit.MILLISECONDS);
		if (finished) {
			logger.info("Successfully waited for workers to finish.");
		}
		else {
			logger.info("Workers not finished.");
			//强制stop消费者线程,isForceCloseChannel默认true
			if (isForceCloseChannel()) {
				canceledConsumers.forEach(consumer -> {
					if (logger.isWarnEnabled()) {
						logger.warn("Closing channel for unresponsive consumer: " + consumer);
					}
					consumer.stop();
				});
			}
		}
	}
	catch (InterruptedException e) {
		Thread.currentThread().interrupt();
		logger.warn("Interrupted waiting for workers.  Continuing with shutdown.");
	}
	
	synchronized (this.consumersMonitor) {
		this.consumers = null;
		this.cancellationLock.deactivate();
	}

}

三:结论

关键点在于this.consumer.hasDelivery()方法为判断该消费者中的阻塞队列是否为空。

导致在停机过程中需要完全处理完队列中的消息才能继续走后续逻辑。

故在默认设置情况下,如果在5s没能处理完成队列中的消息,则强制关闭消费者线程,可能导致重复消费。

故,考虑在容器关闭过程中,处理consumer.basicCancel(true)后,清空消费者线程中的阻塞队列。

四:改造代码

说明:容器大部分为私有属性,所以采用反射操作对象。

private void rabbitMqGraceShutDown(ContextClosedEvent contextClosedEvent) {
     RabbitListenerEndpointRegistry rabbitListenerEndpointRegistry = contextClosedEvent.getApplicationContext().getBean(RabbitListenerEndpointRegistry.class);
     rabbitListenerEndpointRegistry.getListenerContainers().forEach(p -> {
                 try {
                     Class<?> c = p.getClass();
                     Field consumerField = c.getDeclaredField("consumers");
                     consumerField.setAccessible(true);
                     Set<BlockingQueueConsumer> consumers = (Set<BlockingQueueConsumer>) consumerField.get(p);
                     Field cancellationLockField = c.getDeclaredField("cancellationLock");
                     cancellationLockField.setAccessible(true);

                     ActiveObjectCounter<BlockingQueueConsumer> cancellationLock = (ActiveObjectCounter<BlockingQueueConsumer>) cancellationLockField.get(p);
                     List<BlockingQueueConsumer> canceledConsumers = new ArrayList<>();
                     synchronized (this.rabbitmqMonitor) {
                         if (consumers != null) {
                             Iterator<BlockingQueueConsumer> consumerIterator = consumers.iterator();
                             while (consumerIterator.hasNext()) {
                                 BlockingQueueConsumer consumer = consumerIterator.next();

                                 Class<?> a = consumer.getClass();
                                 Method basicCancelMethod = a.getDeclaredMethod("basicCancel", boolean.class);
                                 basicCancelMethod.setAccessible(true);

                                 Field queueField = a.getDeclaredField("queue");
                                 queueField.setAccessible(true);
                                 Field declaringField = a.getDeclaredField("declaring");
                                 declaringField.setAccessible(true);
                                 Field threadField = a.getDeclaredField("thread");
                                 threadField.setAccessible(true);

                                 basicCancelMethod.invoke(consumer, true);
                                 BlockingQueue<Delivery> queue = (BlockingQueue<Delivery>) queueField.get(consumer);
                                 if (!queue.isEmpty()) {
                                     Field queuesField = a.getDeclaredField("queues");
                                     queuesField.setAccessible(true);
                                     logger.info("当前消费者线程:" + JSON.toJSONString(queuesField.get(consumer)) + "存在消息,消息数:" + queue.size() + ",正在清除");
                                     //先清除队列
                                     queue.clear();
                                 }
                                 canceledConsumers.add(consumer);
                                 consumerIterator.remove();
                                 boolean declaring = (boolean) declaringField.get(consumer);

                                 if (declaring) {
                                     Thread thread = (Thread) threadField.get(consumer);
                                     thread.interrupt();
                                 }
                             }
                         } else {
                             logger.info("rabbit mq容器已被关闭,返回");
                             return;
                         }
                         String queueNames = JSON.toJSONString(((SimpleMessageListenerContainer) p).getQueueNames());

                         logger.info("等待所有消费者线程结束 queueNames:"+queueNames);
                         boolean finished = cancellationLock.await(rabbitmqWaitTime, TimeUnit.SECONDS);
                         if (finished) {
                             logger.info("成功等待所有消费者线程结束 queueNames:"+queueNames);
                         } else {
                             logger.info("存在消费者未在" + rabbitmqWaitTime + "s 内结束");
                             canceledConsumers.forEach(consumer -> {
                                 logger.info("强制停止消费者线程");
                                 consumer.stop();
                             });
                         }
                     }
                     synchronized (rabbitmqMonitor) {
                         consumers = null;
                         //容器还需要走this.lifecycleMonitor.notifyAll()
                         //cancellationLock.deactivate();
                     }
                 } catch (Exception e) {
                     e.printStackTrace();
                 }
             }
     );

 }

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值