首頁 > 軟體

SpringBoot+WebSocket實現訊息推播功能

2022-08-09 22:00:46

背景

專案中經常會用到訊息推播功能,關於推播技術的實現,我們通常會聯想到輪詢、comet長連線技術,雖然這些技術能夠實現,但是需要反覆連線,對於服務資源消耗過大,隨著技術的發展,HtML5定義了WebSocket協定,能更好的節省伺服器資源和頻寬,並且能夠更實時地進行通訊。本文將介紹如何採用websocket實現訊息推播。

WebSocket簡介

WebSocket協定是基於TCP的一種新的網路協定。它實現了瀏覽器與伺服器全雙工(full-duplex)通訊——允許伺服器主動傳送資訊給使用者端。瀏覽器和伺服器僅需一次握手,兩者之間就直接可以建立永續性的連線,並進行雙向資料傳輸。

協定原理

Websocket協定基於Http協定,針對Http協定進行了相關的改善,且Websocket協定也需要建立TCP連線來實現資料傳輸,具體實現如下圖:

說明:

  • 使用者端發起http請求,經過3次握手後,建立起TCP連線;http請求裡存放WebSocket支援的版本號等資訊,如:Upgrade、Connection、WebSocket-Version等。
  • 伺服器收到使用者端的握手請求後,同樣採用HTTP協定回饋資料
  • 使用者端收到連線成功的訊息後,開始藉助於TCP傳輸通道進行全雙工通訊.

WebSocket與HTTP協定的區別

相同點:都是一樣基於TCP的,都是可靠性傳輸協定。都是應用層協定。

不同點:

  • WebSocket是雙向通訊協定,可以雙向傳送或接受資訊,而HTTP是單向協定
  • WebSocket需要瀏覽器和伺服器握手進行建立連線的,而http是瀏覽器發起向伺服器的連線。

WebSocket特點

  • 建立在TCP協定之上,伺服器端的實現比較容易。
  • 資料格式比較輕量,效能開銷小,通訊高效。
  • 支援多種資料格式,可以傳送文字、二進位制資料。
  • 使用者端可以與任意伺服器通訊,無同源限制。

應用場景

  • 即時聊天通訊
  • 線上協同編輯/編輯
  • 實時資料流的拉取與推播
  • 實時地圖位置

系統整合Websocket

jar包引入

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.4.RELEASE</version>
    <relativePath/>
 </parent>
 
  <dependency>
      <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-websocket</artifactId>
  </dependency>

Websocket設定

@Configuration
public class WebSocketConfig
{
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

具體實現

@ServerEndpoint(value="/websocket/{uid}")
@Component
public class WebSocketServer
{
    private Logger logger = LoggerFactory.getLogger(WebSocketServer.class);
    
    private static final AtomicInteger onlineCount = new AtomicInteger(0);
    
    private static CopyOnWriteArraySet<Session> sessionSet = new CopyOnWriteArraySet<Session>();
    
    @OnOpen
    public void onOpen(Session session,@PathParam("uid") String uid) 
    {
        logger.info("open message uid:{}",uid);
        sessionSet.add(session);
        onlineCount.incrementAndGet();
        logger.info("視窗開始監聽uid:{},當前線上人數:{}",uid,onlineCount);
    }
    
    @OnClose
    public void onClose(Session session) 
    {
        String sessionId=session.getId();
        logger.info("sessionid:{} close",sessionId);
        sessionSet.remove(this);
        int count=onlineCount.decrementAndGet();
        logger.info("有一連線關閉!當前線上人數為:{}",count);
    }

    @OnError
    public void onError(Session session, Throwable error) 
    {
        logger.error("訊息發生錯誤:{},Session ID: {}",error.getMessage(),session.getId());
    }
    
   
    public void batchSendMesasge(String uid,String message) throws IOException 
    {
        logger.info("推播訊息到視窗:{},推播內容:{}",uid,message);
        for(Session session:sessionSet){
            sendMessage(session, message);
        }
    }
    
    public void sendMessage(Session session, String message) throws IOException {
        
        if(session!=null)
        {
            synchronized (session) {
                session.getBasicRemote().sendText(message);
            }
        }
    }

}

說明: @OnOpen :當有新的WebSocket連線進入時呼叫 @OnClose:當有WebSocket連線關閉時呼叫 @OnError :當有WebSocket丟擲異常時呼叫 @OnMessage:當接收到字串訊息時,對該方法進行回撥

測試範例

@Controller
public class WebScoketController
{
   @Autowired
   private WebSocketServer webSocketServer;
  
    @ResponseBody
    @RequestMapping("/sendMessage")
    public String batchMessage(String uid,String message) 
    {  
        Map<String, String> map =new HashMap<String, String>();
        try 
        {
            map.put("code", "200");
            webSocketServer.batchSendMesasge(uid,message);
        } 
        catch (Exception e) 
        {
            map.put("code", "-1");
            map.put("message", e.getMessage());
        }
        return JSON.toJSONString(map);
    } 
    
    @GetMapping("/enter")
    public String enter() 
    { 
        return "webscoketTest.html";
    }
}

頁面請求websocket

<!DOCTYPE HTML>
<html>
   <head>
   <meta charset="utf-8">
   <title>websocket test</title>
    
      <script type="text/javascript">
            if ("WebSocket" in window)
            {
            	console.log("您的瀏覽器支援 WebSocket!");
               
               var ws = new WebSocket("ws://127.0.0.1:9092/websocket/1234");
               console.log('ws連線狀態:' + ws.readyState);
               
               //開啟
               ws.onopen = function()
               {
                  ws.send("message test");
                  console.log("mesage sending");
               };
                
                //傳送訊息
               ws.onmessage = function (evt) 
               { 
                  var received_msg = evt.data;
                  alert(received_msg);
               };
                
                //關閉
               ws.onclose = function()
               { 
                  // 關閉 websocket
                  console.log("socket is close"); 
               };
            }
            
            else
            {
            	 console.log("您的瀏覽器不支援 WebSocket!");
            }
      </script>
   </head>
</html>

測試效果

啟動程式:執行http://localhost:9092/enter 進入頁面開啟websocket。

使用者傳送訊息:http://localhost:9092/sendMessage?uid=1235&message=this%20is%20message1

執行的結果如下:

 open message uid:1234
 [nio-9092-exec-2] c.s.f.w.controller.WebSocketServer: 視窗開始監聽uid:1234,當前線上人數:1
 [nio-9092-exec-5] c.s.f.w.controller.WebSocketServer: 推播訊息到視窗:1234,推播內容:this is message

以上就是SpringBoot+WebSocket實現訊息推播功能的詳細內容,更多關於SpringBoot WebSocket訊息推播的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com