如何在Golang中释放WebSocket和Redis网关服务器资源?

时间:2019-04-12 07:06:36

标签: go websocket redis

我有一个网关服务器,它可以通过使用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>

2 个答案:

答案 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)

实际上,我的设计架构错误,我将解释我想做什么。

  1. 客户端可以连接到我的websocket服务器;

  2. 服务器具有多个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)
  1. 是时候显示与该问题相关的核心代码。我要订阅一个频道,名称为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)
            }
        }
      }()
  1. 最后一件事是管理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()