跳至內容

使用 Golang 在 Knative 上建構 WebSocket 應用程式

發佈於:2024-09-11

使用 Golang 在 Knative 上建構 WebSocket 應用程式

作者:Matthias Weßendorf,Red Hat 資深首席軟體工程師

Knative Serving 支援多種協定來執行無伺服器工作負載,例如 HTTPHTTP/2gRPCWebSocketWebSocket 協定允許客戶端應用程式和伺服器之間透過單一 TCP 連線進行全雙工通訊。 WebSocket 協定以 HTTP「升級」請求開始,然後將連線從 HTTP 切換到 WebSocket 協定,允許對等點之間進行即時雙向通訊。 在本文中,我們將展示如何使用 Knative 建構可擴展的 WebSocket 伺服器。

注意

此處討論的範例應用程式可以在Knative 文件儲存庫中找到。

HTTP 升級

由於每個 WebSocket 應用程式都從 HTTP 升級請求開始,我們需要定義一個基於 Golang 的 Web 伺服器來處理傳統的 HTTP 請求。 對於 WebSocket 實作,我們使用 gorilla/websocket 套件,它為 Golang 提供了一個快速、經過良好測試且廣泛使用的 WebSocket 實作。 以下是我們範例應用程式的 HTTP 部分

package main

import (
    "fmt"
    "log"
    "net/http"

    "github.com/gorilla/websocket"
    "github.com/knative/docs/code-samples/serving/websockets-go/pkg/handlers"
)

var upgrader = websocket.Upgrader{
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
    CheckOrigin:     func(r *http.Request) bool { return true },
}

func handleWebSocket(w http.ResponseWriter, r *http.Request) {
    conn, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Printf("Error upgrading to websocket: %v", err)
        return
    }
    handlers.OnOpen(conn)

    go func() {
        defer handlers.OnClose(conn)
        for {
            messageType, message, err := conn.ReadMessage()
            if err != nil {
                handlers.OnError(conn, err)
                break
            }
            handlers.OnMessage(conn, messageType, message)
        }
    }()
}

func main() {
    http.HandleFunc("/ws", handleWebSocket)
    fmt.Println("Starting server on :8080...")
    if err := http.ListenAndServe(":8080", nil); err != nil {
        log.Fatalf("Server error: %v", err)
    }
}

main 函式在 /ws 上下文中啟動 Web 伺服器,並註冊 handleWebSocket 處理函式

func main() {
    http.HandleFunc("/ws", handleWebSocket)
    fmt.Println("Starting server on :8080...")
    if err := http.ListenAndServe(":8080", nil); err != nil {
        log.Fatalf("Server error: %v", err)
    }
}

handleWebSocket 執行協定升級並分配各種 websocket 處理函式,例如 OnOpenOnMessage

func handleWebSocket(w http.ResponseWriter, r *http.Request) {
    conn, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Printf("Error upgrading to websocket: %v", err)
        return
    }
    handlers.OnOpen(conn)

    go func() {
        defer handlers.OnClose(conn)
        for {
            messageType, message, err := conn.ReadMessage()
            if err != nil {
                handlers.OnError(conn, err)
                break
            }
            handlers.OnMessage(conn, messageType, message)
        }
    }()
}

注意

為了更清楚地分離關注點,不同的 On... 處理程式位於單獨的 handlers 套件中。

WebSocket 處理程式

WebSocket 應用程式邏輯位於 pkg/handlers/handlers.go 檔案中,其中包含每個 WebSocket 事件的回呼

func OnOpen(conn *websocket.Conn) {
    log.Printf("WebSocket connection opened: %v", conn.RemoteAddr())
}

func OnMessage(conn *websocket.Conn, messageType int, message []byte) {
    log.Printf("Received message from %v: %s", conn.RemoteAddr(), string(message))

    if err := conn.WriteMessage(messageType, message); err != nil {
        log.Printf("Error sending message: %v", err)
    }
}

func OnClose(conn *websocket.Conn) {
    log.Printf("WebSocket connection closed: %v", conn.RemoteAddr())
    conn.Close()
}

func OnError(conn *websocket.Conn, err error) {
    log.Printf("WebSocket error from %v: %v", conn.RemoteAddr(), err)
}

在每個處理程式中,我們記錄已連線對等點的遠端位址,並且透過 OnMessage 處理程式記錄任何傳入訊息,我們會將其回顯給發送者。

測試 WebSocket 伺服器

注意

建構和部署 Golang 應用程式的最簡單方法是使用 ko,如實際的範例應用程式中所述。

一旦 Knative Serving 應用程式部署到 Kubernetes 叢集中,您就可以對其進行測試。為此,您需要取得 WebSocket 應用程式服務的實際 URL

kubectl get ksvc
NAME               URL                                                 LATESTCREATED            LATESTREADY              READY   REASON
websocket-server   http://websocket-server.default.svc.cluster.local   websocket-server-00001   websocket-server-00001   True

現在我們需要一個客戶端來連線到我們的服務。 為此,我們在 Kubernetes 內部啟動一個 Linux 容器,並執行 wscat 以連線到我們的 WebSocket 伺服器應用程式

kubectl run --rm -i --tty wscat --image=monotykamary/wscat --restart=Never -- -c ws://websocket-server.default.svc.cluster.local/ws

之後,您可以像這樣與 WebSocket 伺服器聊天

```If you don't see a command prompt, try pressing enter.
```connected (press CTRL+C to quit)
```> Hello
```< Hello
```>

上述內容會擴展到正好一個 Pod,因為只有一個客戶端連線。 由於 Knative Serving 允許您動態擴展,因此一定數量的並行連線會導致一定數量的 Pod。

以下是在對其執行大量並行請求時,擴展應用程式的範例

k get pods 
NAME                                                READY   STATUS    RESTARTS   AGE
websocket-server-00001-deployment-f785cbd65-42mrn   2/2     Running   0          16s
websocket-server-00001-deployment-f785cbd65-76bjr   2/2     Running   0          14s
websocket-server-00001-deployment-f785cbd65-98cwb   2/2     Running   0          18s
websocket-server-00001-deployment-f785cbd65-9fdbl   2/2     Running   0          18s
websocket-server-00001-deployment-f785cbd65-bpvjj   2/2     Running   0          20s
websocket-server-00001-deployment-f785cbd65-c6f47   2/2     Running   0          20s
websocket-server-00001-deployment-f785cbd65-fl6kk   2/2     Running   0          20s
websocket-server-00001-deployment-f785cbd65-gnffw   2/2     Running   0          20s
websocket-server-00001-deployment-f785cbd65-hqpfx   2/2     Running   0          20s
websocket-server-00001-deployment-f785cbd65-j4v9d   2/2     Running   0          18s
websocket-server-00001-deployment-f785cbd65-j72vk   2/2     Running   0          18s
websocket-server-00001-deployment-f785cbd65-k856w   2/2     Running   0          20s
websocket-server-00001-deployment-f785cbd65-kmmng   2/2     Running   0          20s
websocket-server-00001-deployment-f785cbd65-l4f2v   2/2     Running   0          20s
websocket-server-00001-deployment-f785cbd65-lpfr9   2/2     Running   0          14s
websocket-server-00001-deployment-f785cbd65-mn26w   2/2     Running   0          16s
websocket-server-00001-deployment-f785cbd65-mr98h   2/2     Running   0          18s
websocket-server-00001-deployment-f785cbd65-prjmj   2/2     Running   0          20s
websocket-server-00001-deployment-f785cbd65-v696v   2/2     Running   0          18s
websocket-server-00001-deployment-f785cbd65-ws5k9   2/2     Running   0          20s
websocket-server-00001-deployment-f785cbd65-xmszx   2/2     Running   0          18s
websocket-server-00001-deployment-f785cbd65-znhrr   2/2     Running   0          20s

注意

根據您擁有的目標註解(autoscaling.knative.dev/target),您可以根據連線數進行擴展。

我們使用分析和 Cookie 來了解網站流量。關於您使用我們網站的資訊會因此目的與 Google 共享。 了解更多。