一個基於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