我有一个网关服务器,它可以通过使用websocket将消息推送到客户端,一个新的客户端连接到我的服务器,我将为其生成一个cid
。然后,我还订阅了一个频道,该频道使用cid
。如果有任何消息发布到该频道,则我的服务器会将其推送到客户端。目前,所有单元都工作正常,但是当我尝试使用thor进行基准测试时,它将崩溃,我认为DeliverMessage
存在一些问题,它将永远不会退出,因为它有一个死循环。但是由于redis需要订阅某些内容,因此我不知道如何避免循环。
func (h *Hub) DeliverMessage(pool *redis.Pool) {
conn := pool.Get()
defer conn.Close()
var gPubSubConn *redis.PubSubConn
gPubSubConn = &redis.PubSubConn{Conn: conn}
defer gPubSubConn.Close()
for {
switch v := gPubSubConn.Receive().(type) {
case redis.Message:
// fmt.Printf("Channel=%q | Data=%s\n", v.Channel, string(v.Data))
h.Push(string(v.Data))
case redis.Subscription:
fmt.Printf("Subscription message: %s : %s %d\n", v.Channel, v.Kind, v.Count)
case error:
fmt.Println("Error pub/sub, delivery has stopped", v)
panic("Error pub/sub")
}
}
}
在主函数中,我将上述函数称为:
go h.DeliverMessage(pool)
但是当我通过巨大的连接对其进行测试时,它会给我一些错误,例如:
ERR达到的最大客户数量
因此,我通过更改MaxIdle
来更改Redis池的大小:
func newPool(addr string) *redis.Pool {
return &redis.Pool{
MaxIdle: 5000,
IdleTimeout: 240 * time.Second,
Dial: func() (redis.Conn, error) { return redis.Dial("tcp", addr) },
}
}
但是它仍然不起作用,所以我想知道,在以下选择中,当我的websocket断开与服务器的连接后,是否有任何好的方法可以杀死goroutine:
case client := <-h.Unregister:
if _, ok := h.Clients[client]; ok {
delete(h.Clients, client)
delete(h.Connections, client.CID)
close(client.Send)
if err := gPubSubConn.Unsubscribe(client.CID); err != nil {
panic(err)
}
// TODO kill subscribe goroutine if don't client-side disconnected ...
}
但是我如何识别该goroutine?我该如何像unix
那样做。 kill -9 <PID>
?
答案 0 :(得分:1)
看示例here
一旦您没有收到更多消息,就可以通过在switch案例中的DeliverMessage中包含return语句来使goroutine退出。我猜是case error
,或者如示例中所示,case 0
您要从中返回,您的goroutine将取消。或者,如果我误会了东西,并且case client := <-h.Unregister:
位于DeliverMessage内部,请返回。
您还将关闭两次连接。 defer gPubSubConn.Close()
只需调用conn.Close(),因此您不需要defer conn.Close()
还要查看the Pool并查看所有参数实际执行的操作。如果要处理许多连接,请将MaxActive设置为0“零时,池中的连接数没有限制”。 (您实际上是否想要空闲超时?)
答案 1 :(得分:0)
实际上,我的设计架构错误,我将解释我想做什么。
客户端可以连接到我的websocket服务器;
服务器具有多个http处理程序,管理员可以通过该处理程序发布数据,数据结构如下:
{
"cid": "something",
"body": {
}
}
自那以来,我有几个节点正在运行,以服务于我们的客户端,并且Nginx可以将来自
admin
的每个请求分派到完全不同的Node,但是只有一个Node拥有与cid
的连接“某物”,因此我将需要将此数据发布到Redis
,如果有任何Node获得数据,它将把此消息发送到客户端。
3。寻找NodeID,我将通过指定cid转到Publish
。
// redis code & golang
NodeID, err := conn.Do("HGET", "NODE_MAP", cid)
4。现在,我可以发布来自admin
的任何消息,并发布到我们在步骤3获得的NodeID
。
// redis code & golang
NodeID, err := conn.Do("PUBLISH", NodeID, data)
是时候显示与该问题相关的核心代码。我要订阅一个频道,名称为NodeID。如下所示。
go func(){
for {
switch v := gPubSubConn.Receive().(type) {
case redis.Message:
fmt.Println("Got a message", v.Data)
h.Broadcast <- v.Data
pipeline <- v.Data
case error:
panic(v)
}
}
}()
6。要管理Websocket,您还需要一个goroutine来执行此操作。就像下面这样:
go func () {
for {
select {
case client := <-h.Register:
h.Clients[client] = true
cid := client.CID
h.Connections[cid] = client
body := "something"
client.Send <- msg // greeting
case client := <-h.Unregister:
if _, ok := h.Clients[client]; ok {
delete(h.Clients, client)
delete(h.Connections, client.CID)
close(client.Send)
}
case message := <-h.Broadcast:
fmt.Println("message is", message)
}
}
}()
最后一件事是管理Redis池,您现在实际上并不需要连接池。因为我们只有两个goroutine
,一个主要过程。
func newPool(addr string) *redis.Pool {
return &redis.Pool{
MaxIdle: 100,
IdleTimeout: 240 * time.Second,
Dial: func() (redis.Conn, error) { return redis.Dial("tcp", addr) },
}
}
var (
pool *redis.Pool
redisServer = flag.String("redisServer", ":6379", "")
)
pool = newPool(*redisServer)
conn := pool.Get()
defer conn.Close()