跳至内容

使用 Golang 在 Knative 上构建 WebSocket 应用

发布日期:2024-09-11

使用 Golang 在 Knative 上构建 WebSocket 应用

作者:Matthias Weßendorf,红帽高级首席软件工程师

Knative 服务支持多种协议来运行无服务器工作负载,例如 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 服务应用程序部署到 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 服务允许您进行动态扩展,因此一定数量的并发连接会导致一定数量的 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 共享,用于该目的。 了解更多。