我是 HullQin,公众号 线下团聚游戏 的作者(欢送关注公众号,发送加微信,交个敌人),转发本文前需取得作者 HullQin 受权。我独立开发了《联机桌游合集》,是个网页,能够很不便的跟敌人联机玩斗地主、五子棋等游戏,不免费没广告。还开发了《Dice Crush》加入 Game Jam 2022。喜爱能够关注我 HullQin 噢~我有空了会分享做游戏的相干技术。
背景
在专栏《Go WebSocket》里,有一些前置文章:
第一篇文章:《为什么我选用 Go 重构 Python 版本的 WebSocket 服务?》,介绍了我的指标。
第二篇文章:《你的第一个 Go WebSocket 服务: echo server》,介绍了一下怎么写一个 WebSocket server。
第三篇文章:《单房间的聊天室》,介绍了如何实现一个单房间的聊天室。
第四篇文章:《多房间的聊天室(一)思考篇》,介绍了实现一个多房间的聊天室的思路。
第五篇文章:《多房间的聊天室(二)代码实现》,介绍了实现一个多房间的聊天室的代码。
如果你没浏览下面的文章,肯定要先看一下,因为这篇文章更简单,如果你不弄懂下面几篇,这篇可能跟不上节奏噢。
上篇文章咱们提到:
当初房间数只会源源不断的增多,house 这个 map 会越来越大,终将造成内存不足,这不是一个好事件。
所以咱们后续须要加一个优化:当最初一个客户端断开连接时,回收(删除)这个房间。
明天,咱们实现它。
思路
有一个重要的问题须要想分明:
是在哪个中央执行这个【回收】操作?是哪个 goroutine?什么机会?若有多个中央,有没有竞争关系?
回顾一下之前绘制的图:
能够发现:每个客户端连贯会常驻 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
是否为空即可,若为空,把 hub
从house
(房间汇合)删掉,再完结这个 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
:
测试一下,功败垂成!(能够在 delete 逻辑减少个日志输入)当初断开连接时,无人房间会主动革除掉!并且下次进入时,也会新建房间,不影响失常应用!
真的没问题了吗?
我又绘制了一个图(以一个房间为例),更加残缺:
我用连线,表明了 goroutine 的启动关系:
- User 连贯 WebSocket 服务器时,会先启动
serveWs goroutine
。 - 在
serveWs goroutine
中,会执行register
操作,这一点之前的图中并没画进去。 - 随后
serveWs goroutine
启动了Read goroutine
和Write goroutine
,并完结本人。
这里真的是完满计划不会出错吗?留个悬念,咱们下篇文章,持续解说。
写在最初
我是 HullQin,公众号 线下团聚游戏 的作者(欢送关注公众号,发送加微信,交个敌人),转发本文前需取得作者 HullQin 受权。我独立开发了《联机桌游合集》,是个网页,能够很不便的跟敌人联机玩斗地主、五子棋等游戏,不免费没广告。还开发了《Dice Crush》加入 Game Jam 2022。喜爱能够关注我 HullQin 噢~我有空了会分享做游戏的相干技术。