在浏览器关闭时关闭 Go 服务器

Shutting down a Go server on browser close

我用 Go 编写了一个小型支票簿分类帐作为在本地主机中运行的服务器,并打开默认浏览器到一个小型 Web 应用程序前端 (https://bitbucket.org/grkuntzmd/checks-and-balances)。

为了在浏览器选项卡关闭时自动关闭服务器,我让浏览器每隔几秒调用一次 "heartbeat" URL。如果心跳没有到达,服务器使用 (*Server) Shutdown 停止 运行。

有没有办法使用上下文做同样的事情(https://golang.org/pkg/context/)? It is my understanding from watching this JustForFunc 的插曲,如果客户端取消请求,传递给处理程序的上下文将得到通知。

与其经常发送 "heartbeat" 请求,不如利用 server-sent events.

服务器发送的事件是一种网络技术,浏览器发出 HTTP 请求并保持连接打开以从服务器接收事件。这可以通过在与事件源的连接关闭时关闭服务器来代替您对重复心跳请求的需要。

这是 Go 中的基本服务器实现:

package main

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

func main() {
    http.HandleFunc("/heartbeat", func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "text/event-stream")
        w.Header().Set("Cache-Control", "no-cache")
        w.Header().Set("Connection", "keep-alive")

        flusher, ok := w.(http.Flusher)
        if !ok {
            http.Error(w, "your browser doesn't support server-sent events")
            return
        }

        // Send a comment every second to prevent connection timeout.
        for {
            _, err := fmt.Fprint(w, ": ping")
            if err != nil {
                log.Fatal("client is gone, shutting down")
                return
            }
            flusher.Flush()
            time.Sleep(time.Second)
        }
    })
    fmt.Println(http.ListenAndServe(":1323", nil))
}

有关客户端指南,请参阅 using server-sent events

从页面打开一个 websocket 连接。在服务端,websocket连接关闭时退出:

func wsHandler(w http.ResponseWriter, r *http.Request) {
    conn, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Println(err)
        return
    }
    defer c.Close()

    // Shutdown on exit from handler
    defer server.Shutdown(context.Background())

    // Read  messages from client. NextReader returns an error
    // when the client shuts down.
    for {
        if _, _, err := c.NextReader(); err != nil {
            break
        }
    }
 }

将这行代码添加到客户端应用程序:

 var conn = new WebSocket("ws://" + document.location.host + "/ws");

确保 conn 具有页面的生命周期。

这有点类似于另一个答案中建议的 SSE 解决方案,但具有在更多浏览器中工作的优势。

不需要发送心跳,因为这是在本地主机上。