如何让Grails 4.0.3应用在RabbitMQ不可用时仍正常启动
问题
启动Grails 4.0.3应用时,若RabbitMQ处于不可用状态,应用会直接启动失败并抛出连接拒绝错误,错误栈如下:
java.net.ConnectException: Conexão recusada (Connection refused) at java.base/java.net.PlainSocketImpl.socketConnect(Native Method) at java.base/java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:399) at java.base/java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:242) at java.base/java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:224) at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392) at java.base/java.net.Socket.connect(Socket.java:609) at com.rabbitmq.client.impl.SocketFrameHandlerFactory.create(SocketFrameHandlerFactory.java:60) at com.rabbitmq.client.impl.recovery.RecoveryAwareAMQConnectionFactory.newConnection(RecoveryAwareAMQConnectionFactory.java:62) at com.rabbitmq.client.impl.recovery.AutorecoveringConnection.init(AutorecoveringConnection.java:156) at com.rabbitmq.client.ConnectionFactory.newConnection(ConnectionFactory.java:1095) at com.rabbitmq.client.ConnectionFactory.newConnection(ConnectionFactory.java:1054) at com.rabbitmq.client.ConnectionFactory.newConnection(ConnectionFactory.java:1012) at com.rabbitmq.client.ConnectionFactory.newConnection(ConnectionFactory.java:1201) at com.rabbitmq.client.ConnectionFactory$newConnection$6.call(Unknown Source) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:115) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:127) at com.budjb.rabbitmq.connection.ConnectionContextImpl.start(ConnectionContextImpl.groovy:132) at com.budjb.rabbitmq.RabbitManagedContext$start$0.call(Unknown Source) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:115) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:119) at com.budjb.rabbitmq.connection.ConnectionManagerImpl.start(ConnectionManagerImpl.groovy:194) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:101) at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:323) at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:351) at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:64) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:51) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:156) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:168) at com.budjb.rabbitmq.connection.ConnectionManagerImpl$_start_closure4.doCall(ConnectionManagerImpl.groovy:179) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:101) at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:323) at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:263) at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1041) at groovy.lang.Closure.call(Closure.java:405) at groovy.lang.Closure.call(Closure.java:421) at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2296) at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2281) at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2322) at org.codehaus.groovy.runtime.dgm$186.invoke(Unknown Source) at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:244) at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:53) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:115) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:127) at com.budjb.rabbitmq.connection.ConnectionManagerImpl.start(ConnectionManagerImpl.groovy:177) at com.budjb.rabbitmq.RabbitManager$start$1.call(Unknown Source) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:115) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:119) at com.budjb.rabbitmq.RabbitContextImpl.startConnections(RabbitContextImpl.groovy:201) at com.budjb.rabbitmq.RabbitContextImpl.start(RabbitContextImpl.groovy:82) at com.budjb.rabbitmq.RabbitContext$start$0.call(Unknown Source) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:115) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:119) at com.budjb.rabbitmq.RabbitContextProxy.start(RabbitContextProxy.groovy:65) at com.budjb.rabbitmq.RabbitContext$start$0.call(Unknown Source)
由于RabbitMQ并非应用核心组件,希望此时应用仅输出WARN级日志提示RabbitMQ不可用,仍能正常启动,该如何实现?
当前application.yml配置如下:
grails: profile: rest-api codegen: defaultPackage: plataforma.msid.grails4 gorm: autowire: true reactor: # 是否将GORM事件转换为Reactor事件 # 默认禁用以提升性能! events: false info: app: name: '@info.app.name@' version: '@info.app.version@' grailsVersion: '@info.app.grailsVersion@' spring: jmx: unique-names: true main: banner-mode: "off" groovy: template: check-template-location: false devtools: restart: additional-exclude: - '*.gsp' - '**/*.gsp' - '*.gson' - '**/*.gson' - 'logback.groovy' - '*.properties' management: endpoints: enabled-by-default: true endpoint: health: show-details : always --- grails: mime: disable: accept: header: userAgents: - Gecko - WebKit - Presto - Trident types: json: - application/json - text/json hal: - application/hal+json - application/hal+xml xml: - text/xml - application/xml atom: application/atom+xml css: text/css csv: text/csv js: text/javascript rss: application/rss+xml text: text/plain all: '*/*' urlmapping: cache: maxsize: 1000 controllers: defaultScope: singleton converters: encoding: UTF-8 --- hibernate: allow_update_outside_transaction: true cache: queries: false use_second_level_cache: false use_query_cache: false dataSource: pooled: true jmxExport: true driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver logSql: true environments: development: dataSource: dbCreate: update dialect: org.hibernate.dialect.SQLServer2017Dialect url: jdbc:sqlserver://ip:port;databaseName=BD_DOC_DIGITAL_CMB_PLATAFORMA;domain=DMZHLG username: usr password: 'dddd' cmbaut: gettingNewTokenUrl: 'https://hml.project/plataforma/cmbaut/api/v1/auth/refresh?grant_type=refresh_token&refresh_token=' gettingUserId: 'https://hml.project/plataforma/cmbaut/api/v1/parts/$partId/users/$cpf' rabbitmq: connections: - name: main host: 192.168.xxx.xx port: 5672 username: cmbid password: xxxxx virtualHost: "/plataforma" grails: plugin: springsecurity: rest: token: storage: jwt: secret: 'xxxx' --- server: servlet: contextPath: /cmbid port: 8081 max-http-header-size: 65536
解决方案
从错误栈判断你使用的是budjb/rabbitmq-client插件,可通过以下方式实现RabbitMQ不可用时应用正常启动:
1. 配置层面调整(推荐)
在application.yml的RabbitMQ配置中添加核心开关failOnStartup: false,同时配置自动重连和超时参数:
rabbitmq: failOnStartup: false # 启动时连接失败不终止应用,仅输出WARN日志 connections: - name: main host: 192.168.xxx.xx port: 5672 username: cmbid password: xxxxx virtualHost: "/plataforma" automaticRecoveryEnabled: true # 开启RabbitMQ客户端自动重连 connectionTimeout: 5000 # 缩短连接超时时间,避免启动等待过长
failOnStartup是插件提供的原生配置项,设置为false后,启动阶段RabbitMQ连接失败不会中断应用启动流程。
2. 代码层面自定义异常处理
若配置项未生效,可通过扩展插件的ConnectionManagerImpl类捕获连接异常:
创建grails-app/services/com/budjb/rabbitmq/connection/CustomConnectionManagerImpl.groovy:
package com.budjb.rabbitmq.connection import org.slf4j.Logger import org.slf4j.LoggerFactory class CustomConnectionManagerImpl extends ConnectionManagerImpl { private static final Logger log = LoggerFactory.getLogger(CustomConnectionManagerImpl) @Override void start() { try { super.start() } catch (Exception e) { log.warn("RabbitMQ连接失败,应用将继续启动: ${e.message}", e) // 可在此添加定时重连逻辑,比如使用Spring Task定时重试连接 } } }
然后在grails-app/conf/spring/resources.groovy中替换默认bean:
beans = { connectionManager(com.budjb.rabbitmq.connection.CustomConnectionManagerImpl) { rabbitContext = ref('rabbitContext') connectionContexts = ref('connectionContexts') } }
3. 日志配置优化
在grails-app/conf/logback.groovy中配置RabbitMQ相关包的日志级别为WARN:
logger('com.budjb.rabbitmq', WARN, ['STDOUT'], false) logger('com.rabbitmq', WARN, ['STDOUT'], false)
完成上述配置后,启动时若RabbitMQ不可用,应用会输出WARN级日志提示,随后正常启动;当RabbitMQ恢复后,插件会通过自动重连机制恢复连接。
内容的提问来源于stack exchange,提问作者D Nunes
相关产品推荐
相关产品推荐

