Druid重试机制配置重试breakAfterAcquireFailure失效及解决方案

在项目开发中,遇到druid在连接失败后一直重试,导致接口不会返回结果、日志不停打印,跟踪源码发现druid的失败重试在DruidDataSource类中,位置如下:

public void run() {
            DruidDataSource.this.initedLatch.countDown();
            long lastDiscardCount = 0L;
            int errorCount = 0;
            while(true) {
                ...
                try {
                    connection = DruidDataSource.this.createPhysicalConnection();
                } catch (SQLException var29) {
                    DruidDataSource.LOG.error("create connection SQLException, url: " + DruidDataSource.this.jdbcUrl + ", errorCode " + var29.getErrorCode() + ", state " + var29.getSQLState(), var29);
                    ++errorCount;
                    // 如果重试次数达到后进入if
                    if (errorCount > DruidDataSource.this.connectionErrorRetryAttempts && DruidDataSource.this.timeBetweenConnectErrorMillis > 0L) {
                        DruidDataSource.this.setFailContinuous(true);
                        if (DruidDataSource.this.failFast) {
                            DruidDataSource.this.lock.lock();

                            try {
                                DruidDataSource.this.notEmpty.signalAll();
                            } finally {
                                DruidDataSource.this.lock.unlock();
                            }
                        }
                        // breakAfterAcquireFailure为true结束掉while循环
                        if (DruidDataSource.this.breakAfterAcquireFailure) {
                            break;
                        }

                        try {
                            Thread.sleep(DruidDataSource.this.timeBetweenConnectErrorMillis);
                        } catch (InterruptedException var27) {
                            break;
                        }
                    }
                } catch (RuntimeException var30) {
                    DruidDataSource.LOG.error("create connection RuntimeException", var30);
                    DruidDataSource.this.setFailContinuous(true);
                    continue;
                } 
                ...
            }
        }

是否中断重试 breakAfterAcquireFailure 默认为false;
重试次数 connectionErrorRetryAttempts默认为1;
看完源码后找到问题所在,将connectionErrorRetryAttempts设置为失败后需要重试的次数,并把breakAfterAcquireFailure改为true就可以了
需要在创建DruidDataSource时添加,直接在配置文件内是无效的,在DruidDataSource源码中可以看到,它并没有去读取这两个配置:

 public void configFromPropety(Properties properties) {
        String property = properties.getProperty("druid.name");
        if (property != null) {
            this.setName(property);
        }

        property = properties.getProperty("druid.url");
        if (property != null) {
            this.setUrl(property);
        }

        property = properties.getProperty("druid.username");
        if (property != null) {
            this.setUsername(property);
        }

        property = properties.getProperty("druid.password");
        if (property != null) {
            this.setPassword(property);
        }
        ...

解决办法,可以在创建数据源DataSource的时候将配置写入:

spring:
    datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        druid:
            # 主库数据源
            master:
                driverClassName: com.mysql.cj.jdbc.Driver
                url: jdbc:mysql://127.0.0.1:3306/database?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
                username: username
                password: password
            slave:
                # 从数据源开关/默认关闭
                enabled: true
                driverClassName: com.mysql.cj.jdbc.Driver
                url: jdbc:mysql://127.0.0.1:3306/database?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
                username: username
                password: password
            # 初始连接数
            initialSize: 20
            # 最小连接池数量
            minIdle: 20
            # 最大连接池数量
            maxActive: 20
            # 配置获取连接等待超时的时间
            maxWait: 3000
            # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
            timeBetweenEvictionRunsMillis: 60000
            # 配置一个连接在池中最小生存的时间,单位是毫秒
            minEvictableIdleTimeMillis: 300000
            # 配置一个连接在池中最大生存的时间,单位是毫秒
            maxEvictableIdleTimeMillis: 900000
            # 配置检测连接是否有效
            validationQuery: SELECT 1 FROM DUAL
            # 重连失败后在重试规定次数后 终止重试
            breakAfterAcquireFailure: true
            # 失败重试次数
            connectionErrorRetryAttempts: 5
            testWhileIdle: true
            testOnBorrow: false
            testOnReturn: false
            webStatFilter: 
                enabled: true
            statViewServlet:
                enabled: true
                # 设置白名单,不填则允许所有访问
                allow:
                url-pattern: /druid/*
                # 控制台管理用户名和密码
                login-username: username
                login-password: password
            filter:
                stat:
                    enabled: true
                    # 慢SQL记录
                    log-slow-sql: true
                    slow-sql-millis: 1000
                    merge-sql: true
                wall:
                    config:
                        multi-statement-allow: true
    @Bean
    @ConfigurationProperties("spring.datasource.druid.master")
    public DataSource masterDataSource(DruidProperties druidProperties)
    {
        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
        // 在注入bean时配置
        datasource.setBreakAfterAcquireFailure(true);
        datasource.setConnectionErrorRetryAttempts(3);
        return druidProperties.dataSource(dataSource);
    }

配置完成后重启生效,但是如果数据库连接失败我们还是很有必要进行重试的,可以通过设置连接等待超时时间 maxWait 来让接口快速返回,源码部分在DruidDataSource.class中的getConnectionDirect(long maxWaitMillis)方法

  public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLException {
        int notFullTimeoutRetryCnt = 0;

        DruidPooledConnection poolableConnection;
        while(true) {
            while(true) {
                try {
                    // 进行连接 maxWaitMillis就是我们指定的maxWait时间
                    poolableConnection = this.getConnectionInternal(maxWaitMillis);
                    break;
                } catch (GetConnectionTimeoutException var23) {
                    if (notFullTimeoutRetryCnt > this.notFullTimeoutRetryCount || this.isFull()) {
                        throw var23;
                    }

                    ++notFullTimeoutRetryCnt;
                    if (LOG.isWarnEnabled()) {
                        LOG.warn("get connection timeout retry : " + notFullTimeoutRetryCnt);
                    }
                }
            }

有兴趣的可以debug看一下内部实现

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,076评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,658评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,732评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,493评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,591评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,598评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,601评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,348评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,797评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,114评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,278评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,953评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,585评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,202评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,180评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,139评论 2 352

推荐阅读更多精彩内容