You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 14:30:53