软件定制开发供应商SpringBoot 禁用RabbitMQ自启动、设置RabbitMQ启动开关

一、需求背景

SpringBoot软件定制开发供应商项目里使用了,软件定制开发供应商但某些场景下,软件定制开发供应商不希望项目启动时自动检查RabbitMQ连接,例如:

  • 场景1:软件定制开发供应商在开发过程中,若RabbitMQ服务未启动,会导致SpringBoot项目启动失败。
  • 场景2:RabbitMQ做为系统里的一个插件功能,可能不同的客户部署环境中,并不需要启动RabbitMQ,但是要保证项目正常运行。

因此需要在项目里实现开关配置,可以动态的配置在项目启动时,是否自动启动RabbitMQ连接。

启动错误示例:

  1. [2022-10-12 11:18:11.456] traceId= [RMI TCP Connection(8)-192.168.18.118] WARN o.s.boot.actuate.amqp.RabbitHealthIndicator - Rabbit health check failed
  2. org.springframework.amqp.AmqpConnectException: java.net.ConnectException: Connection refused: connect
  3. at org.springframework.amqp.rabbit.support.RabbitExceptionTranslator.convertRabbitAccessException(RabbitExceptionTranslator.java:61)
  4. at org.springframework.amqp.rabbit.connection.AbstractConnectionFactory.createBareConnection(AbstractConnectionFactory.java:602)
  5. at org.springframework.amqp.rabbit.connection.CachingConnectionFactory.createConnection(CachingConnectionFactory.java:725)
  6. at org.springframework.amqp.rabbit.connection.ConnectionFactoryUtils.createConnection(ConnectionFactoryUtils.java:252)
  7. at org.springframework.amqp.rabbit.core.RabbitTemplate.doExecute(RabbitTemplate.java:2173)
  8. at org.springframework.amqp.rabbit.core.RabbitTemplate.execute(RabbitTemplate.java:2146)
  9. at org.springframework.amqp.rabbit.core.RabbitTemplate.execute(RabbitTemplate.java:2126)
  10. at org.springframework.boot.actuate.amqp.RabbitHealthIndicator.getVersion(RabbitHealthIndicator.java:49)
  11. at org.springframework.boot.actuate.amqp.RabbitHealthIndicator.doHealthCheck(RabbitHealthIndicator.java:44)
  12. at org.springframework.boot.actuate.health.AbstractHealthIndicator.health(AbstractHealthIndicator.java:82)
  13. at org.springframework.boot.actuate.health.HealthIndicator.getHealth(HealthIndicator.java:37)
  14. at org.springframework.boot.actuate.health.HealthEndpoint.getHealth(HealthEndpoint.java:77)
  15. at org.springframework.boot.actuate.health.HealthEndpoint.getHealth(HealthEndpoint.java:40)
  16. at org.springframework.boot.actuate.health.HealthEndpointSupport.getContribution(HealthEndpointSupport.java:130)
  17. at org.springframework.boot.actuate.health.HealthEndpointSupport.getAggregateContribution(HealthEndpointSupport.java:141)
  18. at org.springframework.boot.actuate.health.HealthEndpointSupport.getContribution(HealthEndpointSupport.java:126)
  19. at org.springframework.boot.actuate.health.HealthEndpointSupport.getHealth(HealthEndpointSupport.java:95)
  20. at org.springframework.boot.actuate.health.HealthEndpointSupport.getHealth(HealthEndpointSupport.java:66)
  21. at org.springframework.boot.actuate.health.HealthEndpoint.health(HealthEndpoint.java:71)
  22. at org.springframework.boot.actuate.health.HealthEndpoint.health(HealthEndpoint.java:61)
  23. at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  24. at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  25. at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  26. at java.lang.reflect.Method.invoke(Method.java:498)
  27. at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:282)
  28. at org.springframework.boot.actuate.endpoint.invoke.reflect.ReflectiveOperationInvoker.invoke(ReflectiveOperationInvoker.java:74)
  29. at org.springframework.boot.actuate.endpoint.annotation.AbstractDiscoveredOperation.invoke(AbstractDiscoveredOperation.java:60)
  30. at org.springframework.boot.actuate.endpoint.jmx.EndpointMBean.invoke(EndpointMBean.java:122)
  31. at org.springframework.boot.actuate.endpoint.jmx.EndpointMBean.invoke(EndpointMBean.java:97)
  32. at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
  33. at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
  34. at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1468)
  35. at javax.management.remote.rmi.RMIConnectionImpl.access$300(RMIConnectionImpl.java:76)
  36. at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1309)
  37. at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1401)
  38. at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:829)
  39. at sun.reflect.GeneratedMethodAccessor212.invoke(Unknown Source)
  40. at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  41. at java.lang.reflect.Method.invoke(Method.java:498)
  42. at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:357)
  43. at sun.rmi.transport.Transport$1.run(Transport.java:200)
  44. at sun.rmi.transport.Transport$1.run(Transport.java:197)
  45. at java.security.AccessController.doPrivileged(Native Method)
  46. at sun.rmi.transport.Transport.serviceCall(Transport.java:196)
  47. at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:573)
  48. at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:834)
  49. at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:688)
  50. at java.security.AccessController.doPrivileged(Native Method)
  51. at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:687)
  52. at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
  53. at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
  54. at java.lang.Thread.run(Thread.java:748)
  55. Caused by: java.net.ConnectException: Connection refused: connect
  56. at java.net.DualStackPlainSocketImpl.waitForConnect(Native Method)
  57. at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:81)
  58. at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:476)
  59. at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:218)
  60. at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:200)
  61. at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:162)
  62. at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:394)
  63. at java.net.Socket.connect(Socket.java:606)
  64. at com.rabbitmq.client.impl.SocketFrameHandlerFactory.create(SocketFrameHandlerFactory.java:60)
  65. at com.rabbitmq.client.ConnectionFactory.newConnection(ConnectionFactory.java:1223)
  66. at com.rabbitmq.client.ConnectionFactory.newConnection(ConnectionFactory.java:1173)
  67. at org.springframework.amqp.rabbit.connection.AbstractConnectionFactory.connectAddresses(AbstractConnectionFactory.java:640)
  68. at org.springframework.amqp.rabbit.connection.AbstractConnectionFactory.connect(AbstractConnectionFactory.java:615)
  69. at org.springframework.amqp.rabbit.connection.AbstractConnectionFactory.createBareConnection(AbstractConnectionFactory.java:565)
  70. ... 50 common frames omitted

二、实现方案

方案一、配置autoStartup环境变量,关闭自启动(不推荐)

在bootstrap.yml中配置:

  1. spring:
  2. rabbitmq:
  3. listener:
  4. direct:
  5. auto-startup: false
  6. simple:
  7. auto-startup: false
  8. stream:
  9. auto-startup: false
  10. rabbitmq:
  11. start: false

在SpringBootApplicaiton启动类中配置:

  1. import org.slf4j.Logger;
  2. import org.slf4j.LoggerFactory;
  3. import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistry;
  4. import org.springframework.beans.factory.annotation.Value;
  5. import org.springframework.boot.SpringApplication;
  6. import org.springframework.boot.autoconfigure.SpringBootApplication;
  7. import org.springframework.context.ConfigurableApplicationContext;
  8. import org.springframework.context.annotation.Bean;
  9. import javax.annotation.Resource;
  10. @SpringBootApplication
  11. public class TestServerApp {
  12. static Logger logger = LoggerFactory.getLogger(TestServerApp .class);
  13. public static void main(String[] args) {
  14. ConfigurableApplicationContext context = SpringApplication.run(TestServerApp .class, args);
  15. RabbitMQStart rabbitMQRun = context.getBean(RabbitMQStart.class);
  16. rabbitMQRun.start();
  17. }
  18. @Bean
  19. public RabbitMQStart rabbitMQRun() {
  20. return new RabbitMQStart();
  21. }
  22. private static class RabbitMQStart {
  23. //为了在main中的static方法中使用@value注解只能用这种办法
  24. @Value("${rabbitmq.start}")
  25. private Boolean rabbitmqStart;
  26. @Resource
  27. RabbitListenerEndpointRegistry rabbitListenerEndpointRegistry;
  28. public void start() {
  29. if(rabbitmqStart)
  30. rabbitListenerEndpointRegistry.start();
  31. else
  32. rabbitListenerEndpointRegistry.stop();
  33. System.out.println("=================== Rabbitmq:"+rabbitmqStart+"===================");
  34. }
  35. }
  36. }

缺点:

1、配置麻烦,而且不能放到Nacos配置中心

2、代码侵入多,需要改代码。

3、项目启动时,RabbitMQ仍会触发一次尝试连接,控制台会报错。

拓展:

1、autoStartup变量在RabbitMQ包中的位置:

org.springframework.boot.autoconfigure.amqp.RabbitProperties下的:

        org.springframework.boot.autoconfigure.amqp.RabbitProperties.BaseContainer下的:

                private boolean autoStartup = true;

2、三种container(有啥区别?还没研究~)

org.springframework.boot.autoconfigure.amqp.RabbitProperties.StreamContainer

org.springframework.boot.autoconfigure.amqp.RabbitProperties.DirectContainer

org.springframework.boot.autoconfigure.amqp.RabbitProperties.SimpleContainer(默认)

方案二、排除RabbitMQ的自动配置(不推荐)

在SpringBootApplication启动类上使用exclude排除

@SpringBootApplication(exclude = {RabbitAutoConfiguration.class})

或者在yaml中配置

  1. spring:
  2. autoconfigure:
  3. exclude: org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration

注意:SpringBoot加载AutoConfig的时机,要早于连接配置中心(比如Nacos),因此该yaml配置不能放到配置中心上的文件中,需要放在项目本地的bootstrap.yml或者application.yml中。

缺点:

1、不能放到Nacos配置中心,在bootstrap.yml中配置,在发布版本时会被一起打包发布。

2、若想恢复项目启动时,RabbitMQ自动初始化连接,在fat jar启动时必须指定运行参数来去掉该配置,若是用docker镜像运行则更麻烦,需要配置环境变量:

java -Dspring.autoconfigure.exclude=空 -jar app.jar

方案三、自定义RabbitMQ自动配置类(推荐)

自定义RabbitMQ的自动配置类(使用@Configuration)

  1. import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
  2. import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
  3. import org.springframework.context.annotation.Configuration;
  4. /**
  5. * @desccription 自定义RabbitMQ的启动配置类,可以通过配置变量来控制启用、禁用
  6. * @auth wangshaopeng@talkweb.com.cn
  7. * @date 2022/10/12
  8. */
  9. @Configuration
  10. @ConditionalOnProperty("spring.rabbitmq.enable")
  11. public class MyRabbitAutoConfiguration extends RabbitAutoConfiguration {
  12. }

配置bootstrap.yal,排除默认的RabbitMQ自动配置类

  1. spring:
  2. autoconfigure:
  3. exclude: org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration

再配置RabbitMQ自动配置开关,此配置可以放在Nacos配置中心,因为使用的是@Configuration机制,而不是项目启动自动配置机制,因此读取开关配置的时机被延迟,可以等到读取配置中心完毕后再初始化RabbitMQ。

例如在nacos上配置rabbitmq.yml

  1. spring:
  2. rabbitmq:
  3. #配置rabbitMq启用开关
  4. enable: true
  5. host: 127.0.0.1
  6. port: 5672
  7. username: wsp
  8. password: bugaosuni
  9. virtual-host: /wsp

网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发