Java项目使用webSocket给前端推送数据(Java项目使用WebSocket接口给前端传输数据,通道连接未关闭,但是没有数据返回)

1. nginx设置超时时间配置

websocket

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

websocket超时(必须长)

proxy_connect_timeout 3600s;
proxy_send_timeout 3600s;
proxy_read_timeout 3600s;

禁止缓冲

proxy_buffering off;

防止大数据断开

proxy_request_buffering off;

tcp优化

tcp_nodelay on;

长连接

keepalive_timeout 3600s;

2. 引入WebSocket配置

java 复制代码
/**
 * 开启WebSocket支持
 * @author Zgz
 */
@Configuration
public class WebSocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

}

3.开启异步支持

java 复制代码
@Configuration
@EnableAsync // 确保开启异步支持
public class AsyncTaskConfig {

    @Bean("taskExecutor") // 定义Bean名称为taskExecutor,@Async默认会使用它
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 设置核心线程数,可根据您的任务数量和服务器CPU核心数调整
        executor.setCorePoolSize(10);
        // 设置最大线程数
        executor.setMaxPoolSize(30);
        // 设置队列容量
        executor.setQueueCapacity(100);
        // 设置线程空闲时间(秒)
        executor.setKeepAliveSeconds(60);
        // 设置线程名称前缀,便于日志追踪
        executor.setThreadNamePrefix("DiagnosisAsync-");
        // 设置拒绝策略:当线程池和队列都满时,由调用者线程(通常是定时任务线程)自己执行
        // 这是一种保证任务不会被丢弃的策略,适合您的业务场景 [1,6](@ref)
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 初始化线程池
        executor.initialize();
        return executor;
    }
}

4.启动类添加异步识别注解

5.定义webSocket接口前端访问地址

java 复制代码
/**
 * @author Lxz
 * 状态监测WebSocket - 已增加心跳保活(ping/pong)
 */
@ServerEndpoint("/conditionMonitoring/{userId}")
@Component
public class ConditionMonitoringSocket {

    static Log log = LogFactory.get(ConditionMonitoringSocket.class);
    /**静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。*/
    private static int onlineCount = 0;
    /**concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。*/
    private static ConcurrentHashMap<String, ConditionMonitoringSocket> webSocketMap = new ConcurrentHashMap<>();
    /**与某个客户端的连接会话,需要通过它来给客户端发送数据*/
    private Session session;
    /**接收userId*/
    private String userId = "";

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
        this.session = session;
        this.userId = userId;
        if (webSocketMap.containsKey(userId)) {
            webSocketMap.remove(userId);
            webSocketMap.put(userId, this);
        } else {
            webSocketMap.put(userId, this);
            addOnlineCount();
        }

        log.info("用户连接:" + userId + ",当前在线人数为:" + getOnlineCount());

        try {
            sendMessage("连接成功");
        } catch (IOException e) {
            log.error("用户:" + userId + ",网络异常!!!!!!");
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        if (webSocketMap.containsKey(userId)) {
            webSocketMap.remove(userId);
            subOnlineCount();
        }
        log.info("用户退出:" + userId + ",当前在线人数为:" + getOnlineCount());
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("用户消息:" + userId + ",报文:" + message);

        // ===================== 【新增:心跳包自动响应】 =====================
        if ("ping".equalsIgnoreCase(message)) {
            try {
                sendMessage("pong");
                log.info("用户[{}] 心跳响应 pong", userId);
            } catch (IOException e) {
                log.error("用户[{}] 心跳回复失败", userId, e);
            }
            return; // 心跳包不进入业务逻辑
        }
        // ==================================================================

        if (StrUtil.isNotEmpty(message)) {
            try {
                String toUserId = this.userId;
                if (StrUtil.isNotEmpty(toUserId) && webSocketMap.containsKey(toUserId)) {
                    webSocketMap.get(toUserId).sendMessage(message);
                } else {
                    log.error("请求的userId:" + toUserId + "不在该服务器上");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("用户错误:" + this.userId + ",原因:" + error.getMessage());
        error.printStackTrace();
    }

    /**
     * 实现服务器主动推送(----异步推送----)
     */
    public void sendMessage(String message) throws IOException {
        this.session.getAsyncRemote().sendText(message);
    }

    /**
     * 发送自定义消息
     */
    public static void sendInfo(String message, @PathParam("userId") String userId) throws IOException {
        log.info("发送消息到:" + userId + ",报文:" + message);
        if (StrUtil.isNotEmpty(userId) && webSocketMap.containsKey(userId)) {
            webSocketMap.get(userId).sendMessage(message);
        } else {
            log.error("用户" + userId + ",不在线!");
        }
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        ConditionMonitoringSocket.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        ConditionMonitoringSocket.onlineCount--;
    }

    public static ConcurrentHashMap<String, ConditionMonitoringSocket> getWebSocketSet() {
        return webSocketMap;
    }

}

6.开启定时推送数据

java 复制代码
@Component
public class ConditionMonitoringTask {


    @Resource
    private SzlsProcessPointService szlsProcessPointService;

    @Scheduled(cron = "*/15 * * * * *")
    public void run() throws Exception {
        try {
            ConcurrentHashMap<String, ConditionMonitoringSocket> webSocketMap = ConditionMonitoringSocket.getWebSocketSet();
            Set<String> collect = ConditionMonitoringSocket.getWebSocketSet().keySet().stream().collect(Collectors.toSet());
            // 查询当前设备运行状态
            if(collect.size()>0){
                Object monitoring = szlsProcessPointService.conditionMonitoringInternal(null);
                for (String s : collect) {
                    Map<String,Object> returnMap = new HashMap<>();
                    returnMap.put("code",200);
                    returnMap.put("msg","成功");
                    returnMap.put("data",monitoring);
                    webSocketMap.get(s).sendMessage(JSONObject.toJSONString(returnMap));
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

后端测试地址为:ws://IP:端口号/realTimeData/userId

注:这个userId无论是前端还是后端都要传成随机数,不能重复。

相关推荐
Howrun7771 小时前
从公钥密码学到 HTTPS:一文读懂数字证书与信任链条
网络协议·http·https
2601_957780841 小时前
Agent记忆系统架构设计与工程实践:从短期暂存到长期持久化
大数据·网络·人工智能·架构·agent
聚铭网络1 小时前
聚铭网络入选数说安全《AI重塑网络安全:网络安全智能化产品与市场报告》
网络·人工智能·安全
一只小白0001 小时前
一篇讲清 HTTP / HTTPS / DNS
网络·网络协议·http
happyh h h h p p p p1 小时前
VLAN综合实验
网络·智能路由器
淼淼爱喝水1 小时前
Ansible 中 handler 与 notify 的作用与使用详解
linux·网络·apache·playbook
ggaofeng1 小时前
在应用层用 TAP 设备从零实现完整的 TCP/IP 协议栈,并让两台物理机通过这套“自定义协议栈”通信
网络·网络协议·tcp/ip
@insist1231 小时前
信息安全工程师-网络安全风险评估(下篇):风险计算、工具应用
网络·安全·软考·信息安全工程师·软件水平考试
路baby1 小时前
CSRF漏洞详细讲解 并基于pikachu靶场实战演示
网络·网络协议·安全·web安全·网络安全·网络攻击模型·csrf