赶紧的,用户已经打电话来投诉线上出问题了

登峰小蚁 2019-12-24

出bug了

如标题所示,用户资金获取失败,线上某个服务通过dubbo调用接口都返回异常。
赶紧连上服务器看日志,进去一看吓到了。

Cause: java.sql.SQLException: connection holder is null
; uncategorized SQLException for SQL []; SQL state [null]; error code [0]; connection holder is null; nested exception is java.sql.SQLException: connection holder is null
        at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:84)
        at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
        at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
        at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:73)
        at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:371)
        at com.sun.proxy.$Proxy23.selectOne(Unknown Source)

Caused by: java.sql.SQLException: connection holder is null
        at com.alibaba.druid.pool.DruidPooledConnection.checkStateInternal(DruidPooledConnection.java:1155)
        at com.alibaba.druid.pool.DruidPooledConnection.checkState(DruidPooledConnection.java:1148)
        at com.alibaba.druid.pool.DruidPooledConnection.prepareStatement(DruidPooledConnection.java:336)
        at org.apache.ibatis.executor.statement.PreparedStatementHandler.instantiateStatement(PreparedStatementHandler.java:75)
        at org.apache.ibatis.executor.statement.BaseStatementHandler.prepare(BaseStatementHandler.java:85)
        at org.apache.ibatis.executor.statement.RoutingStatementHandler.prepare(RoutingStatementHandler.java:57)
        at org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.java:73)
        at org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:59)
        at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:267)
        at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:137)
        at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:96)
        at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:77)
        at sun.reflect.GeneratedMethodAccessor223.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)


Caused by: java.io.EOFException: Can not read response from server. Expected to read 4 bytes, read 0 bytes before connection was unexpectedly lost.
        at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:3143)
        at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3597)
        ... 86 more

分析问题

全部是数据库的连接获取不到,技术栈说明下。我们是dubbo远程调用,数据库连接池是druid,数据库用的是mysql。
登录mysql 服务器 show full processlist 命令查,未发现异常。再查看mysql超时设置。
客户那边一直再催问题,没办法先重启这个台服务看看。先重启了一台机器发现好了,难道重启之后释放掉了?

万能大法,重启起效了?不行得找到根本到原因不然下一次肯定还有报这种错误。
查看Druid 的配置文件

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
        init-method="init" destroy-method="close" >
    

        <!-- 配置获取连接等待超时的时间 -->
        <property name="maxWait" value="60000" />

        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="60000" />
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="300000" />

        <property name="validationQuery" value="select now()" />
        <property name="testWhileIdle" value="true" />
        <property name="testOnBorrow" value="false" />
        <property name="testOnReturn" value="false" />
        <property name="removeAbandoned" value="true" />
        <property name="removeAbandonedTimeout" value="1800" />
        <property name="logAbandoned" value="true" />
    </bean>

赶紧的,用户已经打电话来投诉线上出问题了
赶紧的,用户已经打电话来投诉线上出问题了

赶紧的,用户已经打电话来投诉线上出问题了

removeAbandonedTimeout 这个配置是1800秒30分钟。由于我们是整站拆分了多个服务,每个服务还有多个节点。
都连接的同一个数据库实例,数据库的连接数都是固定的。应该是removeAbandonedTimeout ,超时时间设置的问题。
当一个连接用法,不是立马释放,生产环境配置的是30分钟,当多个服务同时取拿数据库的连接释放的时间都是30分钟。
再有数据库请求过来连接拿不到,连接池就会爆掉,以下代码是释放不需要的连接。

public int removeAbandoned() {
        int removeCount = 0;

        long currrentNanos = System.nanoTime();

        List<DruidPooledConnection> abandonedList = new ArrayList<DruidPooledConnection>();

        activeConnectionLock.lock();
        try {
            Iterator<DruidPooledConnection> iter = activeConnections.keySet().iterator();

            for (; iter.hasNext();) {
                DruidPooledConnection pooledConnection = iter.next();

                if (pooledConnection.isRunning()) {
                    continue;
                }

                long timeMillis = (currrentNanos - pooledConnection.getConnectedTimeNano()) / (1000 * 1000);

                if (timeMillis >= removeAbandonedTimeoutMillis) {
                    iter.remove();
                    pooledConnection.setTraceEnable(false);
                    abandonedList.add(pooledConnection);
                }
            }
        } finally {
            activeConnectionLock.unlock();
        }

总结

此次关于数据库都连接池的问题,暂时先改了超时的时间。观察了下还没发现再有此错误,等后续问题记录下来分享。
欢迎大家扫一扫,认识不一样的我。??
赶紧的,用户已经打电话来投诉线上出问题了

相关推荐

lionelf / 0评论 2020-07-28