我正在开发REST API服务器,当创建新资源或修改现有资源时,服务器的一项功能是能够通过websocket通知任意数量的客户端。
我有一个自定义操作路由器将URL绑定到一个函数和 gorillas 的websocket库的实现。对于IPC,我决定依赖于渠道,因为它似乎是 惯用方式在协同程序之间进行通信。它的行为就像一个我熟悉的概念管道。
函数Create
的原型如下所示:
func Create (res http.ResponseWriter, req *http.Request, userdata interface {}) (int, string, interface {})
作为userdata
传递结构PipeSet
的实例。它是一个在所有协程之间共享的映射,其中键是Pipe
的地址(指向的指针)并且值相同。这里的基本原理是在删除时加快查找过程。
type Pipe chan string
type PipeSet struct {
sync.Mutex
Pipes map [*Pipe] *Pipe
}
func NewPipe () Pipe {
return make (Pipe)
}
func NewPipeSet () PipeSet {
var newSet PipeSet
newSet.Pipes = make (map[*Pipe] *Pipe)
return newSet
}
func (o *PipeSet) AddPipe (pipe *Pipe) {
o.Lock ()
o.Pipes[pipe] = pipe
o.Unlock ()
}
func (o *PipeSet) ForeachPipe (f func (pipe Pipe)) {
o.Lock ()
for k := range (o.Pipes) {
f (*o.Pipes[k])
}
o.Unlock ()
}
func (o *PipeSet) DeletePipe (pipe *Pipe) {
o.Lock ()
delete (o.Pipes, pipe)
o.Unlock ()
}
当客户通过websocket连接时,会创建一个新频道(Pipe
)并将其添加到共享PipeSet
。然后,如果创建了新资源,则协程将通过整个PipeSet
向每个Pipe
发送消息。然后,该消息将转发到另一侧的已连接客户端。
我无法检测客户端的websocket连接是否仍然那里。我需要知道确定是否应从Pipe
中移除PipeSet
。在这种情况下,我依靠CloseNotifier
。它永远不会发射。
代码如下(摘录):
var upgrader = websocket.Upgrader {
CheckOrigin: func (r *http.Request) bool { return true },
}
conn, err := upgrader.Upgrade (res, req, nil)
if err != nil {
marker.MarkError (err)
return http.StatusBadRequest, "", nil
}
defer conn.Close ()
exitStatus = http.StatusOK
pipe := genstore.NewPipe ()
quit := res.(http.CloseNotifier).CloseNotify ()
genStore.WSChannels.AddPipe (&pipe)
for {
log.Printf ("waiting for a message")
select {
case wsMsg = <-pipe:
log.Printf ("got a message: %s (num pipes %d)", wsMsg, len (genStore.WSChannels.Pipes))
if err = conn.WriteMessage (websocket.TextMessage, []byte (wsMsg)); err != nil {
marker.MarkError (err)
goto egress
}
case <-quit:
log.Printf ("quit...")
goto egress
}
}
egress:
genStore.WSChannels.DeletePipe (&pipe)
答案 0 :(得分:3)
当您使用Gorilla将HTTP连接升级到WebSocket连接时,它会劫持该连接并且net / http服务器停止为其提供服务。这意味着,您不能依赖那一刻的网络/ http事件。
请检查:https://github.com/gorilla/websocket/issues/123
所以,你可以在这里做的是为每个新的WebSocket连接启动新的goroutine,它将从这个连接读取数据并在失败时向quit
通道写一条消息。