合理設定網關逾時時間是保障專屬網關穩定性和提升使用者體驗的關鍵。不當的設定可能導致串連被意外重設、請求堆積甚至服務雪崩。本文將介紹專屬網關中兩種核心的逾時類型——“空閑連線逾時”與“請求逾時”,提供涵蓋 Java、Go 及阿里雲 EAS SDK 的用戶端配置建議與程式碼範例,並分析常見逾時問題的排查方法,旨在協助您構建更穩定、高效的服務。
背景資訊
為什麼需要設定逾時時間?
在分布式系統中,服務之間的調用鏈路可能很長,任意環節的延遲或故障都可能導致整個請求鏈的卡頓甚至崩潰。逾時機制正是為瞭解決這類問題而設計,通過設定合理的逾時時間,可以:
防止資源耗盡:避免用戶端或網關長時間等待無響應的後端服務,導致資源(如串連、線程)被佔用,最終引起系統雪崩。
提升使用者體驗:快速失敗優於長時間無響應,讓使用者能及時收到錯誤反饋並選擇重試或採取其他措施。
保障系統穩定性:及時釋放資源,防止慢請求或故障服務耗盡系統資源,從而保護整體系統的穩定性。
設定空閑連線逾時時間
空閑連線逾時(Idle Connection Timeout)是指在一段時間內沒有資料轉送的串連會被關閉。這對於管理串連池、釋放不活躍資源至關重要。
配置建議
專屬網關配置了以下固定的空閑連線逾時時間,不支援修改:
網關作為服務端(面向用戶端):固定為
600秒。用戶端與網關的串連若空閑超過此時間,將被網關關閉。網關作為用戶端(面向模型服務):固定為
30秒。網關與後端模型服務的串連若空閑超過此時間,將被網關關閉。
為確保由用戶端主動管理和關閉串連,避免在網關側關閉串連後,用戶端因不知情而使用失效串連帶來問題,強烈建議用戶端的空閑連線逾時應小於專屬網關的空閑連線逾時時間(600秒)。
鏈路如圖所示:
用戶端配置樣本
以下是針對不同程式設計語言管理或設定用戶端空閑連線逾時時間的樣本。
樣本僅作為參數設定的參考說明,不能直接應用於業務系統。參數設定需充分考慮業務系統的流量和負載情況,以及使用的用戶端版本特性等。
Java
如果使用Apache HttpClient 4.x:Apache HttpClient的連線管理員通過周期性地調用 closeIdleConnections() 方法來管理空閑串連。您需要啟動一個單獨的線程來執行此操作。
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.client.config.RequestConfig;
import java.util.concurrent.TimeUnit;
public class HttpClientIdleTimeout {
public static void main(String[] args) throws InterruptedException {
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setDefaultMaxPerRoute(20); // 舉例值:最大路由串連數
cm.setMaxTotal(100); // 舉例值:最大總串連數
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(5000) // 舉例值:連線逾時 5秒
.setSocketTimeout(10000) // 舉例值:讀取資料逾時 10秒
.build();
try (CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(cm)
.setDefaultRequestConfig(requestConfig)
.build()) {
// 啟動一個後台線程,每隔一段時間清理空閑串連
Thread cleanerThread = new Thread(() -> {
try {
while (!Thread.currentThread().isInterrupted()) {
Thread.sleep(5000); // 舉例值:每5秒檢查一次
// 關閉空閑時間超過 500 秒的串連 (小於網關空閑逾時時間600秒)
cm.closeIdleConnections(500, TimeUnit.SECONDS);
// 關閉到期串連(例如被服務端關閉的串連)
cm.closeExpiredConnections();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 重新設定中斷狀態
}
});
cleanerThread.setDaemon(true); // 設定為守護線程,隨主線程退出而退出
cleanerThread.start();
// 執行HTTP請求...
// 例如:httpClient.execute(new HttpGet("http://your-gateway-url"));
// 類比程式運行一段時間
Thread.sleep(60000); // 舉例值:運行1分鐘
// 停止清理線程 (在實際應用中,您需要在應用關閉時優雅地停止它)
cleanerThread.interrupt();
} catch (Exception e) {
e.printStackTrace();
}
}
}Go
Go語言的net/http庫通過Transport結構體提供了對串連池的細粒度控制。請確保IdleConnTimeout值小於600秒。
package main
import (
"net/http"
"time"
)
func main() {
// 建立一個自訂的Transport
tr := &http.Transport{
MaxIdleConns: 100, // 最大空閑串連數
IdleConnTimeout: 500 * time.Second, // 空閑連線逾時時間,例如500秒 (小於600秒)
DisableKeepAlives: false, // 啟用Keep-Alive
}
}EAS SDK(Java)
對於阿里雲EAS SDK,您可以在用戶端設定是否開啟清理空閑串連、以及設定空閑連線逾時時間。
import com.aliyun.openservices.eas.predict.http.PredictClient;
import com.aliyun.openservices.eas.predict.http.HttpConfig;
public class EasSdkTimeoutJava {
public static void main(String[] args) {
// 1. 全域用戶端配置
HttpConfig httpConfig = new HttpConfig();
// 開啟清理空閑串連開關,單位毫秒,建議根據用戶端情況設定
httpConfig.setConnectionCleanupInterval(5000);
// 設定空閑連線逾時時間為500秒,小於網關的空閑連線逾時時間600秒
httpConfig.setIdleConnectionTimeout(500000);
PredictClient client = new PredictClient(httpConfig);
client.setEndpoint("your-eas-service-endpoint");
client.setModelName("your-model-name");
// client.setToken("your-token"); // 如果需要認證
...
}
}佈建要求逾時時間
請求逾時(Response Timeout)是指從TCP串連建立、發送請求到接收完整響應的整個過程允許的最大時間,是用戶端最常用的逾時設定。
配置建議
請求逾時時間,應根據實際業務需求進行調整,以下建議僅供參考,不作為強制性指導。在實際生產環境中,需要綜合考慮容錯性、即時性以及網路抖動等因素,找到適合業務情境的平衡點。
專屬網關請求逾時配置:根據其商務邏輯和預期的處理時間來設定合理的逾時時間。
預設值:10分鐘(600秒)。
使用者自訂設定(應用型專屬網關ALB暫不支援): 在服務組態檔中,通過
metadata.rpc.keepalive欄位來為特定服務自訂網關的請求逾時時間。網關會讀取此值作為該請求的逾時時間。配置方法請參考metadata參數說明。重要短串連同時受請求逾時和空閑連線逾時約束。由於專屬網關空閑串連的逾時時間固定為600秒,佈建要求逾時時間大於600秒不起作用。 對於超過10分鐘的長耗時情境,推薦採用以下方案:
串流(Streaming):大檔案下載、AI內容產生。
WebSocket:即時雙向通訊情境。
用戶端請求逾時配置:為避免用戶端已逾時而服務端仍在處理請求產生的虛假逾時錯誤,用戶端請求逾時時間可以略大於服務端的請求逾時時間。
通常建議
用戶端逾時 = 服務端逾時 + 少量緩衝時間(例如1-5秒)。其中,緩衝時間用於覆蓋網路延遲和服務端處理時間的輕微波動。
鏈路如圖所示:
用戶端配置樣本
以下是針對不同程式設計語言的用戶端請求逾時配置樣本。
樣本僅作為參數設定的參考說明,不能直接應用於業務系統。參數設定需充分考慮業務系統的流量和負載情況,以及使用的用戶端版本特性等。
Java
使用 Apache HttpClient 4.x:
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
public class ApacheHttpClientTimeout {
public static void main(String[] args) {
// 建議用戶端請求逾時略大於等於網關請求逾時時間(如果是預設的600秒),這裡可以是610秒
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(5000) // 舉例值:連線逾時,單位毫秒
.setSocketTimeout(610000) // 資料轉送逾時 (讀取逾時),單位毫秒 (610秒)
.build();
}
}Go
Go語言的net/http庫提供了多種佈建要求逾時的方式,最常見的是在http.Client上設定Timeout屬性,或者使用context.WithTimeout為單個請求設定逾時。
package main
import (
"context"
"fmt"
"io"
"net/http"
"time"
)
func main() {
// 建議用戶端請求逾時略大於等於網關請求逾時時間(如果是預設的600秒),這裡可以是610秒
client := &http.Client{
Timeout: 610 * time.Second, // 整個請求的逾時時間
}
req, err := http.NewRequest("GET", "http://your-gateway-url", nil)
if err != nil {
fmt.Println("Error creating request:", err)
return
}
// 也可以為單個請求設定更短的逾時
ctx, cancel := context.WithTimeout(req.Context(), 610*time.Second) // 610秒
defer cancel()
req = req.WithContext(ctx)
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error sending request:", err)
// 檢查是否是逾時錯誤
if t, ok := err.(interface{ Timeout() bool }); ok && t.Timeout() {
fmt.Println("Request timed out!")
}
return
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error reading response body:", err)
return
}
fmt.Printf("Response Status: %s\n", resp.Status)
fmt.Printf("Response Body: %s\n", body)
}EAS SDK(Java)
對於阿里雲EAS SDK ,您可以在用戶端全域配置或請求層級配置中分別設定連線逾時和讀取逾時。
import com.aliyun.openservices.eas.predict.http.PredictClient;
import com.aliyun.openservices.eas.predict.http.HttpConfig;
public class EasSdkTimeoutJava {
public static void main(String[] args) {
// 1. 全域用戶端配置
HttpConfig httpConfig = new HttpConfig();
// 連線逾時
httpConfig.setConnectTimeout(5);
// 建議用戶端請求逾時略大於等於網關請求逾時時間(如果是預設的600秒),這裡可以是610秒
httpConfig.setReadTimeout(610); // 讀取逾時
}
}常見問題
空閑連線逾時設定不當
情境一:用戶端空閑連線逾時時間 > 專屬網關作為服務端空閑連線逾時時間(即用戶端空閑連線逾時時間 > 600秒)
問題描述:用戶端認為串連有效,但專屬網關已因空閑時間過長而關閉該串連。當用戶端再次使用此串連時,請求失敗。
用戶端常見報錯:
Connection reset by peerBroken pipejava.net.SocketException: Connection reset(Java)requests.exceptions.ConnectionError: ('Connection aborted.', ConnectionResetError(104, 'Connection reset by peer'))(Python requests)read: connection reset by peer(Go)用戶端可能會收到
HTTP 503 Service Unavailable錯誤,這通常發生在用戶端嘗試重用已關閉的串連,而網關無法及時建立新串連或將請求轉寄到後端服務。
情境二:專屬網關作為用戶端空閑連線逾時時間 > 模型服務空閑連線逾時時間(即模型服務空閑連線逾時時間 < 30秒)
問題描述:專屬網關認為串連仍然有效,但後端模型服務已因空閑時間過長主動關閉該串連。當網關再次使用此串連將請求轉寄給模型服務時,會發現串連已斷開。
專屬網關側可能報錯:網關日誌中會出現類似上述用戶端的串連重設錯誤。
用戶端側可能報錯:用戶端通常會收到專屬網關返回的
HTTP 503錯誤碼,表明網關無法將請求轉寄到後端服務。
請求逾時設定不當
情境一:用戶端請求逾時時間設定過短 (小於專屬網關請求逾時時間)
問題描述:用戶端設定的請求逾時時間過短。這會導致用戶端在請求尚未獲得完整響應時就主動中斷,從而產生大量的虛假逾時錯誤,影響業務的正常運行和使用者體驗。
重要在某些情境下,用戶端為實現快速失敗,會故意將用戶端請求逾時時間設定的較短,短於服務端請求逾時時間。因此,再次強調,用戶端請求逾時時間的設定值僅供參考,不同情境下可根據實際需求靈活調整,並非一定要大於服務端請求逾時時間。本文提供的建議主要針對常規情境,請根據具體業務情況酌情應用。
用戶端常見報錯:
用戶端會頻繁拋出其自身的逾時異常,例如:
java.net.http.HttpTimeoutException(JavaHttp Client)java.net.SocketTimeoutException: Read timed out(JavaApache HttpClient)requests.exceptions.ReadTimeout(Pythonrequests)context deadline exceeded(Gocontext.WithTimeout或Client.Timeout觸發)
情境二:專屬網關請求逾時時間 < 模型服務實際處理時間
問題描述:專屬網關為轉寄請求到模型服務設定的逾時時間(預設10分鐘或自訂值),短於模型服務實際處理該請求所需的時間。從而導致網關在模型服務完成處理之前就中斷等待,並向用戶端返回逾時錯誤。
用戶端收到錯誤:用戶端通常會收到來自專屬網關的
HTTP 504 Gateway Timeout錯誤,這表明網關在等待後端服務響應時發生了逾時。也可能是HTTP 502 Bad Gateway或HTTP 500 Internal Server Error,具體取決於模型服務如何處理其內部逾時並向網關返回錯誤。模型服務側狀態:模型服務可能仍在繼續處理該請求,其日誌中不會有逾時錯誤,但會發現請求被中斷(如果網關在逾時後關閉了到模型服務的串連)。
排查建議:當出現上述錯誤時,建議結合用戶端和網關的日誌進行排查。
檢查用戶端日誌:查看用戶端拋出的具體異常類型和堆棧資訊。
檢查專屬網關日誌:查看網關的請求日誌,尤其是與該請求相關的錯誤碼和轉寄狀態。
檢查模型服務日誌:如果網關返回
5xx錯誤碼,請進一步檢查模型服務的日誌,確認是否存在內部錯誤或處理時間過長導致的逾時。