Quzrtz系列(一):Failed to override connection auto commit/transaction isolation

1、問題描述

使用quartz對做業進行調度時,報出下列異常:html

2018-09-27 06:00:00,149 WARN [org.quartz.impl.jdbcjobstore.JobStoreTX] Failed to override connection auto commit/transaction isolation.
com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: The last packet successfully received from the server was 437823 seconds ago.The last packet sent successfully to the server was 437823 seconds ago, which is longer than the server configured value of 'wait_timeout'. You should consider either expiring and/or testing connection validity before use in your application, increasing the server configured values for client timeouts, or using the Connector/J connection property 'autoReconnect=true' to avoid this problem.
at sun.reflect.GeneratedConstructorAccessor246.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl. java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:406) 
 
[Scheduler_QuartzSchedulerThread] WARN org.quartz.impl.jdbcjobstore.JobStoreTX - Failed to override connection auto commit/transaction isolation.
com.mysql.jdbc.CommunicationsException: Communications link failure due to underlying exception: 

** BEGIN NESTED EXCEPTION ** 

java.net.SocketException
MESSAGE: Broken pipe

STACKTRACE:

java.net.SocketException: Broken pipe
    at java.net.SocketOutputStream.socketWrite0(Native Method)
    at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:92)
    at java.net.SocketOutputStream.write(SocketOutputStream.java:136)
    at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:65)
    at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:123)
    at com.mysql.jdbc.MysqlIO.send(MysqlIO.java:2744)
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1612)
    at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:1723)
    at com.mysql.jdbc.Connection.execSQL(Connection.java:3277)
    at com.mysql.jdbc.Connection.setAutoCommit(Connection.java:5442)
    at org.apache.commons.dbcp.DelegatingConnection.setAutoCommit(DelegatingConnection.java:237)
    at org.quartz.impl.jdbcjobstore.AttributeRestoringConnectionInvocationHandler.setAutoCommit(AttributeRestoringConnectionInvocationHandler.java:91)
    at org.quartz.impl.jdbcjobstore.AttributeRestoringConnectionInvocationHandler.invoke(AttributeRestoringConnectionInvocationHandler.java:65)
    at $Proxy4.setAutoCommit(Unknown Source)
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.getConnection(JobStoreSupport.java:711)
    at org.quartz.impl.jdbcjobstore.JobStoreTX.getNonManagedTXConnection(JobStoreTX.java:72)
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.executeInNonManagedTXLock(JobStoreSupport.java:3757)
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.acquireNextTrigger(JobStoreSupport.java:2729)
    at org.quartz.core.QuartzSchedulerThread.run(QuartzSchedulerThread.java:266)


** END NESTED EXCEPTION **

使用的quartz版本爲2.2.3,數據庫的配置以下:java

org.quartz.jobStore.dataSource = myDS
org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.myDS.URL = jdbc:mysql://localhost:3306/quartz?autoReconnect=true&characterEncoding=utf-8
org.quartz.dataSource.myDS.user = root
org.quartz.dataSource.myDS.password = root
org.quartz.dataSource.myDS.maxConnections =50

2、問題分析

  • 數據庫鏈接池技術C3P0

    MySql服務器默認的「wait_timeout」是8小時,也就是說一個connection空閒超過8個小時,MySql將自動斷開該connection。 這就是問題的所在,在C3P0 pools中的connections若是空閒超過8小時,MySql將其斷開,而C3P0並不知道該connection已經失效,若是這時有Client請求connection,C3P0將該失效的Connection提供給Client,將會形成上面的異常。mysql

  • Quartz數據庫配置

    quartz定時任務使用時數據庫鏈接默認是maxIdleTime=0,即永不放棄鏈接。在Spring Boot啓動服務,控制檯打印的quartz數據庫配置信息以下:web

2018-09-28 14:38:31.957  INFO 13932 --- [           main] com.mchange.v2.c3p0.C3P0Registry         : Initializing c3p0-0.9.1.1 [built 15-March-2007 01:32:31; debug? true; trace: 10]
2018-09-28 14:38:31.988  INFO 13932 --- [           main] org.quartz.impl.StdSchedulerFactory      : Using default implementation for ThreadExecutor
2018-09-28 14:38:32.018  INFO 13932 --- [           main] org.quartz.core.SchedulerSignalerImpl    : Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
2018-09-28 14:38:32.018  INFO 13932 --- [           main] org.quartz.core.QuartzScheduler          : Quartz Scheduler v.2.2.3 created.
2018-09-28 14:38:32.019  INFO 13932 --- [           main] org.quartz.impl.jdbcjobstore.JobStoreTX  : Using db table-based data access locking (synchronization).
2018-09-28 14:38:32.021  INFO 13932 --- [           main] org.quartz.impl.jdbcjobstore.JobStoreTX  : JobStoreTX initialized.
2018-09-28 14:38:32.021  INFO 13932 --- [           main] org.quartz.core.QuartzScheduler          : Scheduler meta-data: Quartz Scheduler (v2.2.3) 'MyScheduler' with instanceId 'MyScheduler'
  Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
  NOT STARTED.
  Currently in standby mode.
  Number of jobs executed: 0
  Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 50 threads.
  Using job-store 'org.quartz.impl.jdbcjobstore.JobStoreTX' - which supports persistence. and is clustered.

2018-09-28 14:38:32.021  INFO 13932 --- [           main] org.quartz.impl.StdSchedulerFactory      : Quartz scheduler 'CMDBScheduler' initialized from an externally provided properties instance.
2018-09-28 14:38:32.021  INFO 13932 --- [           main] org.quartz.impl.StdSchedulerFactory      : Quartz scheduler version: 2.2.3
2018-09-28 14:38:32.021  INFO 13932 --- [           main] org.quartz.core.QuartzScheduler          : JobFactory set to: com.cmb.cmdb.bean.JobFactory@26844abb
2018-09-28 14:38:32.083  INFO 13932 --- [           main] c.m.v.c.i.AbstractPoolBackedDataSource   : Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> rhcsim9y8knjf2ph7q1r|51a6cc2a, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> rhcsim9y8knjf2ph7q1r|51a6cc2a, idleConnectionTestPeriod -> 50, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://localhost:3306/quartz?autoReconnect=true&characterEncoding=utf-8, lastAcquisitionFailureDefaultUser -> null, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 50, maxStatements -> 0, maxStatementsPerConnection -> 120, minPoolSize -> 1, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> select 1, properties -> {user=******, password=******}, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> true, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ]
注意到**maxIdleTime -> 0**,這是由於默認**org.quartz.dataSource.myDS.discardIdleConnectionsSeconds**沒有配置。該配置項表示數據庫鏈接在空閒以後放棄鏈接幾秒鐘。0表示禁用該功能。默認值爲0。

3、Quartz數據源鏈接池

  • Quartz各版本數據庫鏈接池技術

    Quartz 2.0 之前 DBCPspring

    Quartz 2.0 之後 C3P0(包含2.0)sql

4、DBCP 和C3P0的使用和區別

  • DBCP 介紹

    DBCP(DataBase connection pool),數據庫鏈接池。是 apache 上的一個 java 鏈接池項目,也是 tomcat 使用的鏈接池組件。單獨使用DBCP須要3個包:common-dbcp.jar,common-pool.jar,common-collections.jar。因爲創建數據庫鏈接是一個很是耗時耗資源的行爲,因此經過鏈接池預先同數據庫創建一些鏈接,放在內存中,應用程序須要創建數據庫鏈接時直接到鏈接池中申請一個就行,用完後再放回去。數據庫

  • C3P0介紹

    C3P0是一個開源的JDBC鏈接池,它實現了數據源和JNDI綁定,支持JDBC3規範和JDBC2的標準擴展。目前使用它的開源項目有Hibernate,Spring等。apache

  • DBCP 和C3P0區別

    DBCP沒有自動的去回收空閒鏈接的功能, C3P0有自動回收空閒鏈接功能。 二者主要是對數據鏈接的處理方式不一樣,C3P0提供最大空閒時間,DBCP提供最大鏈接數。 前者當鏈接超過最大空閒鏈接時間時,當前鏈接就會被斷掉。DBCP當鏈接數超過最大鏈接數時,全部鏈接都會被斷開。DBCP的原理是維護多個鏈接對象Connection,在web項目要鏈接數據庫時直接使用它維護的對象進行鏈接,省去每次都要建立鏈接對象的麻煩。提升效率和減小內存使用。C3P0能夠自動回收鏈接,DBCP須要本身手動釋放資源返回。不過DBCP效率比較高。緩存

  • DBCP和C3P0在Spring的配置

<!-- 配置dbcp數據源 -->      
<bean id="dataSource2" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">      <property name="driverClassName" value="${jdbc.driverClassName}"/>        
   <property name="url" value="${jdbc.url}"/>        
   <property name="username" value="${jdbc.username}"/>        
   <property name="password" value="${jdbc.password}"/>        
   <!-- 池啓動時建立的鏈接數量 -->        
   <property name="initialSize" value="5"/>        
   <!-- 同一時間能夠從池分配的最多鏈接數量。設置爲0時表示無限制。 -->        
   <property name="maxActive" value="30"/>        
   <!-- 池裏不會被釋放的最多空閒鏈接數量。設置爲0時表示無限制。 -->        
   <property name="maxIdle" value="20"/>        
   <!-- 在不新建鏈接的條件下,池中保持空閒的最少鏈接數。 -->        
   <property name="minIdle" value="3"/>        
   <!-- 設置自動回收超時鏈接 -->          
   <property name="removeAbandoned" value="true" />        
   <!-- 自動回收超時時間(以秒數爲單位) -->          
   <property name="removeAbandonedTimeout" value="200"/>        
   <!-- 設置在自動回收超時鏈接的時候打印鏈接的超時錯誤  -->         
   <property name="logAbandoned" value="true"/>        
   <!-- 等待超時以毫秒爲單位,在拋出異常以前,池等待鏈接被回收的最長時間(當沒有可用鏈接時)。設置爲-1表示無限等待。  -->          
   <property name="maxWait" value="100"/>        
 </bean>       
 
 <!-- 配置c3p0數據源 -->    
 <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">     <property name="jdbcUrl" value="${jdbc.url}" />        
    <property name="driverClass" value="${jdbc.driverClassName}" />        
    <property name="user" value="${jdbc.username}" />        
    <property name="password" value="${jdbc.password}" />       
    <!--鏈接池中保留的最大鏈接數。Default: 15 -->       
    <property name="maxPoolSize" value="100" />        
    <!--鏈接池中保留的最小鏈接數。-->        
    <property name="minPoolSize" value="1" />        
    <!--初始化時獲取的鏈接數,取值應在minPoolSize與maxPoolSize之間。Default: 3 -->        
    <property name="initialPoolSize" value="10" />        
    <!--最大空閒時間,60秒內未使用則鏈接被丟棄。若爲0則永不丟棄。Default: 0 -->        
    <property name="maxIdleTime" value="30" />        
    <!--當鏈接池中的鏈接耗盡的時候c3p0一次同時獲取的鏈接數。Default: 3 -->        
    <property name="acquireIncrement" value="5" />        
    <!--JDBC的標準參數,用以控制數據源內加載的PreparedStatements數量。但因爲預緩存的statements屬於單個         connection而不是整個鏈接池。因此設置這個參數須要考慮到多方面的因素。若是maxStatements與                   maxStatementsPerConnection均爲0,則緩存被關閉。Default: 0-->        
    <property name="maxStatements" value="0" />                 
    <!--每60秒檢查全部鏈接池中的空閒鏈接。Default: 0 -->        
    <property name="idleConnectionTestPeriod" value="60" />                 
    <!--定義在從數據庫獲取新鏈接失敗後重復嘗試的次數。Default: 30 -->        
    <property name="acquireRetryAttempts" value="30" />                
    <!--獲取鏈接失敗將會引發全部等待鏈接池來獲取鏈接的線程拋出異常。可是數據源仍有效保留,並在下次調用getConnection()的時候繼續嘗試獲取鏈接。若是設爲true,那麼在嘗試獲取鏈接失敗後該數據源將申明已斷開並永久關閉。Default: false-->        
    <property name="breakAfterAcquireFailure" value="true" />                 
    <!--因性能消耗大請只在須要的時候使用它。若是設爲true那麼在每一個connection提交的時候都將校驗其有效性。建議使用idleConnectionTestPeriod或automaticTestTable等方法來提高鏈接測試的性能。Default: false -->           <property name="testConnectionOnCheckout"  value="false" />            
</bean>

5、Quartz配置DataSources

若是您使用JDBC-Jobstore,則須要使用DataSource(或使用兩個DataSource,若是您使用JobStoreCMT)。

DataSources能夠經過三種方式進行配置:
  1. 在quartz.properties文件中指定的全部池屬性,以便Quartz能夠本身建立DataSource。
  2. 能夠指定應用程序服務器管理的Datasource的JNDI位置,以便Quartz可使用它。
  3. 自定義的org.quartz.utils.ConnectionProvider實現。tomcat

    建議您將Datasource max鏈接大小配置爲至少線程池中的工做線程數量加上三個。若是您的應用程序也頻繁調用調度程序API,則可能須要其餘鏈接。若是您使用JobStoreCMT,則「非受管理」數據源的最大鏈接大小應至少爲4。

    您定義的每一個DataSource(一般爲一個或兩個)必須爲一個名稱,而且您爲每一個定義的屬性必須包含該名稱,以下所示。DataSource的「NAME」能夠是任何您想要的,除了可以在分配給JDBCJobStore以後可以識別它以外,沒有什麼意義。

Property Name Required Type Default Value
org.quartz.dataSource.NAME.driver yes String null
org.quartz.dataSource.NAME.URL yes String null
org.quartz.dataSource.NAME.user no String ""
org.quartz.dataSource.NAME.password no String ""
org.quartz.dataSource.NAME.maxConnections no int 10
org.quartz.dataSource.NAME.validationQuery no String null
org.quartz.dataSource.NAME.idleConnectionValidationSeconds no int 50
org.quartz.dataSource.NAME.validateOnCheckout no boolean false
org.quartz.dataSource.NAME.discardIdleConnectionsSeconds no int 0 (disabled)

org.quartz.dataSource.NAME.driver

必須是數據庫的JDBC驅動程序的java類名稱。

org.quartz.dataSource.NAME.URL

鏈接到數據庫的鏈接URL(主機,端口等)。

org.quartz.dataSource.NAME.user

鏈接到數據庫時要使用的用戶名。

org.quartz.dataSource.NAME.password

鏈接到數據庫時使用的密碼。

org.quartz.dataSource.NAME.maxConnections

DataSource能夠在其鏈接池中建立的最大鏈接數。

org.quartz.dataSource.NAME.validationQuery

是可選的SQL查詢字符串,DataSource可用於檢測和替換失敗/損壞的鏈接。例如,oracle用戶可能會選擇「從user_tables中選擇table_name」 - 這是一個不該該失敗的查詢 - 除非鏈接其實是壞的。

org.quartz.dataSource.NAME.idleConnectionValidationSeconds

空閒鏈接測試之間的秒數 - 僅在設置驗證查詢屬性時啓用。默認值爲50秒。

org.quartz.dataSource.NAME.validateOnCheckout

每次從池中檢索鏈接時,是否應該執行數據庫sql查詢來驗證鏈接,以確保它仍然有效。若是爲假,則在辦理登機手續時將進行驗證。默認值爲false。

org.quartz.dataSource.NAME.discardIdleConnectionsSeconds

它們在空閒以後放棄鏈接幾秒鐘。0禁用該功能。默認值爲0。

Quartz定義的DataSource示例

org.quartz.dataSource.myDS.driver = oracle.jdbc.driver.OracleDriver
org.quartz.dataSource.myDS.URL = jdbc:oracle:thin:@ 10.0.1.23:1521:demodb
org.quartz.dataSource.myDS.user = myUser
org.quartz.dataSource.myDS.password = myPassword
org.quartz.dataSource.myDS.maxConnections = 30

對Application Server DataSources的引用使用如下屬性定義:

Property Name Required Type Default Value
org.quartz.dataSource.NAME.jndiURL yes String null
org.quartz.dataSource.NAME.java.naming.factory.initial no String null
org.quartz.dataSource.NAME.java.naming.provider.url no String null
org.quartz.dataSource.NAME.java.naming.security.principal no String null
org.quartz.dataSource.NAME.java.naming.security.credentials no String null

org.quartz.dataSource.NAME.jndiURL

由應用程序服務器管理的DataSource的JNDI URL。

org.quartz.dataSource.NAME.java.naming.factory.initial

要使用的JNDI InitialContextFactory的(可選)類名。

org.quartz.dataSource.NAME.java.naming.provider.url

用於鏈接到JNDI上下文的(可選)URL。

org.quartz.dataSource.NAME.java.naming.security.principal

用於鏈接到JNDI上下文的(可選)用戶主體。

org.quartz.dataSource.NAME.java.naming.security.credentials

用於鏈接到JNDI上下文的(可選)用戶憑據。

從應用程序服務器引用的數據源示例

org.quartz.dataSource.myOtherDS.jndiURL = JDBC / myDataSource
org.quartz.dataSource.myOtherDS.java.naming.factory.initial = com.evermind.server.rmi.RMIInitialContextFactory
org.quartz.dataSource.myOtherDS.java.naming.provider.url = ormi:// localhost
 org.quartz.dataSource.myOtherDS.java.naming.security.principal = admin
org.quartz.dataSource.myOtherDS.java.naming.security.credentials = 123

自定義ConnectionProvider實現

Property Name Required Type Default Value
org.quartz.dataSource.NAME.connectionProvider.class yes String (class name) null

org.quartz.dataSource.NAME.connectionProvider.class

要使用的ConnectionProvider的類名。實例化以後,Quartz能夠自動設置實例上的配置屬性,bean樣式。

使用自定義ConnectionProvider實現的示例

org.quartz.dataSource.myCustomDS.connectionProvider.class = com.foo.FooConnectionProvider
org.quartz.dataSource.myCustomDS.someStringProperty = someValue
org.quartz.dataSource.myCustomDS.someIntProperty = 5

6、解決方案

  • 方案一:

    quzrtz的dataSource增長以下配置,加上最大空閒時間,設置爲60s:
org.quartz.datasource.qzDS.validateOnCheckout=true
org.quartz.datasource.qzDS.validationQuery=select 1
org.quartz.dataSource.myDS.discardIdleConnectionsSeconds=60
  • 方案二:

    自定義Druid數據庫鏈接池,須要實現org.quartz.utils.ConnectionProvider接口,同時引入Druid相關的jar包,具體實現能夠參考:https://blog.csdn.net/minicto/article/details/77897577或https://www.cnblogs.com/zouhao510/p/5313600.html

參考文章

  1. http://blog.sina.com.cn/s/blog_12cceab5a0102xav6.html
  2. https://blog.csdn.net/hehuanchun0311/article/details/80591929
  3. https://blog.csdn.net/minicto/article/details/77897577
  4. https://blog.csdn.net/retry000/article/details/79494299
  5. http://blog.51cto.com/13797478/2130872
  6. https://stackoverflow.com/questions/9159372/java-net-socketexception-broken-pipe-with-quartz-and-mysql-and-tomcat-tomcat-c
  7. https://stackoverflow.com/questions/9094578/quartz-failure-in-notifyjobstorejobcomplete-method
  8. http://www.cnblogs.com/huahua035/p/7839834.html
  9. https://www.w3cschool.cn/quartz_doc/quartz_doc-d8pn2do9.html
相關文章
相關標籤/搜索