go - 在处理程序 slice 中注册 http URL 处理程序

标签 go goroutine

目标为broadcast message从一个 goroutine 到多个 http URL 处理程序,我尝试注册这些 http URL 处理程序,在 main.go 中使用以下代码:

type webSocketHandler func(http.ResponseWriter, *http.Request)

type threadSafeSlice struct {
    sync.Mutex
    handlers []*webSocketHandler
}

var sliceOfHandlers threadSafeSlice

func (slice *threadSafeSlice) push(handle *webSocketHandler) { //register

    slice.Lock()
    defer slice.Unlock()

    slice.handlers = append(slice.handlers, handle)
}

其中forWardMsgToClient()是需要注册的http URL处理程序,

broadCastMessage() goroutine 可以将消息广播到多个 forWardMsgToClient() 处理程序,代码如下:

func main() {

    go broadcastMessage()
    http.HandleFunc("/websocket", forwardMsgToClient)
    http.ListenAndServe(":3000", nil)

}

func forwardMsgToClient(w http.ResponseWriter, r *http.Request) {
    conn, err := upgrader.Upgrade(w, r, nil)
    for {
         // Forward message to the client upon receiving a msg from publisher
    }
}

以上代码均在main.go

但是问题是,在调用rw, e := l.Accept()之后,会为各个客户端生成goroutine forwardMsgToClient()../go/src/net/http/server.go.

注册(push()) http URL处理函数(forwardMsgToClient())的原因是为了使broadcastMessage() goroutine 知道要为所有 http URL 处理程序创建的 channel 数,并在取消注册 http URL 处理程序函数时删除该 channel (forwardMsgToClient())。


有点紧张,如果我们需要修改/go/src/net/http/server.go来实现这个目标


如何在 sliceOfHandlers.handlers 中注册(push())http URL 处理函数 forwardMsgToClient()

最佳答案

要将消息广播到所有连接的 websocket 客户端,请执行以下操作:

  • 在升级时将连接添加到集合。
  • 连接关闭时从集合中删除连接。
  • 通过迭代集合进行广播。

一个简单的方法是:

type Clients struct {
    sync.Mutex
    m map[*websocket.Conn]struct{}
}

var clients = Clients{m: map[*websocket.Conn]struct{}{}}

func (cs *Clients) add(c *websocket.Conn) {
    cs.Lock()
    cs.m[c] = struct{}{}
    cs.Unlock()
}

func (cs *Clients) remove(c *websocket.Conn) {
    cs.Lock()
    delete(cs.m, c)
    cs.Unlock()
}

func (cs *Clients) broadcast(message []byte) {
    cs.Lock()
    defer cs.Unlock()
    for c, _ := range m {
       c.WriteMessage(websocket.TextMessage, message)
    }
}

处理程序从集合中添加和删除连接,如下所示:

func forwardMsgToClient(w http.ResponseWriter, r *http.Request) {
    conn, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        // handle error
    }
    defer c.Close()
    clients.add(c)
    defer clients.remove(c)

    // Read the connection as required by the package.
    for {
        if _, _, err := c.NextReader(); err != nil {
            break
        }
    }
}

要向所有连接的客户端发送消息,请调用clients.broadcast(message)

由于以下几个原因,这种简单的方法尚未准备好用于生产:它不处理从 WriteMessage 返回的错误,广播可能会在卡住的客户端上阻塞。

有关更强大的解决方案,请参阅 Gorilla chat example hub 。集线器在广播器和连接之间插入一条 channel ,从而允许集线器无阻塞地进行广播。问题中的 go BroadcastMessage() 对应于 Gorilla 示例中的 go hub.run()。问题中的 forwardMsgToClient 处理程序将创建一个 *client 并将其发送到集线器 register 升级 channel 并发送该 *client 到集线器取消注册 断开连接时的 channel 。 *client 有一个泵送到连接的 channel 。

关于go - 在处理程序 slice 中注册 http URL 处理程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55538429/

相关文章:

go - 我可以得到一些帮助来推理 `concurrent prime sieve` 示例吗?

go - gorouines 是否忽略 channel 的缓冲区大小

go - GET 页面请求错误

json - 包含接口(interface)列表的结构上的 UnmarshalJSON

go - 如何通过 golang 或 C# 以外的其他语言将 EWS 获取项目正文传输到 PST 文件

multithreading - 使用 WaitGroups 和 goroutines 优化多线程的正确方法?

go - 限制运行的例程数量

for-loop - 为什么我需要对我的 go 例程/ channel 的结果进行额外的 for 循环以显示所有结果?

go - 取消 go func()

dictionary - 将数组类型的映射键转换为2D slice