最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java如何通過SSE實現(xiàn)消息推送詳解

 更新時間:2025年04月21日 08:33:41   作者:野生java研究僧  
這篇文章主要介紹了Java如何通過SSE實現(xiàn)消息推送的相關資料,SSE是一種服務器向客戶端推送數(shù)據(jù)的技術,基于HTTP協(xié)議,利用長連接特性,它適用于單向數(shù)據(jù)流場景,如股票價格更新、新聞實時推送等,需要的朋友可以參考下

1.什么是SSE?

SSE(Server-Sent Events)是一種用于實現(xiàn)服務器主動向客戶端推送數(shù)據(jù)的技術,也被稱為“事件流”(Event Stream)。它基于 HTTP 協(xié)議,利用了其長連接特性,在客戶端與服務器之間建立一條持久化連接,并通過這條連接實現(xiàn)服務器向客戶端的實時數(shù)據(jù)推送。

2.SSE技術的基本原理

  • 客戶端向服務器發(fā)送一個GET請求,帶有指定的header,表示可以接收事件流類型,并禁用任何的事件緩存。
  • 服務器返回一個響應,帶有指定的header,表示事件的媒體類型和編碼,以及使用分塊傳輸編碼(chunked)來流式傳輸動態(tài)生成的內容。
  • 服務器在有數(shù)據(jù)更新時,向客戶端發(fā)送一個或多個名稱:值字段組成的事件,由單個換行符分隔。事件之間由兩個換行符分隔。服務器可以發(fā)送事件數(shù)據(jù)、事件類型、事件ID和重試時間等字段。
  • 客戶端使用EventSource接口來創(chuàng)建一個對象,打開連接,并訂閱onopen、onmessage和onerror等事件處理程序來處理連接狀態(tài)和接收消息。
  • 客戶端可以使用GET查詢參數(shù)來傳遞數(shù)據(jù)給服務器,也可以使用close方法來關閉連接。

3.SSE和Socket的區(qū)別

SSE(Server-Sent Events)和 WebSocket 都是實現(xiàn)服務器向客戶端實時推送數(shù)據(jù)的技術,但它們在某些方面還是有一定的區(qū)別。

技術實現(xiàn)SSE 基于 HTTP 協(xié)議,利用了其長連接特性,通過瀏覽器向服務器發(fā)送一個 HTTP 請求,建立一條持久化的連接。而 WebSocket 則是通過特殊的升級協(xié)議(HTTP/1.1 Upgrade 或者 HTTP/2)建立新的 TCP 連接,與傳統(tǒng) HTTP 連接不同。

數(shù)據(jù)格式SSE 可以傳輸文本和二進制格式的數(shù)據(jù),但只支持單向數(shù)據(jù)流,即只能由服務器向客戶端推送數(shù)據(jù)。WebSocket 支持雙向數(shù)據(jù)流,客戶端和服務器可以互相發(fā)送消息,并且沒有消息大小限制。

連接狀態(tài)SSE 的連接狀態(tài)僅有三種==:已連接、連接中、已斷開==。連接狀態(tài)是由瀏覽器自動維護的,客戶端無法手動關閉或重新打開連接。而 WebSocket 連接的狀態(tài)更靈活,可以手動打開、關閉、重連等。

兼容性SSE 是標準的 Web API,可以在大部分現(xiàn)代瀏覽器和移動設備上使用。但如果需要兼容老版本的瀏覽器(如 IE6/7/8),則需要使用 polyfill 庫進行兼容。而 WebSocket 在一些老版本 Android 手機上可能存在兼容性問題,需要使用一些特殊的 API 進行處理。

安全性SSE 的實現(xiàn)比較簡單,都是基于 HTTP 協(xié)議的,與普通的 Web 應用沒有太大差異,因此風險相對較低。WebSocket 則需要通過額外的安全措施(如 SSL/TLS 加密)來確保數(shù)據(jù)傳輸?shù)陌踩裕苊獗桓`聽和篡改,否則可能會帶來安全隱患。

總體來說,SSE 和 WebSocket 都有各自的優(yōu)缺點,適用于不同的場景和需求。如果只需要服務器向客戶端單向推送數(shù)據(jù),并且應用在前端的瀏覽器環(huán)境中,則 SSE 是一個更加輕量級、易于實現(xiàn)和維護的選擇。而如果需要雙向傳輸數(shù)據(jù)、支持自定義協(xié)議、或者在更加復雜的網絡環(huán)境中應用,則 WebSocket 可能更加適合。

SSE適用于場景SSE適用場景是指服務器向客戶端實時推送數(shù)據(jù)的場景,例如:

  • 股票價格更新:服務器可以根據(jù)股市的變化,實時地將股票價格推送給客戶端,讓客戶端能夠及時了解股票的走勢和行情。
  • 新聞實時推送:服務器可以根據(jù)新聞的更新,實時地將新聞內容或標題推送給客戶端,讓客戶端能夠及時了解最新的新聞動態(tài)和信息。
  • 在線聊天:服務器可以根據(jù)用戶的發(fā)送,實時地將聊天消息推送給客戶端,讓客戶端能夠及時收到和回復消息。
  • 實時監(jiān)控:服務器可以根據(jù)設備的狀態(tài),實時地將監(jiān)控數(shù)據(jù)或報警信息推送給客戶端,讓客戶端能夠及時了解設備的運行情況和異常情況。

SSE適用場景的特點是:

  • 數(shù)據(jù)更新頻繁:服務器需要不斷地將最新的數(shù)據(jù)推送給客戶端,保持數(shù)據(jù)的實時性和準確性。
  • 低延遲:服務器需要盡快地將數(shù)據(jù)推送給客戶端,避免數(shù)據(jù)的延遲和過期。
  • 單向通信:服務器只需要向客戶端推送數(shù)據(jù),而不需要接收客戶端的數(shù)據(jù)。

4.編寫SSE服務,來進行創(chuàng)建鏈接和發(fā)送消息

Service:

package com.zillion.aggregate.app.controller;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Service
public class SSEService {

    private static final Map<String,SseEmitter> sseEmitterMap = new ConcurrentHashMap<>();

    public SseEmitter crateSse(String uid) {
        SseEmitter sseEmitter = new SseEmitter(0L);
        sseEmitter.onCompletion(() -> {
            log.info("[{}]結束鏈接" , uid);
            sseEmitterMap.remove(uid);
        });
        sseEmitter.onTimeout(() -> {
            log.info("[{}]鏈接超時",uid);
        });
        sseEmitter.onError(throwable -> {
            try{
                log.info("[{}]鏈接異常,{}",uid,throwable.toString());
                sseEmitter.send(SseEmitter.event()
                        .id(uid)
                        .name("發(fā)生異常")
                        .data("發(fā)生異常請重試")
                        .reconnectTime(3000));
                sseEmitterMap.put(uid,sseEmitter);
            }catch (IOException e){
                e.printStackTrace();
            }
        });
        try{
            sseEmitter.send(SseEmitter.event().reconnectTime(5000));
        }catch (IOException e){
            e.printStackTrace();
        }
        sseEmitterMap.put(uid,sseEmitter);
        log.info("[{}]創(chuàng)建sse連接成功!",uid);
        return sseEmitter;
    }

    public boolean sendMessage(String uid,String messageId,String message){
        if(StringUtils.isEmpty(message)){
            log.info("[{}]參數(shù)異常,msg為空",uid);
            return false;
        }
        SseEmitter sseEmitter = sseEmitterMap.get(uid);
        if(sseEmitter == null){
            log.info("[{}]sse連接不存在",uid);
            return  false;
        }
        try{
            sseEmitter.send(SseEmitter.event().id(messageId).reconnectTime(60000).data(message));
            log.info("用戶{},消息ID:{},推送成功:{}",uid,messageId,message);
            return true;
        }catch (IOException e){
            sseEmitterMap.remove(uid);
            log.info("用戶{},消息ID:{},消息推送失敗:{}",uid,messageId,message);
            sseEmitter.complete();
            return false;
        }
    }

    public void closeSse(String uid){
        if(sseEmitterMap.containsKey(uid)){
            SseEmitter sseEmitter = sseEmitterMap.get(uid);
            sseEmitter.complete();
            sseEmitterMap.remove(uid);
        }else {
            log.info("用戶{}連接已關閉",uid);
        }
    }
}

Controller:

package com.zillion.aggregate.app.controller;

import cn.hutool.core.util.IdUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Controller
@RequestMapping("/aggregate/api/pay")
public class TestController {

    private static final Map<String,Boolean> SEND_MAP = new ConcurrentHashMap<>();

    @Autowired
    private SSEService sseService;
    @GetMapping("createSse")
    @CrossOrigin
    public SseEmitter createSse(String uid)
    {
        return sseService.crateSse(uid);
    }

    @GetMapping("/sendMsg")
    @ResponseBody
    @CrossOrigin
    public SseEmitter sendMsg(@RequestParam("uid") String uid) throws InterruptedException {
        SseEmitter sseEmitter = sseService.crateSse(uid);
        if (SEND_MAP.get(uid)==null ||  !SEND_MAP.get(uid)){
             new Thread(()->{
                 int i=0;
                 while (true){
                     try {
                         i++;
                         String message = "uid:"+uid+" number:"+i+" message:"+IdUtil.fastUUID().replace("-", "");
                         sseService.sendMessage(uid,"消息"+i,message);
                         SEND_MAP.put(uid,true);
                         Thread.sleep(1000);
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                         closeSse(uid);
                     }
                 }
             }).start();
         }

        return sseEmitter;
    }

    @GetMapping("closeSse")
    @CrossOrigin
    public void closeSse(String uid){
        sseService.closeSse(uid);
    }
}

5.前端實現(xiàn)消息監(jiān)聽

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>SSE消息推送監(jiān)聽</title>
</head>
<body>
    <div id="conMsg"></div>
<script>
    let uid = 1;
    let chat = document.getElementById("conMsg");
    if(window.EventSource){
        var eventSource = new EventSource(`http://localhost:9001/aggregate/aggregate/api/pay/sendMsg?interfaceId=CEDB297CECCC9DCBAD348204ACDD5BAD&uid=${uid}`);
        eventSource.onopen = ()=>{
            console.log("鏈接成功");
        }
        eventSource.onmessage = (ev)=>{
            if(ev.data){
                chat.innerHTML += ev.data+"<br>";
            }
        }
        eventSource.onerror = ()=>{
            console.log("sse鏈接失敗")
        }
    }else{
        alert("當前瀏覽器不支持sse")
    }
</script>
</body>
</html>

總結 

到此這篇關于Java如何通過SSE實現(xiàn)消息推送的文章就介紹到這了,更多相關Java SSE消息推送內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:

相關文章

  • MyBatis關聯(lián)查詢的實現(xiàn)

    MyBatis關聯(lián)查詢的實現(xiàn)

    MyBatis可以通過定義多個表的關聯(lián)關系,實現(xiàn)多表查詢,本文主要介紹了MyBatis關聯(lián)查詢的實現(xiàn),具有一定的參考價值,感興趣的可以了解一下
    2023-11-11
  • 理解Java中的靜態(tài)綁定和動態(tài)綁定

    理解Java中的靜態(tài)綁定和動態(tài)綁定

    這篇文章主要幫助大家理解Java中的靜態(tài)綁定和動態(tài)綁定,在Java中存在兩種綁定方式,一種為靜態(tài)綁定,另一種就是動態(tài)綁定,亦稱為后期綁定,感興趣的小伙伴們可以參考一下
    2016-02-02
  • java微信公眾號支付開發(fā)之現(xiàn)金紅包

    java微信公眾號支付開發(fā)之現(xiàn)金紅包

    這篇文章主要為大家詳細介紹了java微信公眾號支付開發(fā)之現(xiàn)金紅包,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-04-04
  • Java阻塞隊列中的BlockingQueue接口詳解

    Java阻塞隊列中的BlockingQueue接口詳解

    這篇文章主要介紹了Java阻塞隊列中的BlockingQueue接口詳解,對于Queue而言,BlockingQueue是主要的線程安全的版本,具有阻塞功能,可以允許添加、刪除元素被阻塞,直到成功為止,BlockingQueue相對于Queue而言增加了兩個方法put、take元素,需要的朋友可以參考下
    2023-09-09
  • SpringBoot集成百度AI實現(xiàn)人臉識別的項目實踐

    SpringBoot集成百度AI實現(xiàn)人臉識別的項目實踐

    本文主要介紹了SpringBoot集成百度AI實現(xiàn)人臉識別的項目實踐,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-05-05
  • Spring mvc整合mybatis(crud+分頁插件)操作mysql

    Spring mvc整合mybatis(crud+分頁插件)操作mysql

    這篇文章主要介紹了Spring mvc整合mybatis(crud+分頁插件)操作mysql的步驟詳解,需要的朋友可以參考下
    2017-04-04
  • Springboot上傳文件與物理刪除功能

    Springboot上傳文件與物理刪除功能

    文章介紹了在Springboot中實現(xiàn)文件上傳和刪除的功能,實現(xiàn)了圖片的上傳與刪除,刪除數(shù)據(jù)庫記錄時,同步刪除了文件,結合實例代碼給大家講解的非常詳細,感興趣的朋友跟隨小編一起看看吧
    2026-01-01
  • java實現(xiàn)LRU緩存淘汰算法的方法

    java實現(xiàn)LRU緩存淘汰算法的方法

    LRU(Least recently used,最近最少使用)算法根據(jù)數(shù)據(jù)的歷史訪問記錄來進行淘汰數(shù)據(jù),其核心思想是“如果數(shù)據(jù)最近被訪問過,那么將來被訪問的幾率也更高”。下面看下java實現(xiàn)LRU緩存淘汰算法的方法,一起看看吧
    2021-11-11
  • Maven中插件調試與性能調優(yōu)的學習指南

    Maven中插件調試與性能調優(yōu)的學習指南

    在現(xiàn)代Java生態(tài)系統(tǒng)中,Apache Maven作為項目構建的事實標準工具,其核心價值不僅體現(xiàn)在依賴管理能力上,更在于其靈活的插件體系,本文小編就來和大家聊聊Maven中插件調試與性能調優(yōu)的相關知識吧
    2025-05-05
  • Mybatis示例之SelectKey的應用

    Mybatis示例之SelectKey的應用

    今天小編就為大家分享一篇關于Mybatis示例之SelectKey的應用,小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2018-12-12

最新評論

永寿县| 左贡县| 丹阳市| 锦屏县| 石林| 龙泉市| 安国市| 芦溪县| 陆河县| 会东县| 南充市| 新绛县| 珲春市| 昌邑市| 微山县| 响水县| 白山市| 永城市| 寿宁县| 通州市| 河津市| 炎陵县| 尤溪县| 鄂伦春自治旗| 泗阳县| 庄浪县| 林芝县| 泗水县| 和硕县| 韶关市| 内江市| 德清县| 定安县| 靖安县| 临汾市| 青川县| 磐安县| 平邑县| 静乐县| 调兵山市| 施秉县|