golang中如何释放websocket和redis网关服务器资源?

     2023-02-16     170

关键词:

【中文标题】golang中如何释放websocket和redis网关服务器资源?【英文标题】:How to release a websocket and redis gateway server resource in golang? 【发布时间】:2019-04-12 07:06:36 【问题描述】:

我有一个网关服务器,它可以使用 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.PubSubConnConn: 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 &lt;PID&gt;?

【问题讨论】:

How to stop one of multilpe of the same goroutine的可能重复 看起来您正在使用conn := pool.Get() 建立连接?如果您多次运行go h.DeliverMessage(pool),那么我猜您可能会收到“达到最大连接数/客户端数”错误。或许你应该先conn := pool.Get() 看看是否有可用的连接(如果没有,可能会阻塞?),然后将连接传递给 goroutine 而不是整个池。 你可以看到我的代码有conn := pool.Get() defer conn.Close(),如果我没有得到一个池作为参数,它会在我交付后断开连接.. 【参考方案1】:

看例子here

一旦您没有收到更多信息,您可以通过在 DeliverMessage 的 switch case 中添加一个 return 语句来退出 goroutine。我猜case error,或者如示例中所见,case 0 你想从那里返回,你的 goroutine 将取消。或者,如果我对事情有误解,并且 case client := &lt;-h.Unregister: 在 DeliverMessage 中,请返回。

您还关闭了两次连接。 defer gPubSubConn.Close() 只需调用 conn.Close() 所以你不需要 defer conn.Close()

还可以查看the Pool 并查看所有参数的实际作用。如果要处理许多连接,请将 MaxActive 设置为 0 “当为零时,池中的连接数没有限制。” (你真的想要空闲超时吗?)

【讨论】:

【参考方案2】:

其实我弄错了设计架构,我来解释一下我想做什么。

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

    服务器有多个http的handler,admin可以通过handler发布数据,数据的结构可以是:

    
       "cid": "something",
       "body": 
        
    
    

因为,我有几个节点正在运行来为我们的客户端提供服务,Nginx 可以将来自admin 的每个请求分派到完全不同的节点,但是只有一个节点通过“某事”保持与cid 的连接,所以我需要把这个数据发布到Redis,如果有任何节点得到了数据,它就会把这个消息发送到客户端。

3.寻找 NodeID,我将通过给定 cid 将其转到 Publish

// redis code & golang
NodeID, err := conn.Do("HGET", "NODE_MAP", cid)

4.现在,我可以从admin发布任何消息,并发布到NodeID,这是我们在第3步得到的。

// 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()
    

【讨论】:

Golang websocket 处理程序

】Golangwebsocket处理程序【英文标题】:Golangwebsockethandler【发布时间】:2016-04-1005:48:35【问题描述】:在用Node.js编写了很长时间后,我开始学习Golang,我有点好奇如何实现处理程序-我选择使用GorillaWebsocket,因为我知道它是最可... 查看详情

发送连接升级后,如何在golang中将客户端http连接升级到websockets

...发送连接升级后,如何在golang中将客户端http连接升级到websockets【英文标题】:HowcanIupgradeaclienthttpconnectiontowebsocketsingolangaftersendingtheconnectionupgrade【发布时间】:2019-04-0513:24:10【问题描述】:我需要一个可以从httpget响应升级到web... 查看详情

golang如何连接redis---2022-04-03

参考技术A下面介绍golang如何连接redis服务端。1.golang连接redis通过例子,我们知道主要通过Options配置redis的连接参数,下面对Options参数进行详细说明。提示:go-redis包自带了连接池,会自动维护redis连接,因此创建一次client即可,... 查看详情

如何手动释放servicestack.redis连接池连接

参考技术ARedis是一个非常NB的内存级的数据库,我们可以把很多”热数据“(即读写非常多的数据)放入其中来操作,这样就减少了和关系型数据库(如SqlServer/MySql等)之间的交互,程序的响应速度也大大提升。C#利用ServiceStack.Redis... 查看详情

如何扩展 Node.js WebSocket Redis 服务器?

】如何扩展Node.jsWebSocketRedis服务器?【英文标题】:HowtoScaleNode.jsWebSocketRedisServer?【发布时间】:2012-10-1207:40:17【问题描述】:我正在为Acani编写聊天服务器,我对Scalingnode.js和websocketswithloadbalancerscalability有一些疑问。负载均衡No... 查看详情

SQLite 3 没有在 Golang 中释放内存

】SQLite3没有在Golang中释放内存【英文标题】:SQLite3notreleasingmemoryinGolang【发布时间】:2014-12-1421:08:18【问题描述】:我在让Go与SQLite很好地配合使用时遇到了问题。我过去修复它没有问题,但已经有一段时间了,我不记得我做了... 查看详情

如何在Golang beego框架中将数据从MySQL数据库保存到redis?

】如何在Golangbeego框架中将数据从MySQL数据库保存到redis?【英文标题】:HowtosavedatatoredisfromMySQLdatabaseinGolangbeegoframework?【发布时间】:2021-12-3112:25:27【问题描述】:我正在尝试从MySQL数据库中获取数据,并在每6小时后将其存储在... 查看详情

带有 uWSGI 本机异步 websockets 和 redis 的错误文件描述符

】带有uWSGI本机异步websockets和redis的错误文件描述符【英文标题】:BadFiledescriptorwithuWSGInativeasyncwebsocketsandredis【发布时间】:2014-02-2816:12:19【问题描述】:您好,我有一个简单的websocket服务器,它正在向客户端推送消息,代码如... 查看详情

golang 读取了许多 websockets

】golang读取了许多websockets【英文标题】:golangreadnumerouswebsockets【发布时间】:2016-03-1714:04:17【问题描述】:在过去的几周里,我一直在StackOverflow附近寻找与阅读大量websocket相关的信息。基本上,我有许多主机都通过websocket发出... 查看详情

Webflux,使用Websocket如何防止订阅两次反应式redis消息操作

】Webflux,使用Websocket如何防止订阅两次反应式redis消息操作【英文标题】:Webflux,withWebsockethowtopreventsubscribingtwiceofreactiveredismessagingoperation【发布时间】:2021-12-3104:31:50【问题描述】:我在webflux上有一个使用redis消息传递操作的we... 查看详情

使用 localStorage 和 Websocket 进行自动授权

】使用localStorage和Websocket进行自动授权【英文标题】:AutoAuthorizationusinglocalStorageandWebsocket【发布时间】:2018-03-2200:14:06【问题描述】:我正在开发一个项目,它是一个单页动态Web应用程序,它使用Javascript和Websocket从Golang服务器... 查看详情

easyrtc编译中golang将http请求升级为websocket实现过程分享(代码片段)

在EasyRTC视频通话项目的编译中,部分操作采用websocket连接减少发送请求,增加实时性。一般情况下,会添加新端口的方式,在端口上做websocket服务。但是为了减少端口的使用,还有一种可以直接将http请求升级... 查看详情

golang的websocket(代码片段)

查看详情

如何从golang go-redis中的redis.Cmder获取价值?

】如何从golanggo-redis中的redis.Cmder获取价值?【英文标题】:Howtogetgetvaluefromredis.Cmderingolanggo-redis?【发布时间】:2021-09-1310:56:13【问题描述】:temp1Ctx,temp1Cancer:=lib.GetTimeoutCtx(ctx)pipeline:=util.RedisClusterClient.Pipeline()for_,key: 查看详情

golang中内存管理分配(代码片段)

...数操作的对象一般都是指针。而对于高级语言(Java、Golang、Python等),程序员在编写代码时,则无需关系内存的申请和释放 查看详情

基于redis发布订阅和websocket实现聊天室功能

参考技术A同时在做消息的持久化的时候,可以利用Redis的Zset的特性来对历史消息进行存储。 查看详情

如何在 docker 容器中下载 golang 和 node

】如何在docker容器中下载golang和node【英文标题】:Howtodownloadgolangandnodeindockercontainer【发布时间】:2022-01-1414:48:02【问题描述】:我正在构建一个简单的节点服务器以在docker中运行。我介绍了一个golang的小模块,可以通过运行来... 查看详情

通过 HTTP 中间件验证 WebSocket 连接 - Golang

】通过HTTP中间件验证WebSocket连接-Golang【英文标题】:AuthenticatingWebSocketConnectionsviaHTTPMiddleware-Golang【发布时间】:2018-10-1422:47:41【问题描述】:问题陈述:我正在尝试使用Golang中的基本中间件来保护websocket升级程序http端点,例... 查看详情