Rumah  >  Artikel  >  Java  >  Cara Spring Boot menggunakan SSE untuk menolak data ke bahagian hadapan

Cara Spring Boot menggunakan SSE untuk menolak data ke bahagian hadapan

WBOY
WBOYke hadapan
2023-05-10 17:31:062903semak imbas

Kata Pengantar

SSE hanyalah teknologi di mana pelayan secara aktif menolak data ke bahagian hadapan, yang bermaksud bahagian hadapan tidak boleh menghantar data ke pelayan. SSE sesuai untuk tolak mesej, pemantauan dan senario lain yang hanya memerlukan data tolak pelayan Berikut ialah simulasi mudah menggunakan Spring Boot untuk menolak data kemajuan ke bahagian hadapan, dan halaman hujung hadapan memaparkan bar kemajuan selepas menerimanya.

Sebelah pelayan

Anda perlu memberi perhatian apabila menggunakannya dalam Spring Boot Sebaik-baiknya menggunakan kelas SseEmitter yang disediakan oleh Spring Web untuk beroperasi pada mulanya, saya menggunakan Content-. Taip seperti yang dinyatakan di Internet Jika ditetapkan kepada strim teks, ia akan mendapati bahagian hadapan akan mencipta semula sambungan setiap kali. Akhirnya, saya mencapai kesan akhir yang diingini dengan merujuk artikel ini:

Kelas Alat 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 {

    /**
     * 当前连接数
     */
    private static AtomicInteger count = new AtomicInteger(0);

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

    public static SseEmitter connect(String userId){
        //设置超时时间,0表示不过期,默认是30秒,超过时间未完成会抛出异常
        SseEmitter sseEmitter = new SseEmitter(0L);
        //注册回调
        sseEmitter.onCompletion(completionCallBack(userId));
        sseEmitter.onError(errorCallBack(userId));
        sseEmitter.onTimeout(timeOutCallBack(userId));
        sseEmitterMap.put(userId,sseEmitter);
        //数量+1
        count.getAndIncrement();
        log.info("create new sse connect ,current user:{}",userId);
        return sseEmitter;
    }
    /**
     * 给指定用户发消息
     */
    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();
            }
        }
    }

    /**
     * 想多人发送消息,组播
     */
    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);
            }
        });
    }
    /**
     * 群发消息
     */
    public static void batchSendMessage(String message, Set<String> userIds){
        userIds.forEach(userId->sendMessage(userId,message));
    }
    public static void removeUser(String userId){
        sseEmitterMap.remove(userId);
        //数量-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("结束连接,{}",userId);
            removeUser(userId);
        };
    }
    private static Runnable timeOutCallBack(String userId){
        return ()->{
            log.info("连接超时,{}",userId);
            removeUser(userId);
        };
    }
    private static Consumer<Throwable> errorCallBack(String userId){
        return throwable -> {
            log.error("连接异常,{}",userId);
            removeUser(userId);
        };
    }
}

Kelas di atas boleh digunakan sebagai kelas alat SSE. Mari gunakannya di bawah

Buat SSEController.java dalam lapisan Pengawal

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));
        }
    }
}

Sambungan di atas digunakan untuk menyambung ke sse Pada masa ini Sambungan telah dibuat, dan antara muka proses di bawah digunakan untuk menolak data Saya akan membiarkan bahagian hadapan menyedari kesan bar kemajuan, jadi apa yang ditolak adalah nombor kesan yang jelas, saya menolaknya kepada 50 hingga 70. Kelajuan diperlahankan, selebihnya ialah 100ms

Kod bahagian hadapan

<!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+&#39;%&#39;;
            document.getElementById("my-progress").value = event.data;
        }
    </script>
</head>
<body>
    <div id="result"></div>
    <progress  id="my-progress" value="0" max="100"></progress>
</body>
</html>

Kesan akhir:

Cara Spring Boot menggunakan SSE untuk menolak data ke bahagian hadapan

Atas ialah kandungan terperinci Cara Spring Boot menggunakan SSE untuk menolak data ke bahagian hadapan. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:yisu.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam