一个基于springcloud的微服务项目,详细配置: SpringCloud + SpringMVC+SpringData JPA+ MySql+Postgresql
其中项目配置了多数据源,前期开发测试是没什么问题的,但是等到项目在服务器上面测试时,第二天就上午出现了数据库连接异常。经过查看日志发现下面这个异常:
注意异常信息: No operations allowed after connection closed。
也就是说jpa获取的连接是已经关闭的了,对一个关闭了的链接进行操作导致出现异常了。
原因:
之所以会出现这个异常,是因为MySQL5.0以后针对超长时间DB连接做了一个处理,那就是如果一个DB连接在无任何操作情况下过了8个小时后(Mysql 服务器默认的“wait_timeout”是8小时),Mysql会自动把这个连接关闭。这就是问题的所在,在连接池中的connections如果空闲超过8小时,mysql将其断开,而连接池自己并不知道该connection已经失效,如果这时有 Client请求connection,连接池将该失效的Connection提供给Client,将会造成上面的异常。
所以配置datasource时需要配置相应的连接池参数,定是去检查连接的有效性,定时清理无效的连接。
解决方法
在application.yml的两个数据源的配置下添加如下连接池配置(红色字体部分):
eureka: client: service-url: defaultZone: http://localhost:8761/eureka server: port: 9013 spring: application: name: api datasource: druid: #数据库连接1 mysql: name: mysql type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/datacube?useUnicode=true&characterEncoding=utf8&useSSL=false username: root password: rootrot #数据库连接2 greenplum: name: greenplum type: com.alibaba.druid.pool.DruidDataSource driver-class-name: org.postgresql.Driver url: jdbc:postgresql://localhost:5432/datacube?useUnicode=true&characterEncoding=utf8&useSSL=false username: root password: rootroot # 下面为连接池的补充设置,应用到上面所有数据源中 # 初始化大小,最小,最大 initialSize: 5 minIdle: 10 maxActive: 1000 #配置获取连接等待超时的时间 maxWait: 60000 #配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 timeBetweenEvictionRunsMillis: 60000 #配置一个连接在池中最小生存的时间,单位是毫秒 minEvictableIdleTimeMillis: 300000 #验证连接是否有效。此参数必须设置为非空字符串,下面三项设置成true才能生效 validationQuery: SELECT 1 #指明连接是否被空闲连接回收器(如果有)进行检验.如果检测失败,则连接将被从池中去除. testWhileIdle: true #指明是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个 testOnBorrow: true #指明是否在归还到池中前进行检验 testOnReturn: false #打开PSCache,并且指定每个连接上PSCache的大小 poolPreparedStatements: true maxPoolPreparedStatementPerConnectionSize: 20 #配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 filters: stat,wall,log4j #通过connectProperties属性来打开mergeSql功能;慢SQL记录 connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=1000;druid.stat.logSlowSql=true #合并多个DruidDataSource的监控数据 useGlobalDataSourceStat: true # WebStatFilter: # exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*" # stat-view-servlet: # login-username: admin # login-password: admin jpa: database-platform: org.hibernate.dialect.MySQL5InnoDBDialect show-sql: true hibernate: #dialect: org.hibernate.dialect.MySQL5Dialect ddl-auto: none naming: strategy: org.hibernate.cfg.ImprovedNamingStrategy
在这个项目中使用的是阿里的druid连接池,配置简单,除了数据库地址,驱动类,用户名和密码其他一起都是默认,开始的时候由于项目更新上线频率比较多,没有出现太多的问题,后来换库了 。导致之前的链接失效了,请求的时候时好时坏,跟了一下代码以及其他项目的配置,其中有一个属性 testOnBorrow设置为false(默认设置为false) ,testOnBorrow=false则不检测池里连接的可用性,
于是假如连接池中的连接被数据库关闭了,应用通过连接池getConnection时,都可能获取到这些不可用的连接,且这些连接如果不被其他线程回收的话,它们不会被连接池被废除,也不会重新被创建,占用了连接池的名额,项目本身作为服务端,数据库链接被关闭,客户端调用服务端就会出现大量的timeout,客户端设置了超时时间,然而主动断开,服务端必然出现close_wait ,由于tomcat 默认最大线程数是200,很快就挂掉,虽说多数源,没有问题的数据源,链接并发过来也会死掉,所以说加大tomcat 默认线程(server.tomcat.max-threads=3000)只是短时间内其他数据源链接不会死掉。
默认的配置不适用所有场景,所以使用的时候需要配合场景使用。
由于testOnborrow =true 很大的消耗性能,为了保证服务器的稳定,可以配合其他配置来避免这一点,配合testWhileIdle=true(但是默认为false) 和timeBetweenEvictionRunsMillis来避免这种问题,所以设置testOnborrow =false是可以提高效率的
原文:https://blog.csdn.net/qq_38023253/article/details/80815618