我的理解是为了保证消息的顺序性和防止老消息饿死

这段代码的设计意图,本质上是两个字:

补发

它把消息队列分成两层:

  • ChatServer.Transmit:全局主队列,服务端真正消费这个队列
  • c.SendTo:当前客户端自己的“积压队列”

为什么要先用 forSendTo 里的旧消息往 Transmit 搬,再处理这次新消息?因为作者想保证:

旧消息优先于新消息

不然会出现这种情况:

  1. 之前因为 Transmit 满了,客户端有 3 条旧消息堆在 SendTo
  2. 现在用户又发来一条新消息
  3. 如果不先清空 SendTo,而是直接把新消息塞进 Transmit
  4. 那么新消息就可能比旧消息更早被服务端处理,顺序就乱了

所以这一段:

for len(ChatServer.Transmit) < constants.CHANNEL_SIZE && len(c.SendTo) > 0 {
	sendToMessage := <-c.SendTo
	ChatServer.SendMessageToTransmit(sendToMessage)
}

意思是:

  • 只要全局主队列还有空位
  • 并且这个客户端本地还有积压消息
  • 就优先把旧消息补进主队列

这一步做完之后:

  • 如果 SendTo 已经空了,说明旧账还完了
  • 那当前这条新消息就可以直接进 Transmit

也就是这段:

if len(ChatServer.Transmit) < constants.CHANNEL_SIZE {
	ChatServer.SendMessageToTransmit(jsonMessage)
}

如果这时候 Transmit 还是满的,说明主队列实在塞不进去,那当前新消息只能先暂存在这个客户端自己的 SendTo

else if len(c.SendTo) < constants.CHANNEL_SIZE {
	c.SendTo <- jsonMessage
}

所以整个逻辑顺序是:

  1. 先补发旧消息
  2. 再投递当前新消息
  3. 如果主队列满了,就退而求其次,先存在本地队列
  4. 两边都满了,才报发送失败

你可以把它理解成:

  • Transmit 是高速公路主路
  • SendTo 是匝道等待区

规则是:

  • 匝道里已经排队的车先并回主路
  • 主路有空位时,新来的车直接上主路
  • 主路堵死了,新车先去匝道排队
  • 匝道也堵死了,就不让进了

不过这段代码有个明显问题:

SendTo 里的旧消息,只有当这个客户端再次发送新消息时,才会触发补发。

也就是说,如果一个用户有消息堆在 SendTo,然后他不再发消息了,这些积压消息可能会一直留在那里。这也是这套写法不太合理的地方。

本站总访问量  ·  访客数
你的IP 获取中…