問題排查系列-Expected to read 4 bytes, read 0 bytes before connection

昨天夜里小伙伴上線新系統(tǒng)時巍虫,發(fā)現(xiàn)經(jīng)常服務(wù)連接數(shù)據(jù)庫報錯,第二天早上我收到了日志愈捅,主要的異常如下:

Caused by: com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure

The last packet successfully received from the server was 603,115 milliseconds ago.  The last packet sent successfully to the server was 603,115 milliseconds ago.
    at com.mysql.cj.jdbc.exceptions.SQLError.createCommunicationsException(SQLError.java:174) ~[mysql-connector-java-8.0.16.jar!/:8.0.16]
    at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:64) ~[mysql-connector-java-8.0.16.jar!/:8.0.16]
    at com.mysql.cj.jdbc.ServerPreparedStatement.serverExecute(ServerPreparedStatement.java:634) ~[mysql-connector-java-8.0.16.jar!/:8.0.16]
    at com.mysql.cj.jdbc.ServerPreparedStatement.executeInternal(ServerPreparedStatement.java:414) ~[mysql-connector-java-8.0.16.jar!/:8.0.16]
    at com.mysql.cj.jdbc.ClientPreparedStatement.executeQuery(ClientPreparedStatement.java:1005) ~[mysql-connector-java-8.0.16.jar!/:8.0.16]
    at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeQuery(ProxyPreparedStatement.java:52) ~[HikariCP-2.7.9.jar!/:na]
    at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeQuery(HikariProxyPreparedStatement.java) ~[HikariCP-2.7.9.jar!/:na]
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:60) ~[hibernate-core-5.2.17.Final.jar!/:5.2.17.Final]
    ... 149 common frames omitted

看到這樣的日志帐偎,大致分析思路是:

  1. 是不是連接失活后依然嘗試重新連接導(dǎo)致的
  2. 其他一些超時時間,設(shè)置的連接時間太短
  3. 數(shù)據(jù)庫壓力太大
  4. 網(wǎng)絡(luò)抖動

逐一排查

  • 連接失活

這是比較常見的原因御雕,由于MySQL的 wait_timeout 小于應(yīng)用中連接池設(shè)置的連接存活時間矢沿,導(dǎo)致MySQL已經(jīng)將連接關(guān)閉回收了,而連接池中的連接仍然存在酸纲,當(dāng)應(yīng)用獲取到這個連接進(jìn)行數(shù)據(jù)庫操作時捣鲸,就會拋出類似上述的異常。

查看MySQL 的wait_timeout

show VARIABLES like '%timeout'

結(jié)果如下

connect_timeout 10
delayed_insert_timeout  300
have_statement_timeout  YES
innodb_flush_log_at_timeout 1
innodb_lock_wait_timeout    10
innodb_rollback_on_timeout  OFF
interactive_timeout 28800
lock_wait_timeout   31536000
net_read_timeout    30
net_write_timeout   60
rpl_semi_sync_master_timeout    10000
rpl_stop_slave_timeout  31536000
slave_net_timeout   60
wait_timeout    28800

發(fā)現(xiàn)wait_timeout 值已經(jīng)設(shè)置為28800 (8小時)闽坡, 而我的Hikari使用的是默認(rèn)配置栽惶,在com.zaxxer.hikari.HikariConfig 查看

public class HikariConfig implements HikariConfigMXBean
{
   ...
   private static final long CONNECTION_TIMEOUT = SECONDS.toMillis(30);
   private static final long VALIDATION_TIMEOUT = SECONDS.toMillis(5);
   private static final long IDLE_TIMEOUT = MINUTES.toMillis(10);
   private static final long MAX_LIFETIME = MINUTES.toMillis(30);
   private static final int DEFAULT_POOL_SIZE = 10;
   ...
}

可以看到默認(rèn)的存活時間 (MAX_LIFETIME)是30分鐘,小于8小時疾嗅,所有并不是連接失活導(dǎo)致的異常媒役。

  • 其他超時時間分析

其實Hikari 設(shè)置的默認(rèn)時間一般還是比較合理的,比如連接超時時長30s宪迟,驗證連接有效性的超時時長是5s, 連接閑置超時時長是10min, 感覺也沒啥問題,應(yīng)用方面暫時沒有出路交惯,看看數(shù)據(jù)庫服務(wù)側(cè)是不是有突破口次泽。

  • 數(shù)據(jù)庫壓力太大

服務(wù)側(cè)首先想到就是會不會連接不夠用了,于是回頭看了下應(yīng)用側(cè)應(yīng)用連接池配置的連接數(shù)席爽,

spring:
    ...
    datasource
        hikari:
            minimum-idle: 10
            maximum-pool-size: 100

應(yīng)用已經(jīng)配置了最大連接池為100意荤,在應(yīng)用日志中并沒有發(fā)現(xiàn)獲取連接超時等問題,服務(wù)端的最大連接數(shù)配置為3200只锻, 而發(fā)生異常的這段時間監(jiān)控MySQL的最大連接數(shù)還不過百玖像,也說明了連接池是完全夠用的。

似乎看到了曙光

這時候小伙伴告訴我 百度到有通過 在應(yīng)用添加 connection-test-query解決了問題齐饮。

spring:
    ...
    datasource
        hikari:
            minimum-idle: 10
            maximum-pool-size: 100
            connection-test-query: SELECT 1

我們嘗試也在應(yīng)用添加這個配置捐寥,不幸的是異常出現(xiàn)的頻率降低了笤昨,但仍然會出現(xiàn)!這個配置的作用是在連接池將連接交給jpa使用前校驗一下連接的可用性握恳,根據(jù)這個表現(xiàn)當(dāng)時已經(jīng)有了一些猜測瞒窒,可能是網(wǎng)絡(luò)抖動引起的。

為啥猜想是網(wǎng)絡(luò)抖動
connection-test-query 的使用: 在 com.zaxxer.hikari.pool.PoolBase.java 中

boolean isConnectionAlive(final Connection connection)
{
           ... 代碼省略
           // 設(shè)置校驗超時時間
           setNetworkTimeout(connection, validationTimeout);
           // isUseJdbc4Validation = config.getConnectionTestQuery() == null, 
           // 即如果沒配置connection-test-query, 直接使用connection.isValid驗證
           if (isUseJdbc4Validation) {
              return connection.isValid(validationSeconds);
           }
           ... 代碼省略
           // 執(zhí)行查詢語句進(jìn)行校驗
           statement.execute(config.getConnectionTestQuery());
           ... 代碼省略
  }

當(dāng)JPA 獲取連接時會調(diào)用com.zaxxer.hikari.pool.HikariPool.java的getConnection方法

public Connection getConnection(final long hardTimeout) throws SQLException {
       ... 代碼省略
       // 1. 被標(biāo)記為evicted, 關(guān)閉連接
       // 2. 如果上次訪問時間到現(xiàn)在超過 ALIVE_BYPASS_WINDOW_MS乡洼,需要檢測連接是否可用
       if (poolEntry.isMarkedEvicted() 
           || (elapsedMillis(poolEntry.lastAccessed, now) > ALIVE_BYPASS_WINDOW_MS
           && !isConnectionAlive(poolEntry.connection))) {
           closeConnection(poolEntry, poolEntry.isMarkedEvicted() ? >EVICTED_CONNECTION_MESSAGE : DEAD_CONNECTION_MESSAGE);
           timeout = hardTimeout - elapsedMillis(startTime);
       }
       ... 代碼省略
}

從代碼片段可以看出崇裁,獲取連接時會去判斷 上次訪問時間到現(xiàn)在的時間,如果這個時間小于ALIVE_BYPASS_WINDOW_MS(常量默認(rèn)值是500ms)束昵,則不會檢測連接有效性拔稳。假設(shè)是因為網(wǎng)絡(luò)抖動導(dǎo)致的異常,在頻繁操作數(shù)據(jù)庫時锹雏,JPA可能會在500ms內(nèi)多次拿取同一個連接巴比,假設(shè)第一次獲取連接時網(wǎng)絡(luò)正常,所以可以正常執(zhí)行數(shù)據(jù)庫操作逼侦,而當(dāng)?shù)诙文萌∵B接時:

  • 如果在500ms以內(nèi)匿辩,則不會校驗有效性直接給JPA使用
  • 如果在500ms外但沒有配置connection-test-query,也不會進(jìn)行請求校驗榛丢,這個過程中只要網(wǎng)絡(luò)發(fā)生抖動铲球,都可能造成jpa執(zhí)行失敗
  • 如果配置了connection-test-query,因為500ms以外每次都會先連接數(shù)據(jù)庫校驗,一定程度上可以減少報錯的頻率。
    基于以上原因爸黄,推測本次異澄拖拢可能是網(wǎng)絡(luò)不穩(wěn)定導(dǎo)致;

雙向驗證

在驗證connection-test-query過程中该编,也排查了下數(shù)據(jù)庫日志,發(fā)現(xiàn)了大量的“Got an error reading communication packet” 警告,順騰摸瓜芍瑞,看到了Aborted clients或者Aborted connects的值

show status like 'Abort%';
----------------------------------------
Aborted_clients 17803532
Aborted_connects    20
----------------------------------------

發(fā)現(xiàn) 大量 Aborted_clients, 應(yīng)用程序使用連接池管理褐墅,也沒有出現(xiàn)程序死機等情況拆檬,可以排查是未正確關(guān)閉連接導(dǎo)致的,超時時間之前已經(jīng)確認(rèn)過妥凳,也可以排出竟贯,那么最可能就是網(wǎng)絡(luò)抖動導(dǎo)致客戶端意外中斷,兩相驗證逝钥,基本可以確定網(wǎng)絡(luò)問題屑那。

Aborted Connect
嘗試連接到MySQL服務(wù)器失敗的次數(shù),增長原因:

  • 沒有權(quán)限訪問數(shù)據(jù)庫;
  • 密碼錯誤持际;
  • 連接的數(shù)據(jù)包不合法沃琅;
  • 超過連接時間限制,connect_timeout控制(mysql默認(rèn)是10s,一般不會超時)

Aborted Clients
客戶端沒有正確關(guān)閉連接而中止的連接數(shù);

  • 客戶端程序在退出之前未正確關(guān)閉MySQL連接;
  • 客戶端休眠的時間超過了系統(tǒng)變量wait_timeout和interactive_timeout的值选酗,導(dǎo)致連接被MySQL進(jìn)程終止;
  • 客戶端程序在數(shù)據(jù)傳輸過程中突然結(jié)束;

問題解決

確定原因基本就解決了90%阵难,最后咨詢相關(guān)同事發(fā)現(xiàn)昨天恰好夜里公司DNS更新,為了防止類似問題再出現(xiàn)芒填,直接使用MySQL集群的VIP進(jìn)行訪問呜叫,目前沒有再出現(xiàn)問題。

總結(jié):感慨一下殿衰,程序員的時間都去哪了朱庆,不知道你可有領(lǐng)悟,哈哈哈C葡椤S榧铡!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末凯砍,一起剝皮案震驚了整個濱河市箱硕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌悟衩,老刑警劉巖剧罩,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異座泳,居然都是意外死亡惠昔,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進(jìn)店門挑势,熙熙樓的掌柜王于貴愁眉苦臉地迎上來镇防,“玉大人,你說我怎么就攤上這事潮饱±囱酰” “怎么了?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵香拉,是天一觀的道長饲漾。 經(jīng)常有香客問我,道長缕溉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任吃型,我火速辦了婚禮证鸥,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己枉层,他們只是感情好泉褐,可當(dāng)我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鸟蜡,像睡著了一般膜赃。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上揉忘,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天跳座,我揣著相機與錄音,去河邊找鬼泣矛。 笑死疲眷,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的您朽。 我是一名探鬼主播狂丝,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼哗总!你這毒婦竟也來了几颜?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤讯屈,失蹤者是張志新(化名)和其女友劉穎蛋哭,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體耻煤,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡具壮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了哈蝇。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片棺妓。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖炮赦,靈堂內(nèi)的尸體忽然破棺而出怜跑,到底是詐尸還是另有隱情,我是刑警寧澤吠勘,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布性芬,位于F島的核電站,受9級特大地震影響剧防,放射性物質(zhì)發(fā)生泄漏植锉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一峭拘、第九天 我趴在偏房一處隱蔽的房頂上張望俊庇。 院中可真熱鬧狮暑,春花似錦、人聲如沸辉饱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽彭沼。三九已至缔逛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間姓惑,已是汗流浹背褐奴。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留挺益,地道東北人歉糜。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像望众,于是被迫代替她去往敵國和親匪补。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內(nèi)容