使用 WebSocket

本頁面提供指南和最佳做法,說明如何在 Cloud Run 執行 WebSockets 或其他串流服務,以及如何為這類服務編寫用戶端。

Cloud Run 支援 WebSockets 應用程式,不需額外設定。不過,WebSocket 串流是 HTTP 要求,仍受 Cloud Run 服務設定的要求逾時限制,因此您需要執行下列操作:

  • 將要求逾時時間長度增加到您希望保持 WebSockets 串流開啟的時間長度上限,例如 60 分鐘。
  • 確認用戶端可以重新連線。
  • 建議您為用戶端使用工作階段相依性,盡可能重新連線至同一個執行個體。
  • 請勿啟用「HTTP/2 end-to-end」(HTTP/2 端對端)。

雖然 Cloud Run 的工作階段相依性會盡可能提供相依性,但由於內建負載平衡功能,新的 WebSocket 要求仍可能連線至不同的執行個體。如要解決這個問題,您必須同步處理執行個體之間的資料。

請注意,如果您使用 Cloud Load Balancing,Cloud Run 也支援 WebSocket。

部署範例 WebSockets 服務

使用 Cloud Shell 快速部署範例白板服務,該服務會搭配 Cloud Run 使用 WebSocket:部署範例

或者,如果您想手動部署該範例白板服務:

  1. 使用 git 指令列工具,在本機複製 Socket.IO 存放區:

    git clone https://github.com/socketio/socket.io.git
    
  2. 前往範例目錄:

    cd socket.io/examples/whiteboard/
    
  3. 使用 Google Cloud CLI,從原始碼建構服務,部署新的 Cloud Run 服務:

    gcloud run deploy whiteboard --allow-unauthenticated --source=.
    
  4. 服務部署完成後,請開啟兩個不同的瀏覽器分頁,並前往服務網址。您在其中一個分頁中繪製的任何內容,都應傳播到另一個分頁 (反之亦然),因為用戶端透過 WebSocket 連線至同一個執行個體。

WebSockets 即時通訊範例完整教學課程

如需完整的程式碼逐步操作說明,請參閱「為 Cloud Run 建構 WebSocket 聊天服務教學課程」主題,其中提供其他程式碼範例。

最佳做法

在 Cloud Run 上建立 WebSockets 服務時,最困難的部分是在多個 Cloud Run 執行個體之間同步處理資料。由於執行個體具有自動調度資源和無狀態的特性,且並行和要求逾時有限制,因此這項操作並不容易。

處理要求逾時和用戶端重新連線

在 Cloud Run 中,WebSocket 要求會視為長時間執行的 HTTP 要求。即使應用程式伺服器未強制執行任何逾時,這些要求仍會受到要求逾時的限制 (目前最長為 60 分鐘,預設為 5 分鐘)。

因此,如果用戶端保持連線的時間超過 Cloud Run 服務設定的必要逾時時間,系統會在要求逾時時中斷連線。

因此,如果要求逾時或伺服器中斷連線,連線至 Cloud Run 的 WebSockets 用戶端應處理重新連線至伺服器的作業。在瀏覽器用戶端中,您可以使用 reconnecting-websocket 等程式庫,或是在使用 SocketIO 程式庫時處理「中斷連線」事件,來達到這個目的。

使用 WebSocket 時產生的費用

只要 Cloud Run 執行個體有「任何」開啟的 WebSocket 連線,就會視為有效,因此系統會分配 CPU,並依執行個體計費。

盡可能提高並行作業數量

WebSocket 服務通常設計為同時處理多個連線。Cloud Run 支援並行連線 (每個容器最多 1,000 個),如果服務能以指定資源處理負載,建議您將容器的最大並行設定提高至預設值以上。

關於黏性工作階段 (工作階段相依性)

由於 WebSocket 連線具有狀態,因此在連線的生命週期內,用戶端會持續連線至 Cloud Run 上的同一個容器。這自然會在單一 WebSocket 連線的背景中提供工作階段黏性。

如有多個後續 WebSocket 連線,您可以將 Cloud Run 服務設為使用工作階段相依性,但這會提供盡力相依性,因此 WebSocket 要求仍可能最終會傳送至不同的執行個體。連線至 Cloud Run 服務的用戶端,最終可能會由不協調或不共用資料的不同執行個體提供服務。

如要解決這個問題,您必須使用外部資料儲存庫,在 Cloud Run 執行個體之間同步處理狀態,詳情請參閱下一節。

在執行個體之間同步處理資料

您必須同步處理資料,確保連線至 Cloud Run 服務的用戶端,能從 WebSockets 連線接收相同資料。

舉例來說,假設您使用 WebSocket 建構聊天室服務,並將並行處理數量上限設為 1,000。如果同時有超過 1,000 位使用者連線至這項服務,系統會透過不同的執行個體提供服務,因此他們無法在聊天室中看到相同的訊息。

如要在 Cloud Run 執行個體之間同步處理資料,例如從所有執行個體接收發布至聊天室的訊息,您需要外部資料儲存系統,例如資料庫或訊息佇列。

如果您使用 Cloud SQL 等外部資料庫,可以將訊息傳送至資料庫,並定期從資料庫輪詢。不過,請注意,當容器未處理任何要求時,Cloud Run 執行個體不會有 CPU。如果服務主要處理 WebSockets 要求,只要至少有一個用戶端連線至容器,容器就會分配到 CPU。

訊息佇列更適合用於即時同步 Cloud Run 容器之間的資料,因為外部訊息佇列無法將資料「推送」至各個執行個體。服務必須與訊息佇列建立連線,才能從訊息佇列「提取」新訊息。

Google 建議您使用外部訊息佇列系統,例如 Redis Pub/Sub (Memorystore) 或 Firestore 即時更新,透過容器執行個體啟動的連線,將更新傳送至所有執行個體。

使用 Redis Pub/Sub

WebSockets 聊天室服務架構

您可以從 Memorystore 建立 Redis 執行個體,使用 Redis Pub/Sub 機制。如果您使用 Socket.IO 程式庫處理 WebSocket,可以使用其 redis 轉接器。

在這個以 Redis 為基礎的架構中,每個 Cloud Run 執行個體都會與包含已接收訊息的 Redis 管道建立長時間執行的連線 (使用 SUBSCRIBE 指令)。容器執行個體在管道上收到新訊息後,可以透過 WebSocket 即時將訊息傳送給用戶端。

同樣地,當用戶端使用 WebSocket 發出訊息時,接收訊息的執行個體會將訊息發布至 Redis 管道 (使用 PUBLISH 指令),而訂閱這個管道的其他執行個體則會收到這則訊息。

如需完整的程式碼逐步操作說明,請參閱「為 Cloud Run 建構 WebSocket 聊天服務教學課程」主題,其中提供其他程式碼範例。