Spring Boot系列21 Spring Websocket實現websocket集羣方案討論

概述

本文對websocket集羣的方案進行討論:前端

  1. 在websocket集羣中,後端準確將指定的消息推送到指定的用戶,前端實時接收服務推送的消息
  2. 對websocket集羣的方案進行討論,並肯定最佳方案

webscoket集羣方案

集羣方案分析

這裏寫圖片描述

在上個博文Spring Boot系列20 Spring Websocket實現向指定的用戶發送消息中實現向指定用戶發送消息的功能,可是咱們將提供websocket服務的服務進行集羣(如上圖)則存在以下問題:web

上圖中,用戶A經過websocket註冊到服務A,服務A經過STOMP協議訂閱RabbitMQ上的消息,同理用戶B。若是用戶A鏈接到服務A上,那麼在位於服務B上的MQ模塊即便使用SimpMessagingTemplate實例向用戶A發送消息,此消息也沒法到達用戶A,緣由是由於服務B上沒有服務A的註冊信息,沒法準確的推送消息.只有在服務A上的MQ模塊使用SimpMessagingTemplate實例向這個用戶發送消息,消息纔會到達用戶Aredis

針對這個問題下文咱們經過3個方案解決這個問題,並詳細分析每一個方案的有缺點。後端

webSocket集羣方案一

概述 無論消息的接收者鏈接在哪一個服務上,每一個服務A/B都接收消息,對相同的消息都使用SimpMessagingTemplate實例進行推送,保證總有一個消息會被用戶收到。 瀏覽器

這裏寫圖片描述

詳細流程以下緩存

  1. 用戶A/B分別經過ws鏈接服務A/B, 而後服務A/B經過stomp協議接入RabbitMQ
  2. 消息發送者將消息發送到RabbitMQ的交換機上,使用扇形交換機。這樣保證同一個消息能夠同時被服務A/B接收
  3. 兩個服務上的MQ模塊接收對應消息後,無論對應的用戶是不是經過本身鏈接到RabbitMQ,直接使用SimpMessagingTemplate實例向消息中指定的用戶推送消息
  4. 用戶A/B接收到對應的消息

優勢服務器

  1. 實現比較簡單

不足websocket

  1. 消息生產者發送消息的RabbitMQ交換機必須是廣播功能,如扇形交換機
  2. 爲了保證消息順利到達用戶,相同的消息必須在兩個服務A/B上執行相同的操做。這樣若是服務越多,則重複的發送消息越多
  3. 若是用戶不在線,不管發送多少消息用戶都不能收到

webSocket集羣方案二

概述 使用redis緩存用戶的websocket鏈接信息,記錄用戶登陸到哪一個服務上,當有消息過來時,將消息推送到用戶登陸的服務,而後服務都使用SimpMessagingTemplate實例進行推送session

這裏寫圖片描述

在方案一的基礎上增長以下功能:架構

  1. 服務A/B上增長MQ模塊,服務A/B上MQ模塊會鏈接到RabbitMQ,分別訂閱隊列A/B
  2. 服務A/B增長WS模塊,當websocket鏈接過來時,將此用戶的鏈接信息存儲到redis上,系統記住每一個用戶登陸的到哪一個服務
  3. 消息生產者將消息推送到交換機,不直接推送到服務A/B
  4. 增長新的模塊dispatch,此模塊接收到消息,而後從redis中讀取要消息要推送到用戶鏈接到那個服務器上,而後將消息發送到用戶鏈接服務對應的隊列中。若是消息要發送給用戶B,則dispatch模塊會將消息發送到隊列B
  5. 服務A/B的MQ模塊接收到消息後,使用SimpMessagingTemplate實例向指定用戶推送消息

優勢

  1. 此方案克服上一個方案不足的地方

缺點

  1. 實現複雜
  2. 發送MQ消息的次數增長1倍

webSocket集羣方案三

概述 不使用SimpMessagingTemplate,使用RabbitMQ的客戶端API直接向用戶在RabbitMQ上訂閱的隊列發送消息

發現用戶經過瀏覽器登陸websocket並註冊RabbitMQ時,此時這個鏈接會在RabbitMQ創建一個隊列,隊列的名稱相似stomp-subscription-***,此隊列綁定到默認交換機amq.topic,路由鍵爲"web訂閱隊列名稱+'-user'+websocket sessionId"(這裏是demo-userpjplggbl,demo是stomp weboscket鏈接的隊列名稱,pjplggbl登陸websocket登陸時的websocket sessionId值),圖片以下:

這裏寫圖片描述

根據這個,設計以下架構:

這裏寫圖片描述

在方案一的基礎進行以下修改,新的架構圖流程以下:

  1. 服務A增長WS模塊,當websocket鏈接過來時,將此用戶的鏈接信息(主要是websocket sesionId值)存儲redis中
  2. 消息生產者發送消息到的交換機,這些服務不直接推送服務A/B
  3. 增長新的模塊dispatch,此模塊接收推送過來的信息,並從redis中讀取消息接收用戶對應的websocket sesionId值,而後根據上面的規則計算出用戶對應的路由鍵,而後將消息發送到用戶訂閱的隊列上
  4. 前端接收消息

優勢:

  1. 即克服第一個方案不足的地方,又比第二個方案簡單

結論

方案三是最好的方案,下一篇文章,咱們會介紹如何在代碼中實現方案三

相關文章
相關標籤/搜索