天天看點

[Go WebSocket] 多房間的聊天室(三)自動清理無人房間背景思路直接看源碼開始開發真的沒問題了嗎?寫在最後

大家好,我是公衆号「線下聚會遊戲」作者HullQin,開發了《聯機桌遊合集》,是個網頁,可以很友善的跟朋友聯機玩鬥地主、五子棋等遊戲。

背景

在專欄《Go WebSocket》裡,有一些前置文章:

第一篇文章:《為什麼我選用Go重構Python版本的WebSocket服務?》,介紹了我的目标。

第二篇文章:《你的第一個Go WebSocket服務: echo server》,介紹了一下怎麼寫一個WebSocket server。

第三篇文章:《單房間的聊天室》,介紹了如何實作一個單房間的聊天室。

第四篇文章:《多房間的聊天室(一)思考篇》,介紹了實作一個多房間的聊天室的思路。

第五篇文章:《多房間的聊天室(二)代碼實作》,介紹了實作一個多房間的聊天室的代碼。

如果你沒閱讀上面的文章,一定要先看一下,因為這篇文章更複雜,如果你不弄懂上面幾篇,這篇可能跟不上節奏噢。

上篇文章我們提到:

現在房間數隻會源源不斷的增多,house這個map會越來越大,終将造成記憶體不足,這不是一個好事情。

是以我們後續需要加一個優化:當最後一個用戶端斷開連接配接時,回收(删除)這個房間。

今天,我們實作它。

思路

有一個重要的問題需要想清楚:

是在哪個地方執行這個【回收】操作?是哪個goroutine?什麼時機?若有多個地方,有沒有競争關系?

回顧一下之前繪制的圖:

[Go WebSocket] 多房間的聊天室(三)自動清理無人房間背景思路直接看源碼開始開發真的沒問題了嗎?寫在最後

可以發現:每個用戶端連接配接會常駐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

[Go WebSocket] 多房間的聊天室(三)自動清理無人房間背景思路直接看源碼開始開發真的沒問題了嗎?寫在最後

測試一下,大功告成!(可以在delete邏輯增加個日志輸出)現在斷開連接配接時,無人房間會自動清除掉!并且下次進入時,也會建立房間,不影響正常使用!

真的沒問題了嗎?

我又繪制了一個圖(以一個房間為例),更加完整:

[Go WebSocket] 多房間的聊天室(三)自動清理無人房間背景思路直接看源碼開始開發真的沒問題了嗎?寫在最後
  • User連接配接WebSocket伺服器時,會先啟動

    serveWs goroutine

  • serveWs goroutine

    中,會執行

    register

    操作,這一點之前的圖中并沒畫出來。
  • 随後

    serveWs goroutine

    啟動了

    Read goroutine

    Write goroutine

    ,并結束自己。

寫在最後