当前位置: 首页 > news >正文

[Go WebSocket] 多房间的聊天室(三)自动清理无人房间

我是HullQin,公众号线下聚会游戏的作者(欢迎关注公众号,发送加微信,交个朋友),转发本文前需获得作者HullQin授权。我独立开发了《联机桌游合集》,是个网页,可以很方便的跟朋友联机玩斗地主、五子棋等游戏,不收费没广告。还开发了《Dice Crush》参加Game Jam 2022。喜欢可以关注我 HullQin 噢~我有空了会分享做游戏的相关技术。

背景

在专栏《Go WebSocket》里,有一些前置文章:

第一篇文章:《为什么我选用Go重构Python版本的WebSocket服务?》,介绍了我的目标。

第二篇文章:《你的第一个Go WebSocket服务: echo server》,介绍了一下怎么写一个WebSocket server。

第三篇文章:《单房间的聊天室》,介绍了如何实现一个单房间的聊天室。

第四篇文章:《多房间的聊天室(一)思考篇》,介绍了实现一个多房间的聊天室的思路。

第五篇文章:《多房间的聊天室(二)代码实现》,介绍了实现一个多房间的聊天室的代码。

如果你没阅读上面的文章,一定要先看一下,因为这篇文章更复杂,如果你不弄懂上面几篇,这篇可能跟不上节奏噢。

上篇文章我们提到:

现在房间数只会源源不断的增多,house这个map会越来越大,终将造成内存不足,这不是一个好事情。

所以我们后续需要加一个优化:当最后一个客户端断开连接时,回收(删除)这个房间。

今天,我们实现它。

思路

有一个重要的问题需要想清楚:

是在哪个地方执行这个【回收】操作?是哪个goroutine?什么时机?若有多个地方,有没有竞争关系?

回顾一下之前绘制的图:

1.png

可以发现:每个客户端连接会常驻2个goroutine:Read和Write。其中Read重要的职责就是unregister,这点我之前在《单房间的聊天室》强调过。

unregister就是把客户端连接从hub中删除掉。这个时候,我们就可以检查一下hub内是否还有其它客户端,若无,则删除。

注意,unregister只是个channel,真正的处理逻辑是写在goroutine中的,是哪个gotoutine负责接收unregister并执行逻辑呢?就是Hub。所以我们需要修改Hub代码。

直接看源码

多房间聊天室案例代码的地址:github.com/HullQin/go-websocket-examples

chat-multi-rooms文件夹中,文章可配套commit记录阅读:

  • delete empty room 就是清理无人房间的逻辑。

开始开发

我们以《多房间的聊天室(二)代码实现》的代码为基础,做改动。

关注hub goroutine的代码:

func (h *Hub) run() {
   for {
      select {
      case client := <-h.register:
         h.clients[client] = true
      case client := <-h.unregister:
         if _, ok := h.clients[client]; ok {
            delete(h.clients, client)
            close(client.send)
         }
      case message := <-h.broadcast:
         for client := range h.clients {
            select {
            case client.send <- message:
            default:
               close(client.send)
               delete(h.clients, client)
            }
         }
      }
   }
}

可以看到case client := <-h.unregister:这段代码,就是处理unregister逻辑的。

这里删除了hub中的对应客户端。删除时,我们检查一下h.clients是否为空即可,若为空,把hubhouse(房间集合)删掉,再结束这个hub goroutine即可。

但是,有个问题,这里我们要在house中删掉,是需要知道key的,key是roomId,最好从hub的属性中获得,目前还不支持,所以还需要给hub增加一个roomId属性,方便做删除。

if len(h.clients) == 0 {
   delete(house, h.roomId)
   break
}

下面,我们增加roomId属性:

type Hub struct {
   // Identity of room.
   roomId string

   // Registered clients.
   clients map[*Client]bool

   // Inbound messages from the clients.
   broadcast chan []byte

   // Register requests from the clients.
   register chan *Client

   // Unregister requests from clients.
   unregister chan *Client
}

func newHub(roomId string) *Hub {
   return &Hub{
      roomId:     roomId,
      broadcast:  make(chan []byte),
      register:   make(chan *Client),
      unregister: make(chan *Client),
      clients:    make(map[*Client]bool),
   }
}

此外,还需要修改main.go,新建hub时,传入roomId

2.png

测试一下,大功告成!(可以在delete逻辑增加个日志输出)现在断开连接时,无人房间会自动清除掉!并且下次进入时,也会新建房间,不影响正常使用!

真的没问题了吗?

我又绘制了一个图(以一个房间为例),更加完整:

3.png

我用连线,表明了goroutine的启动关系:

  • User连接WebSocket服务器时,会先启动serveWs goroutine
  • serveWs goroutine中,会执行register操作,这一点之前的图中并没画出来。
  • 随后serveWs goroutine启动了Read goroutineWrite goroutine,并结束自己。

这里真的是完美方案不会出错吗?留个悬念,我们下篇文章,继续讲解。

写在最后

我是HullQin,公众号线下聚会游戏的作者(欢迎关注公众号,发送加微信,交个朋友),转发本文前需获得作者HullQin授权。我独立开发了《联机桌游合集》,是个网页,可以很方便的跟朋友联机玩斗地主、五子棋等游戏,不收费没广告。还开发了《Dice Crush》参加Game Jam 2022。喜欢可以关注我 HullQin 噢~我有空了会分享做游戏的相关技术。

相关文章:

  • linux进程管理-进程
  • 【图像透视】基于matlab图像逆透视映射【含Matlab源码 2139期】
  • 【Java】逻辑控制
  • Qt下生成pdb文件,并在exe崩溃时生成dmp文件,且由dmp查询崩溃原因
  • 蜂鸣器、风扇、震动马达
  • 【VRP问题】基于帝国企鹅优化算法求解冷链配送物流车辆调度优化研究
  • 3) 时频分析与傅立叶变换
  • stm32f4xx-I2C
  • 有了这个 Python 库,以后再也不用写正则表达式了
  • 学习python很无聊?看看这几个有意思的代码,拿去整蛊一下好朋友~ 适当娱乐哈
  • 【老生谈算法】matlab实现滤波器设计源码——滤波器设计
  • 后端研发工程师面经——手撕设计模式
  • 1679. K 和数对的最大数目-自定义哈希表解决
  • 【Objective-C内存管理之引用计数】
  • 找工作经验总结——嵌入式软件工程师必备的能力(表达篇)
  • Facebook AccountKit 接入的坑点
  • java中具有继承关系的类及其对象初始化顺序
  • Meteor的表单提交:Form
  • Netty 框架总结「ChannelHandler 及 EventLoop」
  • PyCharm搭建GO开发环境(GO语言学习第1课)
  • Vim 折腾记
  • 基于axios的vue插件,让http请求更简单
  • 计算机在识别图像时“看到”了什么?
  • 理解在java “”i=i++;”所发生的事情
  • 使用 QuickBI 搭建酷炫可视化分析
  • 使用agvtool更改app version/build
  • 使用Maven插件构建SpringBoot项目,生成Docker镜像push到DockerHub上
  • 验证码识别技术——15分钟带你突破各种复杂不定长验证码
  • 一起来学SpringBoot | 第三篇:SpringBoot日志配置
  • 由插件封装引出的一丢丢思考
  • 原创:新手布局福音!微信小程序使用flex的一些基础样式属性(一)
  • 再谈express与koa的对比
  • 在Docker Swarm上部署Apache Storm:第1部分
  • 函数计算新功能-----支持C#函数
  • 京东物流联手山西图灵打造智能供应链,让阅读更有趣 ...
  • ​secrets --- 生成管理密码的安全随机数​
  • ​卜东波研究员:高观点下的少儿计算思维
  • #define MODIFY_REG(REG, CLEARMASK, SETMASK)
  • #Linux(权限管理)
  • (C#)Windows Shell 外壳编程系列9 - QueryInfo 扩展提示
  • (html转换)StringEscapeUtils类的转义与反转义方法
  • (板子)A* astar算法,AcWing第k短路+八数码 带注释
  • (十)【Jmeter】线程(Threads(Users))之jp@gc - Stepping Thread Group (deprecated)
  • (转)JVM内存分配 -Xms128m -Xmx512m -XX:PermSize=128m -XX:MaxPermSize=512m
  • (转)linux下的时间函数使用
  • .NET Core 网络数据采集 -- 使用AngleSharp做html解析
  • .net 前台table如何加一列下拉框_如何用Word编辑参考文献
  • .NET与 java通用的3DES加密解密方法
  • @Tag和@Operation标签失效问题。SpringDoc 2.2.0(OpenApi 3)和Spring Boot 3.1.1集成
  • [Android]Android开发入门之HelloWorld
  • [bzoj1324]Exca王者之剑_最小割
  • [c++] 自写 MyString 类
  • [CareerCup] 12.3 Test Move Method in a Chess Game 测试象棋游戏中的移动方法
  • [DP 训练] Longest Run on a Snowboard, UVa 10285
  • [go] 策略模式