Java Springboot SSE 解决永久存活 判断客户端离线问题

这篇具有很好参考价值的文章主要介绍了Java Springboot SSE 解决永久存活 判断客户端离线问题。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

        在生产环境下,服务端的SseEmitter对象在初始化时可以填入参数,以保证其存活时间,一旦超时,客户端会自动断线重连,在这个过程中如果没有做消息队列等缓存手段,就可能会丢数据。

java sse 客户端,java,spring

        但是如果设置SseEmitter存活时间为永久(参数填0),就会导致服务端无法感知客户端下线,从而使服务端维持连接池会越来越大无法释放。

java sse 客户端,java,spring

         导致这一问题的出现,就是服务端在发送消息后没有直接可用的方法可以感知客户端是否接到消息,我们可以手写一些判断解决上述问题。依据的原理是当客户端无法接受消息时,SseEmitter对象在send一次之后sendFailed状态会变为True,这时候就可以剔除。同时在订阅时用此判断可以减少重复创建的机会(获取对象属性的方法式借用网上大佬的代码)

java sse 客户端,java,spring

   public boolean checkSseConnectAlive(SseEmitter sseEmitter) {
        if (sseEmitter == null) {
            return false;
        }
        return !(Boolean) getFieldInstance(sseEmitter, "sendFailed") &&
                !(Boolean) getFieldInstance(sseEmitter, "complete");
    }



    public static Object getFieldInstance(Object obj, String fieldPath) {
        String fields[] = fieldPath.split("#");
        for (String field : fields) {
            obj = getField(obj, obj.getClass(), field);
            if (obj == null) {
                return null;
            }
        }

        return obj;
    }

    public static Object getField(Object obj, Class<?> clazz, String fieldName) {
        for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
            try {
                Field field;
                field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);
                return field.get(obj);
            } catch (Exception e) {
            }
        }

        return null;
    }

以下为关键步骤:

1、首先设置服务端生存时间为永久

2、建立定时器,固定时间发送消息用来检测客户端是否离线

3、订阅判断

附完整代码:


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

    public SseEmitter subscribe(String sseClientId) {
        if (!NotNullCheck.str(sseClientId)) {
            return null;
        }
        SseEmitter sseEmitter = null;
        SseEmitter getClientEmitter = sseCache.get(sseClientId);
        if (getClientEmitter == null || !checkSseConnectAlive(getClientEmitter)) {
            // 生存时间设置 默认30s
            sseEmitter = new SseEmitter();
            // 设置前端的重试时间
            try {
                sseEmitter.send(SseEmitter.event().reconnectTime(Settings.sseClientReconnectTime).data("连接成功"));
                sseCache.put(sseClientId, sseEmitter);
                System.out.println("add " + sseClientId);
                sseEmitter.onTimeout(() -> {
                    System.out.println(sseClientId + "超时");
                    sseCache.remove(sseClientId);
                });
                sseEmitter.onCompletion(() -> System.out.println("已关闭连接:" + sseClientId));
            } catch (Exception e) {
                System.err.println(e.getMessage());
            }
        } else {
            System.out.println("已存在!");
            sseEmitter = getClientEmitter;
        }
        return sseEmitter;
    }


    public boolean sendOneClientMessage(String sseClientId,Object msg) throws IOException {
        SseEmitter sseEmitter = sseCache.get(sseClientId);
        if (sseEmitter != null && checkSseConnectAlive(sseEmitter)) {
            sseEmitter.send(SseEmitter.event().data(msg));
            return true;
        } else {
            return false;
        }
    }

    public void sendAllClientMsg(Object msg) {
        if (sseCache != null && !sseCache.isEmpty()) {
            sseCache.entrySet().forEach(sseEmitterSet -> {
                String sseClientId = sseEmitterSet.getKey();
                SseEmitter sseEmitter = sseEmitterSet.getValue();
                if (sseEmitter != null) {
                    if (checkSseConnectAlive(sseEmitter)) {
                        SseEmitter.SseEventBuilder sseEventBuilder = SseEmitter
                                .event()
                                .data(msg)
                                .reconnectTime(Settings.sseClientReconnectTime);
                        try {
                            sseEmitter.send(sseEventBuilder);
                        } catch (Exception e) {
                            System.err.println("SSE check sseClientId send error:" + sseClientId);
                        }
                    } else {
                        sseEmitter.complete();
                        sseCache.remove(sseClientId);
                        System.out.println("SSE check sseClientId offline:" + sseClientId);
                    }
                }
            });
        }
    }

    public boolean close(String sseClientId) {
        SseEmitter sseEmitter = sseCache.get(sseClientId);
        if (sseEmitter != null) {
            System.out.println("SSE active close connection :" + sseClientId);
            sseEmitter.complete();
            sseCache.remove(sseClientId);
            return true;
        } else {
            return false;
        }
    }

    public boolean checkSseConnectAlive(SseEmitter sseEmitter) {
        if (sseEmitter == null) {
            return false;
        }
        return !(Boolean) GetObjField.getFieldInstance(sseEmitter, "sendFailed") &&
                !(Boolean) GetObjField.getFieldInstance(sseEmitter, "complete");
    }


    public void aliveCheck(ApplicationArguments args) throws Exception {
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                sendAllClientMsg("Check Alive");
            }
        }, 3 * 1000, 3 * 1000);
    }
    
    

        当然这只是基础做法,如果要保证时效和可靠性还需要其他工具来辅助,欢迎各位大佬留言相互学习。文章来源地址https://www.toymoban.com/news/detail-790678.html

到了这里,关于Java Springboot SSE 解决永久存活 判断客户端离线问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Java】SpringBoot快速整合WebSocket实现客户端服务端相互推送信息

    目录 什么是webSocket? webSocket可以用来做什么? WebSocket操作类 一:测试客户端向服务端推送消息 1.启动SpringBoot项目 2.打开网站 3.进行测试消息推送 4.后端进行查看测试结果 二:测试服务端向客户端推送消息 1.接口代码 2.使用postman进行调用 3.查看测试结果         WebSocke

    2024年01月20日
    浏览(58)
  • 《深入理解Java虚拟机》读书笔记:判断对象是否存活

    本节内容的概要如下; 对象已死吗?   给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。 客观地说,引用计数算法(Reference Counting)的实现简单,判定效率也很高,在

    2024年02月14日
    浏览(87)
  • SpringBoot中使用Spring integration加Eclipse Paho Java Client 实现MQTT客户端

    Spring Integration 是一个开源的集成消息处理框架,它提供了消息传递、消息过滤、消息转换、消息路由等功能,可以用于构建异步、分布式的系统。 Spring-integration-stream是Spring Integration框架的一个组件,用于在不同的系统和应用之间进行消息传递、集成和流处理。 它提供了一套

    2024年02月10日
    浏览(50)
  • java webSocket服务端、客户端、心跳检测优雅解决

    项目分为三个端,项目之间需要webSocket通信。 WebSocketConfig WebSocketServer

    2024年02月17日
    浏览(38)
  • 【go】gorilla/websocket如何判断客户端强制断开连接

    当客户端因为某些问题异常关闭连接时,可以判断关闭连接的异常类型 通过调用websocket.IsCloseError或websocket.IsUnexpectedCloseError即可 其中github源码如下 异常类型如下

    2024年02月16日
    浏览(56)
  • 【Elasticsearch学习笔记五】es常用的JAVA API、es整合SpringBoot项目中使用、利用JAVA代码操作es、RestHighLevelClient客户端对象

    目录 一、Maven项目集成Easticsearch 1)客户端对象 2)索引操作 3)文档操作 4)高级查询 二、springboot项目集成Spring Data操作Elasticsearch 1)pom文件 2)yaml 3)数据实体类 4)配置类 5)Dao数据访问对象 6)索引操作 7)文档操作 8)文档搜索 三、springboot项目集成bboss操作elasticsearch

    2023年04月09日
    浏览(48)
  • TCP客户端判断与服务端断开连接的几种方法

    目前已知的方法有: 1、 epoll(能检测正常的断开连接,事件触发机制,优点是快速,但是插拔网线是检测不到的) 2、自定义心跳包方式检测 3、keeplive方式检测 4、getsockopt 1、相对于select和poll来说,epoll更加灵活,没有描述符限制。epoll使用一个文件描述符管理多个描述符,

    2023年04月14日
    浏览(55)
  • IP-Guard如何判断Windows客户端是否安装成功?

    1.在电脑本地打开services.msc 服务页面,找有没有windows helper service服务,该服务的可执行文件的路径中的进程是winrdgv3.exe是客户端进程。 2.cmd输入netstat -an|find \\\"823\\\",从输出的内容上判断是否连接上服务器连; 客户端开放8235端口,供服务器连接客户端使用,正常情况是LISTENING 客

    2024年02月16日
    浏览(36)
  • tcp连接断开分析,判断tcp断开原因是客户端还是服务端?

              当与使用TCP协议对接的硬件设备进行通信时,往往会遇到一些问题,导致一些人难以找到tcp断开的根源,因些无法判定是充电桩设备客户端还是服务器端。我曾经在十多年前对接银行接口的POS机时遇到过类似的情况,现在在对接充电桩时又遇到了相似的问题。经过

    2024年02月03日
    浏览(36)
  • java SECS管理系统 将逐步推出 SECS 客户端(Passive) 管理系统 SECS快速开发平台 springboot secs开发平台

    这是一套SECS客户端(Passive),可以直接连接PLC设备,支持Modbus、三菱MC、欧姆龙Fine、OPC-UA、西门子S7设备等通信。 企业已经有了EAP软件,但是设备没有SECS通信功能,这时候可以使用这套框架,直接连接设备,实现SECS通信。    

    2024年01月17日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包