目标为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/