首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Golang服务器向每个用户发送事件

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

Stack Overflow用户
提问于 2022-11-20 03:31:50
回答 1查看 49关注 0票数 1

我和Go一起工作已经有一段时间了,但以前从未做过SSE。我有一个问题,能不能有人提供一个工作的例子,服务器发送的事件,将只发送到特定的用户(连接)。

我正在使用大猩猩会话进行身份验证,我希望使用UserID来分离连接。

还是应该通过Ajax使用5秒轮询?

非常感谢

以下是我发现并尝试过的:

  1. https://gist.github.com/ismasan/3fb75381cd2deb6bfa9c发送给单个用户,如果连接被关闭,go func不会停止,

  1. https://github.com/striversity/gotr/blob/master/010-server-sent-event-part-2/main.go,这是我所需要的,但是一旦连接被移除,它就不会跟踪。所以现在,一旦您关闭并打开私有窗口中的浏览器,它就根本不工作了。同样,如前所述,go例程继续进行。
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-11-20 06:24:11

创建一个“代理”,将消息分发给连接的用户:

代码语言:javascript
复制
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
        }
    }
}

在包级别向代理声明一个变量:

代码语言:javascript
复制
 var broker = newBroker()

使用代理编写SSE端点:

代码语言:javascript
复制
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。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74505447

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档