在許多情況下,應用程式延遲情況加重最終會導致5xx 伺服器錯誤。因為錯誤和延遲尖峰的根本原因可能相同,請套用下列策略來排解延遲問題:
縮小延遲問題的範圍
請提出下列問題,定義問題範圍:
- 這項問題會影響哪些應用程式、服務和版本?
- 這項問題會影響服務上的哪些特定端點?
- 這項異動會影響全球所有用戶端,還是特定用戶端子集?
- 事件的開始和結束時間為何?建議指定時區。
- 具體錯誤為何?
- 觀察到的延遲差異為何?這通常會以特定百分位數的增幅表示。舉例來說,第 90 百分位數的延遲時間增加了 2 秒。
- 您如何測量延遲時間?具體來說,您是在用戶端測量延遲時間,還是可透過 Cloud Logging 或 App Engine 服務基礎架構提供的 Cloud Monitoring 延遲時間資料查看?
- 您的服務有哪些依附元件?其中是否有任何元件發生事件?
- 您最近是否變更過程式碼、設定或工作負載,因而觸發這個問題?
服務可能會有自己的自訂監控和記錄功能,可用於進一步縮小問題範圍。定義問題範圍有助於找出可能根本原因,並決定後續的疑難排解步驟。
找出原因
判斷要求路徑中,最有可能導致延遲或錯誤的元件。要求路徑的主要元件如下:
用戶端 --> 網際網路 --> Google Front End (GFE) --> App Engine 服務基礎架構 --> 服務例項
如果上述資訊無法指出失敗原因,請在檢查服務執行個體的健康狀態和效能時,套用下列策略:
監控 App Engine 要求記錄。如果這些記錄中顯示 HTTP 狀態碼錯誤或延遲時間變長,問題可能出在執行服務的執行個體。
如果服務執行個體數量未根據流量層級擴充,執行個體可能會過載,導致錯誤和延遲時間增加。
如果在 Cloud Monitoring 中看到錯誤或延遲時間增加,問題可能出在負載平衡器的上游,該負載平衡器會記錄 App Engine 指標。在大多數情況下,這表示服務執行個體有問題。
如果監控指標顯示延遲時間增加或發生錯誤,但要求記錄中沒有相關資訊,表示負載平衡失敗,或是發生嚴重執行個體故障,導致負載平衡器無法轉送要求。如要區分這些情況,請查看事件開始前的要求記錄。如果要求記錄顯示延遲時間在失敗前不斷增加,表示應用程式執行個體在負載平衡器停止將要求轉送至這些執行個體前,就已開始失敗。
疑難排解
本節說明如何排解要求路徑中下列元件的延遲時間偏高問題:
網際網路
連線品質不佳或頻寬較低,可能會導致應用程式發生延遲問題。
網路連線品質不佳
如要判斷問題是否為網路連線不佳,請在用戶端執行下列指令:
$ curl -s -o /dev/null -w '%{time_connect}\n' <hostname>
time_connect 的值代表用戶端連線到最近 Google Front End 的延遲時間。如果連線速度緩慢,請使用 traceroute 進一步排解問題,判斷網路上的哪個躍點導致延遲。
從不同地理位置的用戶端執行測試。App Engine 會自動將要求轉送至最接近的 Google 資料中心,具體取決於用戶端所在位置。
頻寬偏低
應用程式的回應速度可能很快,但網路瓶頸會導致 App Engine 服務基礎架構無法快速透過網路傳送封包,進而延遲回應。
Google Front End (GFE)
如果路由設定不正確、HTTP/2 用戶端傳送平行要求,或是 SSL 連線終止,應用程式可能會發生延遲問題。
將用戶端 IP 對應至地理區域
Google 會根據用戶端在 DNS 查詢中使用的 IP 位址,將 App Engine 應用程式的主機名稱解析為最接近用戶端的 GFE。如果用戶端的 DNS 解析器未使用 EDNS0 通訊協定,Google 可能不會將用戶端要求轉送至最接近的 GFE。
HTTP/2 佇列前端封鎖
如果 HTTP/2 用戶端並行傳送多個要求,GFE 的隊首封鎖可能會導致延遲時間變長。如要解決這個問題,用戶端必須使用 QUIC 通訊協定。
自訂網域的安全資料傳輸層 (SSL) 終止
GFE 可能會終止 SSL 連線。如果您使用自訂網域而非 appspot.com網域,SSL 終止作業需要額外一躍。這可能會導致在部分區域執行的應用程式延遲。詳情請參閱「對應自訂網域」。
App Engine 服務基礎架構
服務範圍事件或自動調整規模可能會導致應用程式延遲時間增加。
全服務事件
如果服務發生嚴重問題,Google 會在「服務健康狀態」資訊主頁中發布詳細資料。不過,Google 會逐步推出更新,因此服務範圍的事件不太可能一次影響所有執行個體。
自動調度資源
下列自動調整規模情境可能會導致延遲時間變長或發生錯誤:
流量增加速度過快:App Engine 自動調度資源功能可能無法跟上流量增加的速度,導致執行個體暫時過載。通常,過載是電腦程式而非使用者產生流量時發生。如要解決這個問題,請限制產生流量的系統。
流量暴增:如果自動調度服務需要比平常更快速地擴充,但又不能影響延遲時間,流量暴增可能會導致延遲時間拉長。一般來說,使用者流量不會導致流量頻繁暴增。如果流量突然激增,請調查原因。如果批次系統以間隔執行,您或許可以平緩流量或使用不同的縮放設定。
自動調度器設定:自動調度器可根據服務的調度特性進行設定。在下列情況中,縮放參數可能會變得不理想:
如果設定過於積極,App Engine 標準環境的資源調度設定可能會導致延遲。如果在記錄中看到狀態碼為
500的伺服器回應,以及「Request was aborted after waiting too long to attempt to service your request」(等待嘗試處理要求的時間過長,因此要求已中止) 訊息,表示要求在等待閒置執行個體時,於待處理佇列中逾時。即使您已佈建足夠的執行個體,手動調度仍可能導致待處理時間增加。如果應用程式會處理使用者流量,建議您不要使用手動調整規模。手動調整資源配置更適合工作佇列等工作負載。
基本縮放功能會以延遲為代價,盡量降低成本。如果服務容易受到延遲影響,建議不要使用基本縮放。
App Engine 的預設資源調度設定可為大多數服務提供最佳延遲時間。如果仍看到待處理時間較長的要求,請指定執行個體數量下限。如果您調整縮放設定,盡量減少閒置執行個體來降低成本,一旦負載突然增加,延遲時間就可能大幅增加。
建議您先使用預設的縮放設定評估效能,然後在每次變更這些設定後,重新評估效能。
部署作業
部署後不久延遲時間就變長,表示您在遷移流量前,擴充作業的規模不夠大。較新的執行個體可能尚未預熱本機快取,因此服務速度會比舊執行個體慢。
為避免延遲時間突然變長,請勿使用與現有服務版本相同的版本名稱,部署 App Engine 服務。如果沿用現有版本名稱,就無法將流量緩慢遷移至新版本。由於 App Engine 會在短時間內重新啟動每個執行個體,因此要求可能會變慢。如要還原至先前版本,也必須重新部署。
應用程式執行個體
本節說明可套用至應用程式例項和原始碼的常見策略,以最佳化效能並減少延遲。
應用程式程式碼
應用程式程式碼中的問題可能難以偵錯,尤其是間歇性或無法重現的問題。
如要解決問題,請按照下列步驟操作:
為診斷問題,建議您使用記錄、監控和追蹤功能,為應用程式加入檢測功能。您也可以使用 Cloud Profiler。
嘗試在本機開發環境中重現問題,您或許就能執行語言專屬的偵錯工具,這類工具可能無法在 App Engine 中執行。
如要進一步瞭解應用程式的失敗原因和瓶頸,請對應用程式執行負載測試,直到發生失敗為止。設定執行個體數量上限,然後逐步增加負載,直到應用程式發生故障為止。
如果延遲問題與應用程式程式碼新版本的部署作業有關,請還原至舊版,判斷是否為新版本導致事件發生。不過,如果您持續部署,頻繁的部署作業會導致難以根據事件發生時間,判斷事件是否由部署作業所致。
您的應用程式可能會將設定儲存在 Datastore 或其他位置。建立設定變更的時間軸,判斷是否有任何變更與延遲時間增加的開始時間一致。
工作負載變更
工作負載變更可能會導致延遲時間增加。部分監控指標會顯示工作負載變化,包括 qps、API 使用量和延遲時間。同時檢查要求和回應大小的變化。
記憶體壓力
如果監控結果顯示記憶體用量呈現鋸齒狀模式,或是記憶體用量下降與部署作業相關,則效能問題可能是由記憶體洩漏所致。記憶體流失也可能導致垃圾收集作業頻繁執行,進而造成延遲時間變長。如果無法將這個問題追溯到程式碼中的問題,請嘗試佈建記憶體較大的執行個體。
資源洩漏
如果應用程式例項的延遲時間增加,且與例項存續時間有關,則可能表示有資源洩漏,導致效能問題。部署作業完成後,延遲時間就會縮短。舉例來說,如果資料結構因 CPU 使用率提高而逐漸變慢,任何受 CPU 限制的工作負載都可能變慢。
程式碼最佳化
如要縮短 App Engine 的延遲時間,請使用下列方法最佳化程式碼:
離線工作:使用 Cloud Tasks,避免使用者要求阻礙應用程式等待工作完成 (例如傳送郵件)。
非同步 API 呼叫:請確保程式碼不會遭到封鎖,等待 API 呼叫完成。
批次 API 呼叫:批次版本的 API 呼叫通常比傳送個別呼叫更快。
資料模型去正規化:將資料模型去正規化,減少對資料持續層發出的呼叫延遲。
應用程式依附元件
監控應用程式的依附元件,偵測延遲尖峰是否與依附元件故障有關。
工作負載變更和流量增加可能會導致依附元件的延遲時間增加。
非縮放依附元件
如果應用程式的依附元件不會隨著 App Engine 執行個體數量增加而擴充,依附元件可能會在流量增加時過載。SQL 資料庫就是可能無法擴充的依附元件。應用程式執行個體數量越多,資料庫連線數量就越多,這可能會導致資料庫無法啟動,進而引發連鎖故障。如要解決這個問題,請按照下列步驟操作:
- 部署不連線至資料庫的新預設版本。
- 關閉先前的預設版本。
- 部署可連線至資料庫的新非預設版本。
- 慢慢將流量轉送至新版本。
為防患未然,請設計應用程式,使用自動調節功能捨棄對依附元件的要求。
快取層故障
如要加快要求速度,請使用多個快取層,例如邊緣快取、Memcache 和執行個體內記憶體。其中一個快取層發生故障,可能會導致延遲時間突然增加。舉例來說,Memcache 清除作業可能會導致更多要求傳送至速度較慢的 Datastore。