問題追查:QA壓測工具http長鏈接老是被服務端close狀況

1. 背景

最近QA對線上單模塊進行壓測(非全鏈路壓測),http客戶端 與 thrift服務端的tcp連接總在一段時間被close。
查看服務端日誌顯示 i/o timeout.
最後的結果是: qps過小, 長時間不發送請求, server主動關閉socketredis

2. 查看thrift框架原代碼

thrift框架處理請求僞代碼以下:服務器

//服務端listen
for {
    // 服務端接收一個tcp鏈接請求
    client := server.accept()

    // 獨立啓動一個goroutine處理client後續請求
    go fun () {
        inputTrans := xxx(client)
        outputTrans := xxx(client)
        if inputTrans != nil {  //return 時, 關閉讀
            defer   inputTrans.close()   
        }
        if outputTrans != nil {
            defer   outputTrans.close() //return時, 關閉寫
        }
        
        //獨立goroutine處理寫結果 (使用channel同步)
        go func(
            for {
                //超時,或者其餘錯誤, return
            }
        ) ()

        //for循環: 服務讀取請求 (兩個for循環使用channel同步)
        for {
            //超時,或者其餘錯誤, return
        }
    }()
}

能夠看出,框架

  • thrift框架並無使用像epollo那樣的io多路複用,而是爲每一個client單獨啓一個goroutine進行處理
  • tcp時雙通道, 上面代碼中讀、寫分別處理,使用channel進行同步
  • 遇到client 超時或者錯誤,server會主動關閉tcp鏈接 (使用SetDeadline,SetReadDeadline,SetWriteDeadline設置超時時間)

3. 可能的兩種超時狀況

3.1 server寫超時

壓測工具,只負責發送請求,通常不讀取請求。
若是使用socket直接連服務器thrift端口,只send數據,不recv數據。可能使得server發送緩衝區滿,發送數據超時。socket

3.2 server 讀超時

client qps過小,發送請求時間間隔大於server設置的讀超時時間。tcp

4. 總結

這裏不是長鏈接、短鏈接的問題,是server服務器端的一種自我保護機制。
服務器用於接收大量的client請求,若是存在大量的無用鏈接不只會佔用服務器資源,更嚴重的狀況會使得其餘client沒法鏈接服務器的狀況。
能夠使用其餘方法解決:工具

  • client和server自定義心跳協議。如secret的發送no-op命令
  • 好比redis client, 若是鏈接斷開能夠進行重發一次(先鏈接,再發)
相關文章
相關標籤/搜索