了解SSE.前后端通信.spring boot 实现SSE.spring mvc 实现SSE.养生店房间显示屏通知示例..单向通信.可靠性高. 单点实现方

spring boot 实现SSE.高可靠性.前后端通信.spring mvc 实现SSE.养生店房间显示屏通知示例…单向通信. 单点实现方案

引用

  • SSE 和 Websocket 的优缺点分析与比较

SSE(Server-Sent Events)是 HTML5 遵循 W3C 标准提出的客户端和服务端之间进行实时通信的协议。

优点

  1. SSE 客户端可以接收来自服务器的“流”数据,而不需要进行轮询。由于**不浪费请求**,因此 SSE 对于减轻服务器的压力非常有用。


  2. SSE 使用**纯 JavaScript** 实现简单,不需要额外的插件或库来处理消息(简单方便)。


  3. SSE 天生具有自适应性,由于 SSE 是基于 HTTP 响应使用 EventStream 传递消息,因此它利用了 HTTP 的开销和互联网上的结构。


  4. SSE 可以与任何服务器语言和平台一起使用,因为 SSE 是一种规定了消息传递方式的技术,不依赖于具体的服务器语言和平台。

缺点

  1. SSE 是**单向通信**,只能从服务器推送到客户端。如果应用程序需要双向通信,就需要使用 Websocket。


  2. SSE无法发送二进制数据,只能发送 UTF-8 编码的文本。如果应用程序需要发送二进制数据,就需要使用 Websocket。


  3. SSE 不是所有浏览器都支持。虽然 SSE 是 HTML5 的一部分,但具体的浏览器支持性可能会有差异。

示例场景:

养生店PC端操作单端通信告知房间小屏数据发生变更做出页面刷新。

当前台PC端 安排了 技师到某个房间,或者上钟,确认项目,设置空房等等. 进行消息通知 .房间小屏进行数据的相应变更

前后端通信断网情况处理。断开连接后,每隔一定睡眠时间,再次自调用确保重建连接。


let source = null
//查看 
    if (!!window.EventSource) {
        createSource()

    } else {
        console.log("不支持");
    }

    window.onbeforeunload = function () {
        close();
    };

    // 关闭  为避免占用服务器资源. 调用后台提供出来的清除通信链接API 
    function close() {
        source.close();
        const httpRequest = new XMLHttpRequest();
        httpRequest.open('GET', 'http://192.168.10.83:8080/api/sse/communication/closePadClient?shopId=1&roomId=1', true);
        httpRequest.send();
        console.log("close");
    }

    function createSource(){
        source = new EventSource('http://192.168.10.83:8080/api/sse/communication/createPadClient?shopId=1&roomId=1');
        //建立连接
        source.onopen = function (event) {
            console.log("建立连接" + event);
        }
        //接收数据
        source.onmessage = function (event) {
            console.log(event.data);
        }
        //错误监听
        source.onerror = async function (event) {
            if (event.currentTarget.readyState === EventSource.CLOSED) {
                console.log("连接关闭");
                await sleep(3000)
                close()
                createSource()
            } else {
                console.log(event);
            }
        }
    }
	//js 睡眠解决方案
    const sleep = async (count) => {
        return new Promise((resolve) => {
            setTimeout(() => {
                resolve()
            }, count)
        })
    }

Spring MVC代码

@Controller
@RequestMapping("/api/sse/communication")
public class SSECommunicationServiceApi {


    @RequestMapping("/createPadClient")
    public SseEmitter createPadClient(HttpServletRequest request) {
        String shopId = request.getParameter("shopId");
        String roomId = request.getParameter("roomId");
        if (!StringUtils.hasText(shopId)) {
            throw new BusinessException("连接错误!店铺id为空");
        }
        if (!StringUtils.hasText(roomId)) {
            throw new BusinessException("连接错误!房间id为空");
        }
        // 设置超时时间,0表示不过期。默认30000毫秒
        //可以在客户端一直断网、直接关闭页面但未提醒后端的情况下,服务端在一定时间等待后自动关闭网络连接
        SseEmitter sseEmitter = new SseEmitter(0L);
        String roomKey = shopId+":"+roomId;
        SSECommunicationUtil.putSSEEmitter(roomKey,sseEmitter);
        System.out.println("sse连接,当前客户端:"+roomKey);
        return sseEmitter;
    }
    @ResponseBody
    @RequestMapping("/closePadClient")
    public BaseRespVO closePadClient(HttpServletRequest request) {
        String shopId = request.getParameter("shopId");
        String roomId = request.getParameter("roomId");
        if (!StringUtils.hasText(shopId)) {
            throw new BusinessException("关闭连接错误!店铺id为空");
        }
        if (!StringUtils.hasText(roomId)) {
            throw new BusinessException("关闭连接错误!房间id为空");
        }
        String roomKey = shopId+":"+roomId;
        SSECommunicationUtil.removeSSEEmitter(roomKey);
        System.out.println("sse连接,当前客户端:"+roomKey);
        return new BaseRespVO("关闭成功!");
    }

    @ResponseBody
    @RequestMapping( "/testNotify")
    public BaseRespVO testNotify(HttpServletRequest request) {
        String shopId = request.getParameter("shopId");
        String roomId = request.getParameter("roomId");
        SSECommunicationUtil.updateRegNotificationPad(shopId,roomId, RegChangeNotificationEnum.ARRANGE_DOCTOR);

        return new BaseRespVO("发送完成!");
    }


}

SSE管理连接工具类。

public class SSECommunicationUtil {

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


    public static void updateRegNotificationPad(String shopId, String roomId, RegChangeNotificationEnum notify) {
        try {
            SseEmitter sseEmitter = cache.get(shopId+":"+roomId);
            Random random = new Random();
            int sseId = random.nextInt(100000);
            sseEmitter.send(
                    SseEmitter
                            .event()
                            .data(notify)
                            .id("" + sseId)
                            .reconnectTime(3000)
            );
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    public static void putSSEEmitter(String key,SseEmitter emitter) {
        SSECommunicationUtil.cache.put(key,emitter);
    }
    public static void removeSSEEmitter(String key) {
        SSECommunicationUtil.cache.remove(key);
    }
}

通知消息枚举类

public enum RegChangeNotificationEnum {
    ARRANGE_DOCTOR("ARRANGE_DOCTOR","对已预约记录安排医生"),
    CLOCK_UP("CLOCK_UP","医生已进行调理(已上钟)"),
    CLOCK_DOWN("CLOCK_DOWN","医生已结束调理(已下钟)"),
    CONFIM_ITEM("CONFIM_ITEM","已确认调理项目"),
    VACATED_ROOM("VACATED_ROOM","已打扫,置空房"),
    ;
    private String code;
    private String title;

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    RegChangeNotificationEnum(String code, String title) {
        this.code = code;
        this.title = title;
    }
}

你可能感兴趣的:(Java,SSE,前后端通信,spring,spring,boot,mvc,socket)