首頁  >  文章  >  後端開發  >  Golang 伺服器傳送給每個使用者的事件

Golang 伺服器傳送給每個使用者的事件

PHPz
PHPz轉載
2024-02-06 11:39:141063瀏覽

Golang 服务器向每个用户发送的事件

問題內容

我使用 Go 已經有一段時間了,但之前從未使用過 SSE。我遇到了一個問題,有人可以提供一個僅發送給特定用戶(連接)的伺服器發送事件的工作範例嗎?

我正在使用 gorilla - 會話進行身份驗證,並且我想使用 UserID 來分隔連接。

或者我應該透過 Ajax 使用 5 秒輪詢?

非常感謝

這是我發現並嘗試過的:

  1. https://gist.github.com/ismasan/3fb75381cd2deb6bfa9c 它不會發送給個人用戶,如果連接關閉,go func 也不會停止

  2. https://github.com/striversity/gotr/blob/master/010-server-sent-event-part-2/main.go 這正是我所需要的,但一旦連線被刪除。所以現在,一旦您在私人視窗中關閉並打開瀏覽器,它就根本無法工作。另外,如上所述,Go 例程繼續進行。


正確答案


建立一個「代理」來將訊息分發給連接的使用者:

type broker struct {
    // users is a map where the key is the user id
    // and the value is a slice of channels to connections
    // for that user id
    users map[string][]chan []byte

    // actions is a channel of functions to call
    // in the broker's goroutine. the broker executes
    // everything in that single goroutine to avoid
    // data races.
    actions chan func()
}

// run executes in a goroutine. it simply gets and 
// calls functions.
func (b *broker) run() {
    for a := range b.actions {
        a()
    }
}

func newbroker() *broker {
    b := &broker{
        users:   make(map[string][]chan []byte),
        actions: make(chan func()),
    }
    go b.run()
    return b
}

// adduserchan adds a channel for user with given id.
func (b *broker) adduserchan(id string, ch chan []byte) {
    b.actions <- func() {
        b.users[id] = append(b.users[id], ch)
    }
}

// removeuserchan removes a channel for a user with the given id.
func (b *broker) removeuserchan(id string, ch chan []byte) {
    // the broker may be trying to send to 
    // ch, but nothing is receiving. pump ch
    // to prevent broker from getting stuck.
    go func() { for range ch {} }()

    b.actions <- func() {
        chs := b.users[id]
        i := 0
        for _, c := range chs {
            if c != ch {
                chs[i] = c
                i = i + 1
            }
        }
        if i == 0 {
            delete(b.users, id)
        } else {
            b.users[id] = chs[:i]
        }
        // close channel to break loop at beginning
        // of removeuserchan.
        // this must be done in broker goroutine
        // to ensure that broker does not send to
        // closed goroutine.
        close(ch)
    }
}

// sendtouser sends a message to all channels for the given user id.
func (b *broker) sendtouser(id string, data []byte) {
    b.actions <- func() {
        for _, ch := range b.users[id] {
            ch <- data
        }
    }
}

在套件層級使用代理宣告變數:

var broker = newbroker()

使用代理程式編寫 sse 端點:

func sseEndpoint(w http.ResponseWriter, r *http.Request) {
    // I assume that user id is in query string for this example,
    // You should use your authentication code to get the id.
    id := r.FormValue("id")

    // Do the usual SSE setup.
    flusher := w.(http.Flusher)
    w.Header().Set("Content-Type", "text/event-stream")
    w.Header().Set("Cache-Control", "no-cache")
    w.Header().Set("Connection", "keep-alive")

    // Create channel to receive messages for this connection.  
    // Register that channel with the broker.
    // On return from the function, remove the channel
    // from the broker.
    ch := make(chan []byte)
    broker.addUserChan(id, ch)
    defer broker.removeUserChan(id, ch)
    for {
        select {
        case <-r.Context().Done():
            // User closed the connection. We are out of here.
            return
        case m := <-ch:
            // We got a message. Do the usual SSE stuff.
            fmt.Fprintf(w, "data: %s\n\n", m)
            flusher.Flush()
        }
    }
}

向您的應用程式添加程式碼以呼叫 broker.sendtouser。

以上是Golang 伺服器傳送給每個使用者的事件的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:stackoverflow.com。如有侵權,請聯絡admin@php.cn刪除