golang websocket如何在浏览器意外关闭时收到通知

Pat*_*k Z 5 go websocket gorilla

我正在用 golang 编写一个 websocket 服务。该程序使用gollira websocket接受ws请求,并在每个请求处理程序中监听rabbitmq队列的消息。

问题是,当我关闭浏览器窗口时,处理程序线程仍在运行,我猜想有一种机制可以在连接断开时收到通知。

当我不工作时,我尝试收听频道 request.Context().Done() 。

package main

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

    "github.com/gorilla/websocket"
    "github.com/streadway/amqp"
)

var (
    addr     = "localhost:9999"
    upgrader = websocket.Upgrader{
        CheckOrigin: func(r *http.Request) bool { return true },
    }
)

var conn *amqp.Connection

func watch(w http.ResponseWriter, r *http.Request) {
    ns := r.URL.Query().Get("ns")
    if ns == "" {
        return
    }
    c, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Print("upgrade:", err)
        return
    }
    defer c.Close()

    ch, err := conn.Channel()
    failOnError(err, "Failed to open a channel")
    defer ch.Close()

    err = ch.ExchangeDeclare(
        "notify", // name
        "fanout",           // type
        true,               // durable
        false,              // auto-deleted
        false,              // internal
        false,              // no-wait
        nil,                // arguments
    )
    failOnError(err, "Failed to declare an exchange")

    q, err := ch.QueueDeclare(
        "",    // name
        false, // durable
        false, // delete when usused
        true,  // exclusive
        false, // no-wait
        nil,   // arguments
    )
    failOnError(err, "Failed to declare a queue")

    err = ch.QueueBind(
        q.Name,             // queue name
        ns,                 // routing key
        "dsm_tasks_notify", // exchange
        false,
        nil)
    failOnError(err, "Failed to bind a queue")

    msgs, err := ch.Consume(
        q.Name, // queue
        "",     // consumer
        true,   // auto-ack
        false,  // exclusive
        false,  // no-local
        false,  // no-wait
        nil,    // args
    )
    failOnError(err, "Failed to register a consumer")

    for {
        select {
        case d := <-msgs:
            err = c.WriteMessage(websocket.TextMessage, d.Body)
            if err != nil {
                log.Println("write:", err)
                break
            }
        case <-r.Context().Done():
            log.Println("Disconnect")
            return
        }
    }

}

func failOnError(err error, msg string) {
    if err != nil {
        log.Fatalf("%s: %s", msg, err)
        panic(fmt.Sprintf("%s: %s", msg, err))
    }
}

func main() {
    var err error
    conn, err = amqp.Dial("amqp://guest:guest@localhost:5672/")
    failOnError(err, "Failed to connect to RabbitMQ")
    defer conn.Close()

    http.HandleFunc("/watch", watch)
    log.Fatal(http.ListenAndServe(addr, nil))
}
Run Code Online (Sandbox Code Playgroud)

Cer*_*món 5

如果浏览器干净地关闭连接,则读取 websocket 连接会返回错误。像处理任何读取错误一样清理 websocket 连接。

应用程序必须 PING 连接并期望相应的 PONG 检测其他情况。聊天示例展示了如何发送 PING 和接收 PONG。