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

Spring?Boot?使用?SSE?方式向前端推送數(shù)據(jù)詳解

 更新時(shí)間:2022年08月10日 16:09:06   作者:@胡海龍  
這篇文章主要介紹了Spring?Boot?使用SSE方式向前端推送數(shù)據(jù)詳解,SSE簡(jiǎn)單的來(lái)說(shuō)就是服務(wù)器主動(dòng)向前端推送數(shù)據(jù)的一種技術(shù),它是單向的,也就是說(shuō)前端是不能向服務(wù)器發(fā)送數(shù)據(jù)的

前言

SSE簡(jiǎn)單的來(lái)說(shuō)就是服務(wù)器主動(dòng)向前端推送數(shù)據(jù)的一種技術(shù),它是單向的,也就是說(shuō)前端是不能向服務(wù)器發(fā)送數(shù)據(jù)的。SSE適用于消息推送,監(jiān)控等只需要服務(wù)器推送數(shù)據(jù)的場(chǎng)景中,下面是使用Spring Boot 來(lái)實(shí)現(xiàn)一個(gè)簡(jiǎn)單的模擬向前端推動(dòng)進(jìn)度數(shù)據(jù),前端頁(yè)面接受后展示進(jìn)度條。

服務(wù)端

在Spring Boot中使用時(shí)需要注意,最好使用Spring Web 提供的SseEmitter這個(gè)類來(lái)進(jìn)行操作,我在剛開(kāi)始時(shí)使用網(wǎng)上說(shuō)的將Content-Type設(shè)置為text-stream這種方式發(fā)現(xiàn)每次前端每次都會(huì)重新創(chuàng)建接。最后參考該文實(shí)現(xiàn)了最終想要的效果:

SSE工具類

SSEServer.java

package vip.huhailong.catchat.sse;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;

/**
 * @author Huhailong
 */
@Slf4j
public class SSEServer {

    /**
     * 當(dāng)前連接數(shù)
     */
    private static AtomicInteger count = new AtomicInteger(0);

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

    public static SseEmitter connect(String userId){
        //設(shè)置超時(shí)時(shí)間,0表示不過(guò)期,默認(rèn)是30秒,超過(guò)時(shí)間未完成會(huì)拋出異常
        SseEmitter sseEmitter = new SseEmitter(0L);
        //注冊(cè)回調(diào)
        sseEmitter.onCompletion(completionCallBack(userId));
        sseEmitter.onError(errorCallBack(userId));
        sseEmitter.onTimeout(timeOutCallBack(userId));
        sseEmitterMap.put(userId,sseEmitter);
        //數(shù)量+1
        count.getAndIncrement();
        log.info("create new sse connect ,current user:{}",userId);
        return sseEmitter;
    }
    /**
     * 給指定用戶發(fā)消息
     */
    public static void sendMessage(String userId, String message){
        if(sseEmitterMap.containsKey(userId)){
            try{
                sseEmitterMap.get(userId).send(message);
            }catch (IOException e){
                log.error("user id:{}, send message error:{}",userId,e.getMessage());
                e.printStackTrace();
            }
        }
    }

    /**
     * 想多人發(fā)送消息,組播
     */
    public static void groupSendMessage(String groupId, String message){
        if(sseEmitterMap!=null&&!sseEmitterMap.isEmpty()){
            sseEmitterMap.forEach((k,v) -> {
                try{
                    if(k.startsWith(groupId)){
                        v.send(message, MediaType.APPLICATION_JSON);
                    }
                }catch (IOException e){
                    log.error("user id:{}, send message error:{}",groupId,message);
                    removeUser(k);
                }
            });
        }
    }
    public static void batchSendMessage(String message) {
        sseEmitterMap.forEach((k,v)->{
            try{
                v.send(message,MediaType.APPLICATION_JSON);
            }catch (IOException e){
                log.error("user id:{}, send message error:{}",k,e.getMessage());
                removeUser(k);
            }
        });
    }
    /**
     * 群發(fā)消息
     */
    public static void batchSendMessage(String message, Set<String> userIds){
        userIds.forEach(userId->sendMessage(userId,message));
    }
    public static void removeUser(String userId){
        sseEmitterMap.remove(userId);
        //數(shù)量-1
        count.getAndDecrement();
        log.info("remove user id:{}",userId);
    }
    public static List<String> getIds(){
        return new ArrayList<>(sseEmitterMap.keySet());
    }
    public static int getUserCount(){
        return count.intValue();
    }
    private static Runnable completionCallBack(String userId) {
        return () -> {
            log.info("結(jié)束連接,{}",userId);
            removeUser(userId);
        };
    }
    private static Runnable timeOutCallBack(String userId){
        return ()->{
            log.info("連接超時(shí),{}",userId);
            removeUser(userId);
        };
    }
    private static Consumer<Throwable> errorCallBack(String userId){
        return throwable -> {
            log.error("連接異常,{}",userId);
            removeUser(userId);
        };
    }
}

上面這個(gè)類可以把它當(dāng)作一個(gè)SSE的工具類,下面我們使用一下它

在Controller層創(chuàng)建 SSEController.java

package vip.huhailong.catchat.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import vip.huhailong.catchat.sse.SSEServer;

/**
 * @author Huhailong
 */
@Slf4j
@RestController
@CrossOrigin
@RequestMapping("/sse")
public class SSEController {

    @GetMapping("/connect/{userId}")
    public SseEmitter connect(@PathVariable String userId){
        return SSEServer.connect(userId);
    }

    @GetMapping("/process")
    public void sendMessage() throws InterruptedException {
        for(int i=0; i<=100; i++){
            if(i>50&&i<70){
                Thread.sleep(500L);
            }else{
                Thread.sleep(100L);
            }
            SSEServer.batchSendMessage(String.valueOf(i));
        }
    }
}

上面的connect是用來(lái)連接sse的,它返回一個(gè)SseEmitter實(shí)例,這時(shí)候連接就已經(jīng)創(chuàng)建了,然后下面的process接口是用來(lái)推送數(shù)據(jù)的,我這里是準(zhǔn)備讓前端實(shí)現(xiàn)一個(gè)進(jìn)度條的效果,所以推送的是數(shù)字,為了效果明顯,我在推送到50到70的時(shí)候速度放慢,其余都是100ms

前端代碼

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Home</title>
    <script>
        let data = new EventSource("/cat-chat/sse/connect/huhailong")
        data.onmessage = function(event){
            console.log("test=>",event)
            document.getElementById("result").innerText = event.data+'%';
            document.getElementById("my-progress").value = event.data;
        }
    </script>
</head>
<body>
    <div id="result"></div>
    <progress style="width: 300px" id="my-progress" value="0" max="100"></progress>
</body>
</html>

最終效果:

到此這篇關(guān)于Spring Boot 使用 SSE 方式向前端推送數(shù)據(jù)詳解的文章就介紹到這了,更多相關(guān)Spring Boot SSE內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • JavaCV使用ffmpeg實(shí)現(xiàn)錄屏功能

    JavaCV使用ffmpeg實(shí)現(xiàn)錄屏功能

    這篇文章主要介紹了JavaCV如何使用ffmpeg實(shí)現(xiàn)錄屏功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-06-06
  • MyBatis?的?XML?配置文件和緩存使用步驟

    MyBatis?的?XML?配置文件和緩存使用步驟

    MyBatis?包含一個(gè)非常強(qiáng)大的查詢緩存特性,它可以非常方便地配置和定制,這篇文章主要介紹了MyBatis的XML配置文件和緩存,需要的朋友可以參考下
    2022-01-01
  • java多態(tài)機(jī)制原理特點(diǎn)詳解

    java多態(tài)機(jī)制原理特點(diǎn)詳解

    在本篇文章里小編給大家分享的是關(guān)于java多態(tài)機(jī)制原理特點(diǎn)詳解,有需要的朋友們可以跟著學(xué)習(xí)下。
    2020-02-02
  • 解決JDK版本沖突顯示問(wèn)題(雙版本沖突)

    解決JDK版本沖突顯示問(wèn)題(雙版本沖突)

    這篇文章主要介紹了解決JDK版本沖突顯示問(wèn)題(雙版本沖突),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • 解讀@Bean和@Autowired、@Resource之間的區(qū)別

    解讀@Bean和@Autowired、@Resource之間的區(qū)別

    這篇文章主要介紹了@Bean和@Autowired、@Resource之間的區(qū)別及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-03-03
  • Java8 Stream flatmap中間操作用法解析

    Java8 Stream flatmap中間操作用法解析

    這篇文章主要介紹了Java8 Stream flatmap中間操作用法解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-07-07
  • SpringBoot數(shù)據(jù)層測(cè)試事務(wù)回滾的實(shí)現(xiàn)流程

    SpringBoot數(shù)據(jù)層測(cè)試事務(wù)回滾的實(shí)現(xiàn)流程

    這篇文章主要介紹了SpringBoot數(shù)據(jù)層測(cè)試事務(wù)回滾的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧
    2022-10-10
  • Java如何獲取JSON中某個(gè)對(duì)象的值

    Java如何獲取JSON中某個(gè)對(duì)象的值

    這篇文章主要介紹了Java如何獲取JSON中某個(gè)對(duì)象的值,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • spring實(shí)現(xiàn)靜態(tài)注入(類或者屬性)操作示例

    spring實(shí)現(xiàn)靜態(tài)注入(類或者屬性)操作示例

    這篇文章主要為大家介紹了spring實(shí)現(xiàn)靜態(tài)注入(類或者屬性)操作示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • mybatis-plus 判斷isnull or的操作

    mybatis-plus 判斷isnull or的操作

    這篇文章主要介紹了mybatis-plus 判斷isnull or的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-02-02

最新評(píng)論

会泽县| 蒲江县| 渝北区| 永州市| 湛江市| 仙桃市| 顺义区| 汉川市| 阿巴嘎旗| 苏尼特右旗| 海宁市| 建始县| 蒙自县| 鸡东县| 宜君县| 洛隆县| 都安| 游戏| 乐亭县| 买车| 周宁县| 武陟县| 白城市| 永清县| 白山市| 灵寿县| 阜新| 彩票| 南召县| 西乌珠穆沁旗| 台安县| 堆龙德庆县| 百色市| 襄城县| 财经| 渝中区| 肥西县| 汪清县| 保德县| 嘉禾县| 凉城县|