欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java 服務(wù)端消息推送的實(shí)現(xiàn)小結(jié)

 更新時(shí)間:2023年10月09日 09:04:21   作者:專注于Java中間件的程序員木木  
本文主要介紹了Java 服務(wù)端消息推送的實(shí)現(xiàn)小結(jié),主要包括四種常見的消息實(shí)時(shí)推送方案:短輪詢、長(zhǎng)輪詢、SSE?和?WebSocket,具有一定的參考價(jià)值,感興趣的可以了解一下

前言:當(dāng)構(gòu)建實(shí)時(shí)消息推送功能時(shí),選擇適合的方案對(duì)于開發(fā)高效的實(shí)時(shí)應(yīng)用至關(guān)重要。消息的推送無非就推、拉兩種數(shù)據(jù)模型。本文將介紹四種常見的消息實(shí)時(shí)推送方案:短輪詢(拉)、長(zhǎng)輪訓(xùn)(拉)、SSE(Server-Sent Events)(推)和WebSocket(推),并以Spring Boot作為技術(shù)底座,展示如何在Java全棧開發(fā)中實(shí)現(xiàn)這些功能。

1. 短輪詢(Short Polling)

什么是短輪詢?

短輪詢是一種簡(jiǎn)單的實(shí)時(shí)消息推送方案,其中客戶端通過定期向服務(wù)器發(fā)送請(qǐng)求來獲取最新的消息。服務(wù)器在接收到請(qǐng)求后立即響應(yīng),無論是否有新消息。如果服務(wù)器沒有新消息可用,客戶端將再次發(fā)送請(qǐng)求。

短輪詢的實(shí)現(xiàn)

在Spring Boot中,可以通過HTTP接口和定時(shí)任務(wù)來實(shí)現(xiàn)短輪詢。下面是一個(gè)簡(jiǎn)單的示例:

// -- 后端接口
@GetMapping("/short")
public String  getShort() {
    long l = System.currentTimeMillis();
    if((l&1) == 1) {
        return "ok";
    }
    return "fail";
}
// --- 前端頁面
@org.springframework.stereotype.Controller
public class Controller {
    @GetMapping("/s")
    public String s() {
        return "s";
    }
}
// -- s.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>短輪詢</title>
</head>
<body>
<p>msg=<span id="message"></span></p>
<script>
    function pollMessage() {
// 發(fā)送輪詢請(qǐng)求
        const xhr = new XMLHttpRequest();
        xhr.open("GET", "/short", true);
        xhr.onreadystatechange = function () {
            if (xhr.readyState === XMLHttpRequest.DONE) {
                if (xhr.status === 200) {
                    document.getElementById("message").innerHTML = xhr.responseText;
                }
            }
        };
        xhr.send();
    }
    setInterval(()=>{
        pollMessage()
    }, 1000)
</script>
</body>
</html>

在上述示例中,getShort()方法用于返回消息,而s方法用于渲染s.html??蛻舳丝梢远ㄆ谡{(diào)用getShort()接口來獲取最新的消息。

短輪詢的特點(diǎn)與限制

短輪詢的實(shí)現(xiàn)簡(jiǎn)單,但存在一些特點(diǎn)和限制:

  • 高延遲: 客戶端需要定期發(fā)送請(qǐng)求,無論是否有新消息。這會(huì)導(dǎo)致一定的延遲,特別是在消息更新較慢的情況下。
  • 高網(wǎng)絡(luò)負(fù)載: 客戶端需要頻繁發(fā)送請(qǐng)求,即使消息沒有更新。這會(huì)增加服務(wù)器和網(wǎng)絡(luò)的負(fù)載。
  • 實(shí)時(shí)性差: 由于需要等待下一次輪詢才能獲取新消息,短輪詢的實(shí)時(shí)性相對(duì)較差。

2. 長(zhǎng)輪詢(Long Polling)

什么是長(zhǎng)輪詢?

長(zhǎng)輪詢是改進(jìn)的輪詢方法,它在沒有新消息時(shí)會(huì)保持請(qǐng)求掛起,直到有新消息到達(dá)或超時(shí)。相比于短輪詢,長(zhǎng)輪詢可以更快地獲取新消息,減少了不必要的請(qǐng)求。

長(zhǎng)輪詢的實(shí)現(xiàn)

在Spring Boot中,可以使用異步請(qǐng)求和定時(shí)任務(wù)來實(shí)現(xiàn)長(zhǎng)輪詢。下面是一個(gè)簡(jiǎn)單的示例:

// -- 請(qǐng)求接口
/**
 * 長(zhǎng)輪詢
 * @return
 */
@GetMapping("/long")
public DeferredResult<String> getLong() {
    DeferredResult<String> deferredResult = new DeferredResult<>();
    if (latestMessage != null) {
        deferredResult.setResult(latestMessage);
    } else {
        // 使用定時(shí)任務(wù)設(shè)置超時(shí)時(shí)間
        TimerTask timeoutTask = new TimerTask() {
            @Override
            public void run() {
                deferredResult.setResult(null);
            }
        };
        Timer timer = new Timer();
        timer.schedule(timeoutTask, 5000); // 設(shè)置超時(shí)時(shí)間為5秒
        // 設(shè)置回調(diào)函數(shù),在消息到達(dá)時(shí)觸發(fā)
        deferredResult.onTimeout(() -> {
            timer.cancel();
            deferredResult.setResult(null);
        });
        deferredResult.onCompletion(timer::cancel);
    }
    return deferredResult;
}
/**
 * 設(shè)置消息
 * @param message
 */
@PostMapping("/send-message")
public void sendMessage(@RequestBody String message) {
    latestMessage = message;
}
// -- 前端請(qǐng)求
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>長(zhǎng)輪詢</title>
</head>
<body>
<p>msg=<span id="message"></span></p>
<p>請(qǐng)求次數(shù):<span id="cnt"></span></p>
<script>
    var cnt = 0
    function pollMessage() {
// 發(fā)送輪詢請(qǐng)求
        const xhr = new XMLHttpRequest();
        xhr.open("GET", "/long", true);
        xhr.onreadystatechange = function () {
            if (xhr.readyState === XMLHttpRequest.DONE) {
                if (xhr.status === 200) {
                    document.getElementById("message").innerHTML = xhr.responseText;
                }
            }
        };
        xhr.send();
    }
    setInterval(()=>{
        ++cnt;
        document.getElementById('cnt').innerHTML = cnt.toString()
        pollMessage()
    }, 5000)
</script>
</body>
</html>

在上述示例中,getLong()方法返回一個(gè)DeferredResult對(duì)象,它會(huì)在有新消息到達(dá)時(shí)觸發(fā)回調(diào)函數(shù)。如果在超時(shí)時(shí)間內(nèi)沒有新消息到達(dá),DeferredResult對(duì)象將返回null。

長(zhǎng)輪詢的特點(diǎn)與限制

長(zhǎng)輪詢具有以下特點(diǎn)與限制:

  • 減少請(qǐng)求次數(shù): 長(zhǎng)輪詢可以更快地獲取新消息,相比于短輪詢,可以減少不必要的請(qǐng)求次數(shù)。
  • 減少網(wǎng)絡(luò)負(fù)載: 當(dāng)沒有新消息時(shí),長(zhǎng)輪詢會(huì)保持請(qǐng)求掛起,減少了頻繁的請(qǐng)求,從而減輕了服務(wù)器和網(wǎng)絡(luò)的負(fù)載。
  • 相對(duì)實(shí)時(shí)性提升: 長(zhǎng)輪詢可以更快地獲取新消息,相比于短輪詢,實(shí)時(shí)性有所提升。然而,仍然需要等待下一次輪詢才能獲取新消息。

3. SSE(Server-Sent Events)

在這里插入圖片描述

什么是SSE?

當(dāng)使用Server-Sent Events(SSE)時(shí),客戶端(通常是瀏覽器)與服務(wù)器之間建立一種持久的連接,使服務(wù)器能夠主動(dòng)向客戶端發(fā)送數(shù)據(jù)。這種單向的、服務(wù)器主動(dòng)推送數(shù)據(jù)的通信模式使得實(shí)時(shí)更新的數(shù)據(jù)能夠被實(shí)時(shí)地傳送到客戶端,而無需客戶端進(jìn)行輪詢請(qǐng)求。

SSE的工作原理如下:

  • 建立連接:客戶端通過使用EventSource對(duì)象在瀏覽器中創(chuàng)建一個(gè)與服務(wù)器的連接??蛻舳讼蚍?wù)器發(fā)送一個(gè)HTTP請(qǐng)求,請(qǐng)求的頭部包含Accept: text/event-stream,以表明客戶端希望接收SSE數(shù)據(jù)。服務(wù)器響應(yīng)這個(gè)請(qǐng)求,并建立一個(gè)持久的HTTP連接。
  • 保持連接:服務(wù)器保持與客戶端的連接打開狀態(tài),不斷發(fā)送數(shù)據(jù)。這個(gè)連接是單向的,只允許服務(wù)器向客戶端發(fā)送數(shù)據(jù),客戶端不能向服務(wù)器發(fā)送數(shù)據(jù)。
  • 服務(wù)器發(fā)送事件:服務(wù)器使用Content-Type: text/event-stream標(biāo)頭來指示響應(yīng)是SSE數(shù)據(jù)流。服務(wù)器將數(shù)據(jù)封裝在特定的SSE格式中,每個(gè)事件都以data:開頭,后面是實(shí)際的數(shù)據(jù)內(nèi)容,以及可選的其他字段,如event:id:。服務(wù)器發(fā)送的數(shù)據(jù)可以是任何文本格式,通常是JSON。
  • 客戶端接收事件:客戶端通過EventSource對(duì)象監(jiān)聽服務(wù)器發(fā)送的事件。當(dāng)服務(wù)器發(fā)送事件時(shí),EventSource對(duì)象會(huì)觸發(fā)相應(yīng)的事件處理程序,開發(fā)人員可以在處理程序中獲取到事件數(shù)據(jù)并進(jìn)行相應(yīng)的操作。常見的事件是message事件,表示接收到新的消息。
  • 斷開連接:當(dāng)客戶端不再需要接收服務(wù)器的事件時(shí),可以關(guān)閉連接。客戶端可以調(diào)用EventSource對(duì)象的close()方法來顯式關(guān)閉連接,或者瀏覽器在頁面卸載時(shí)會(huì)自動(dòng)關(guān)閉連接。

SSE的實(shí)現(xiàn)

在Spring Boot中,可以使用SseEmitter類來實(shí)現(xiàn)SSE。下面是一個(gè)簡(jiǎn)單的示例:

@RestController
public class SSEController {
    private SseEmitter sseEmitter;
    @GetMapping("/subscribe")
    public SseEmitter subscribe() {
        sseEmitter = new SseEmitter();
        return sseEmitter;
    }
    @PostMapping("/send-message")
    public void sendMessage(@RequestBody String message) {
        try {
            if (sseEmitter != null) {
                sseEmitter.send(SseEmitter.event().data(message));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
// - s.html
<!DOCTYPE html>
<html>
<head>
    <title>SSE Demo</title>
</head>
<body>
<h1>SSE Demo</h1>
<div id="message-container"></div>
<script>
    // 創(chuàng)建一個(gè)EventSource對(duì)象,指定SSE的服務(wù)端端點(diǎn)
    var eventSource = new EventSource('/subscribe');
    console.log("eventSource=", eventSource)
    // 監(jiān)聽message事件,接收從服務(wù)端發(fā)送的消息
    eventSource.addEventListener('message', function(event) {
        var message = event.data;
        console.log("message=", message)
        var messageContainer = document.getElementById('message-container');
        messageContainer.innerHTML += '<p>' + message + '</p>';
    });
</script>
</body>
</html>

在上述示例中,客戶端可以通過訪問/subscribe接口來訂閱SSE事件,服務(wù)器會(huì)返回一個(gè)SseEmitter對(duì)象。當(dāng)有新消息到達(dá)時(shí),調(diào)用SseEmitter對(duì)象的send()方法發(fā)送消息。

在這里插入圖片描述

SSE的特點(diǎn)與限制

SSE具有以下特點(diǎn)與限制:

  • 實(shí)時(shí)性較好: SSE使用了持久連接,可以實(shí)現(xiàn)比短輪詢和長(zhǎng)輪詢更好的實(shí)時(shí)性。
  • 單向通信: SSE是單向的,只允許服務(wù)器向客戶端推送消息,客戶端無法向服務(wù)器發(fā)送消息。
  • 不適用于低版本瀏覽器: SSE是HTML5的一部分,不支持低版本的瀏覽器。在使用SSE時(shí),需要確??蛻舳藶g覽器的兼容性。

4. WebSocket

在這里插入圖片描述

什么是WebSocket?

WebSocket是一種雙向通信協(xié)議,允許在單個(gè)持久連接上進(jìn)行全雙工通信。與之前介紹的方案不同,WebSocket提供了雙向通信的能力,可以實(shí)現(xiàn)實(shí)時(shí)的雙向數(shù)據(jù)傳輸。

WebSocket的實(shí)現(xiàn)

在Spring Boot中,可以使用Spring WebSocket模塊來實(shí)現(xiàn)WebSocket功能。下面是一個(gè)簡(jiǎn)單的示例:

1. 創(chuàng)建一個(gè)WebSocket處理器:

@Component
public class WebSocketHandler extends TextWebSocketHandler {
    private List<WebSocketSession> sessions = new CopyOnWriteArrayList<>();
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        sessions.add(session);
    }
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        for (WebSocketSession webSocketSession : sessions) {
            webSocketSession.sendMessage(message);
        }
    }
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        sessions.remove(session);
    }
}

2. 配置WebSocket端點(diǎn):

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
    @Autowired
    private WebSocketHandler webSocketHandler;
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(webSocketHandler, "/websocket").setAllowedOrigins("*");
    }
}

在上述示例中,WebSocketHandler處理器負(fù)責(zé)處理WebSocket連接、消息傳遞和連接關(guān)閉等事件。WebSocketConfig類用于配置WebSocket端點(diǎn)。

3. 前端實(shí)現(xiàn)

<!DOCTYPE html>
<html>
<head>
    <title>WebSocket Demo</title>
</head>
<body>
<h1>WebSocket Demo</h1>
<div id="message-container"></div>
<script>
    // 創(chuàng)建WebSocket對(duì)象,并指定服務(wù)器的URL
    var socket = new WebSocket('ws://localhost:8080/websocket');
    // 監(jiān)聽WebSocket的連接事件
    socket.onopen = function(event) {
        console.log('WebSocket connected');
    };
    // 監(jiān)聽WebSocket的消息事件
    socket.onmessage = function(event) {
        var message = event.data;
        var messageContainer = document.getElementById('message-container');
        messageContainer.innerHTML += '<p>' + message + '</p>';
    };
    // 監(jiān)聽WebSocket的關(guān)閉事件
    socket.onclose = function(event) {
        console.log('WebSocket closed');
    };
    // 發(fā)送消息到服務(wù)器
    function sendMessage() {
        var messageInput = document.getElementById('message-input');
        var message = messageInput.value;
        socket.send(message);
        messageInput.value = '';
    }
</script>
<input type="text" id="message-input" placeholder="Enter message">
<button onclick="sendMessage()">Send</button>
</body>
</html>

在這里插入圖片描述

WebSocket的特點(diǎn)與限制

WebSocket具有以下特點(diǎn)與限制:

  • 實(shí)時(shí)性最佳: WebSocket 提供了真正的雙向通信,可以實(shí)現(xiàn)實(shí)時(shí)的雙向數(shù)據(jù)傳輸,具有最佳的實(shí)時(shí)性。
  • 低延遲: 與輪詢和長(zhǎng)輪詢相比,WebSocket 使用單個(gè)持久連接,減少了連接建立和斷開的開銷,從而降低了延遲。
  • 雙向通信: WebSocket 允許服務(wù)器與客戶端之間進(jìn)行雙向通信,服務(wù)器可以主動(dòng)向客戶端發(fā)送消息,同時(shí)客戶端也可以向服務(wù)器發(fā)送消息。
  • 較高的網(wǎng)絡(luò)負(fù)載: WebSocket 使用長(zhǎng)連接,會(huì)占用一定的網(wǎng)絡(luò)資源。在大規(guī)模并發(fā)場(chǎng)景下,需要注意服務(wù)器的負(fù)載情況。
  • 瀏覽器支持: 大多數(shù)現(xiàn)代瀏覽器都支持 WebSocket,但需要注意在開發(fā)過程中考慮不同瀏覽器的兼容性。

總結(jié)

本文介紹了四種常見的消息實(shí)時(shí)推送方案:短輪詢、長(zhǎng)輪詢、SSE 和 WebSocket,并以 Spring Boot 作為技術(shù)底座,展示了如何在 Java 全棧開發(fā)中實(shí)現(xiàn)這些功能。

  • 短輪詢是一種簡(jiǎn)單的實(shí)時(shí)消息推送方案,但存在高延遲、高網(wǎng)絡(luò)負(fù)載和實(shí)時(shí)性差的限制。
  • 長(zhǎng)輪詢通過保持請(qǐng)求掛起來減少不必要的請(qǐng)求次數(shù),提高了實(shí)時(shí)性,但仍需要輪詢才能獲取新消息。
  • SSE 使用持久連接實(shí)現(xiàn)單向?qū)崟r(shí)消息推送,具有較好的實(shí)時(shí)性,但只支持服務(wù)器向客戶端的單向通信。
  • WebSocket 提供了真正的雙向通信,具有最佳的實(shí)時(shí)性和低延遲,但需要注意較高的網(wǎng)絡(luò)負(fù)載和瀏覽器兼容性。

選擇合適的消息實(shí)時(shí)推送方案取決于具體的需求和場(chǎng)景。根據(jù)應(yīng)用程序的要求和預(yù)期的用戶體驗(yàn),開發(fā)人員可以選擇適當(dāng)?shù)姆桨竵韺?shí)現(xiàn)實(shí)時(shí)消息推送功能。

注意

以上實(shí)現(xiàn)均屬于demo 級(jí)別,為了簡(jiǎn)單演示,將所有的服務(wù)保證措施都刪除了,所以存在包括但不限于以下缺點(diǎn)

比如

  • sse
  • 沒有做會(huì)話管理
  • 明文傳輸
  • WebSocket
  • 客戶端連接的區(qū)分,目前的實(shí)現(xiàn)屬于消息廣播。

  • 連接的可靠性保證:心跳檢測(cè)以及自動(dòng)重連等。

  • 消息的明文傳輸

 到此這篇關(guān)于Java 服務(wù)端消息推送的實(shí)現(xiàn)小結(jié)的文章就介紹到這了,更多相關(guān)Java 服務(wù)端消息推送內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 新手學(xué)習(xí)微服務(wù)SpringCloud項(xiàng)目架構(gòu)搭建方法

    新手學(xué)習(xí)微服務(wù)SpringCloud項(xiàng)目架構(gòu)搭建方法

    這篇文章主要介紹了新手學(xué)習(xí)微服務(wù)SpringCloud項(xiàng)目架構(gòu)搭建方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-01-01
  • 使用Java實(shí)現(xiàn)加密之AES加解密

    使用Java實(shí)現(xiàn)加密之AES加解密

    這篇文章主要介紹了使用Java實(shí)現(xiàn)加密之AES加解密,AES為最常見的對(duì)稱加密算法,對(duì)稱加密算法也就是加密和解密用相同的密鑰,需要的朋友可以參考下
    2023-05-05
  • IDEA Maven 配置備忘筆記

    IDEA Maven 配置備忘筆記

    這篇文章主要介紹了IDEA Maven 配置備忘筆記,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-09-09
  • java.lang.OutOfMemoryError 錯(cuò)誤整理及解決辦法

    java.lang.OutOfMemoryError 錯(cuò)誤整理及解決辦法

    這篇文章主要介紹了java.lang.OutOfMemoryError 錯(cuò)誤整理及解決辦法的相關(guān)資料,需要的朋友可以參考下
    2016-10-10
  • Java生成PDF文檔兩個(gè)超實(shí)用的庫( iText和Apache PDFBox)

    Java生成PDF文檔兩個(gè)超實(shí)用的庫( iText和Apache PDFBox)

    這篇文章主要介紹了Java生成PDF文檔兩個(gè)超實(shí)用的庫,分別是用 iText庫以及用Apache PDFBox庫生成PDF,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-02-02
  • 利用java讀取web項(xiàng)目中json文件為map集合方法示例

    利用java讀取web項(xiàng)目中json文件為map集合方法示例

    這篇文章主要給大家介紹了關(guān)于利用java讀取web項(xiàng)目中json文件為map集合的相關(guān)資料,文中通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧。
    2017-08-08
  • Java匿名內(nèi)部類原理與用法詳解

    Java匿名內(nèi)部類原理與用法詳解

    這篇文章主要介紹了Java匿名內(nèi)部類原理與用法,結(jié)合實(shí)例形式分析了Java匿名內(nèi)部類的概念、原理、應(yīng)用與相關(guān)操作注意事項(xiàng),需要的朋友可以參考下
    2019-09-09
  • 如何使用jakarta.json進(jìn)行json序列化和反序列化

    如何使用jakarta.json進(jìn)行json序列化和反序列化

    java里,json框架何其多,常見的有jackson、fastjson、gson等,本文重點(diǎn)介紹如何使用jakarta.json進(jìn)行json序列化和反序列化,需要的朋友可以參考下,
    2024-07-07
  • 關(guān)于LocalDateTime最常用方法和時(shí)間轉(zhuǎn)換方式

    關(guān)于LocalDateTime最常用方法和時(shí)間轉(zhuǎn)換方式

    Java8版本引入了LocalDateTime和LocalDate類,極大地方便了日期和時(shí)間的處理,本文主要介紹了字符串與LocalDateTime的互轉(zhuǎn),Long型時(shí)間戳與UTC時(shí)間字符串的轉(zhuǎn)換,獲取今天、某天的起止時(shí)間,自定義時(shí)間的設(shè)置,以及LocalDateTime與Date的相互轉(zhuǎn)換等常用方法
    2024-11-11
  • Java中構(gòu)造器內(nèi)部的多態(tài)方法的行為實(shí)例分析

    Java中構(gòu)造器內(nèi)部的多態(tài)方法的行為實(shí)例分析

    這篇文章主要介紹了Java中構(gòu)造器內(nèi)部的多態(tài)方法的行為,結(jié)合實(shí)例形式分析了java構(gòu)造器內(nèi)部多態(tài)方法相關(guān)原理、功能及操作技巧,需要的朋友可以參考下
    2019-10-10

最新評(píng)論