java使用websocket推送消息到页面

在这里插入图片描述

一、项目背景

公司有2个项目,项目一采用vue2,项目二采用vue3,目前分别记录。

二、使用方式

1.vue2+java+spring

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>ems-common</artifactId>
        <groupId>com.hero.lte.ems</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>ems-common-websocket</artifactId>
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-messaging</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-websocket</artifactId>
        <version>4.2.3.RELEASE</version>
    </dependency>

    <!-- http://mvnrepository.com/artifact/org.eclipse.jetty.websocket/websocket-server -->
    <dependency>
        <groupId>org.eclipse.jetty.websocket</groupId>
        <artifactId>websocket-server</artifactId>
    </dependency>


    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
    </dependency>

    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
    </dependency>

    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>

    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>jcl-over-slf4j</artifactId>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>log4j-over-slf4j</artifactId>
    </dependency>

    <dependency>
        <groupId>com.hero.lte.ems</groupId>
        <artifactId>other</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>



</dependencies>

</project>

RealtimeMonitor.vue

data() {
    return {
        websocketSetting: [
                { topic: '/user/#/realTimeMonitoring', clientId: 'monitoring', callback: this.websocketCallback }
            ]
	}
}

methods: {
	websocketInit() {
            socketInstance.init(this.websocketSetting);
    },
    websocketCallback(msg) {
        const data = JSON.parse(msg.body);
        const CPUUtilization = data.CPUUsage;
        this.$refs.realtimeCpuMonitor.getMonitorData(CPUUtilization);
        const memoryTotal = data.memoryTotal;
        const memoryUsage = data.memoryUsage;
        const memoryUsageRatio = data.memoryUsageRatio;
        this.$refs.realtimeMemoryMonitor.getMonitorData(memoryTotal, memoryUsage, memoryUsageRatio);
        const diskTotal = data.diskTotal;
        const diskUsage = data.diskUsage;
        const diskUtilization = data.diskUsageRatio;
        this.$refs.realtimeDiskMonitor.getMonitorData(diskTotal, diskUsage, diskUtilization);
    },
	mounted() {
		this.websocketInit();
	},
	beforeDestroy() {
	    for (let i = 0; i < this.websocketSetting.length; i++) {
	        socketInstance.stopReceiveTopicMsg(this.websocketSetting[i].clientId, this.websocketSetting[i].topic);
	}
}

MonitorTaskExe.java

import com.hero.lte.ems.websocket.server.WServerHelper;

@Resource
WServerHelper serverHelper;

private void realTimeMonitoring() {
	serverHelper.push2OneClient(WSTopicEnum.realTimeMonitoring.name(),"monitoring",JSONObject.toJSONString(jsonObj));
}

WSTopicEnum.java

package com.hero.lte.ems.websocket.enums;

public enum WSTopicEnum {

    ElementServer,
    OamTaskServer,
    TrackReportServer,
    TrackTaskServer,
    ObserveReportServer,
    ObserveTaskServer,
    SpectrumServer,
    TSTaskServer,
    CurrentEventServer,
    AlarmCountServer,
    PmTaskServer,
    MsgDispathServer,
    AutoAlarmServer,
    PmCurrenttimeWsServer,
    NodeEventPushServer,
    CurrentAlarmServer,
    RackBoardStatusServer,
    MMLServer,
    VmQueryWsServer,
    VmUpdateWsServer,
    PocVersionServer,
    DHCPServer,
    VmQueryVersionServer,
    ConfigExportServer,
    ConsistencyServer,
    ConfigImportMMLServer,
    realTimeMonitoring,
    monitorServiceProcess,
    configBackup,
    ExecuteTheTaskImmediately,
    BatchInitNECfgServer,
    uploadConfigServer;

    public static WSTopicEnum formatEnum(String value)
    {
        for(WSTopicEnum status : WSTopicEnum.values())
        {
            if(status.name().equalsIgnoreCase(value))
            {
                return status;
            }
        }
        return null;
    }
}

WServerHelper.java

package com.hero.lte.ems.websocket.server;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Component;

@Component
public class WServerHelper {

    @Autowired
    SimpMessagingTemplate messagingTemplate;


    public void push2OneClient(String topic,String channlId ,Object msg) {
        this.messagingTemplate.convertAndSend("/user/"+channlId+"/"+topic, msg);
    }


    public void push2AllClient(String topic,Object msg) {
        this.messagingTemplate.convertAndSend("/topic/"+topic, msg);
    }

}

2.vue3+java+springboot

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>data-service-solution</artifactId>
        <groupId>com.xnms</groupId>
        <version>1.0.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>xnms-data-service</artifactId>
    <version>1.0.0</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.xnms</groupId>
            <artifactId>xnms-data-contract</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>com.xnms</groupId>
            <artifactId>xnms-data-service-api</artifactId>
            <version>${project.version}</version>
        </dependency>

        <!-- Fastjson dependency -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>2.0.30</version> <!-- 使用最新版本的fastjson -->
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- Spring Boot Starter Data JPA -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <!-- Spring Boot WebSocket 支持 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
            <version>2.7.7</version>
        </dependency>
        <!-- MySQL Connector -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.zaxxer</groupId>
            <artifactId>HikariCP</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
        </dependency>

        <!-- Apache POI for Excel -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>5.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>5.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-collections4</artifactId>
            <version>4.4</version>
        </dependency>
        <dependency>
            <groupId>org.apache.xmlbeans</groupId>
            <artifactId>xmlbeans</artifactId>
            <version>5.1.1</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.60.Final</version>
        </dependency>
    </dependencies>

</project>

TopologyView.vue

let webSocket = null;

const connectWebSocket = (url) => {
  if (
    webSocket &&
    (webSocket.readyState === WebSocket.OPEN ||
      webSocket.readyState === WebSocket.CONNECTING)
  ) {
    return;
  }
  webSocket = new WebSocket(url);
  webSocket.onopen = () => console.log("TopologyView.vue WebSocket已连接");
  webSocket.onmessage = handleWebSocketMessage;
  webSocket.onclose = () => console.log("TopologyView.vue WebSocket已关闭");
  webSocket.onerror = (error) =>
    console.error("TopologyView.vue WebSocket错误:", error);
};

// WebSocket处理逻辑
const handleWebSocketMessage = (event) => {
  try {
    const message = JSON.parse(event.data);
    	......
    } catch (error) {
    console.error("TopologyView.vue WebSocket消息处理错误:", error);
  }
};

onUnmounted(() => {
  if (webSocket &&
    (webSocket.readyState === WebSocket.OPEN ||
      webSocket.readyState === WebSocket.CONNECTING)
  ) {
    webSocket.close();
  }
});

onMounted(async () => {
  connectWebSocket("/ws/topoView");
}

AlarmDataInquiryController.java

@Operation(summary = "根据站点ID集合查询站点详细信息")
@GetMapping(value = "/testWebsocketRepeaterNetState")
public ResponseModel<List<Repeater>> testWebsocketRepeaterNetState(@RequestParam String repeaterId, @RequestParam Integer rptState, @RequestParam String serialNo){
    RepeaterNetStateEntity repeaterNetStateEntity = new RepeaterNetStateEntity();
    repeaterNetStateEntity.setRepeaterId(repeaterId);
    repeaterNetStateEntity.setRptState(rptState);
    repeaterNetStateEntity.setSerialNo(serialNo);
    pushService.messageDataFormatting("topoView", repeaterNetStateEntity);
    return ResponseModel.ofSuccess();
}

PushService.java

package com.xnms.data.service.service.impl.websocket;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;

@Service
public class PushService {

    /**
     * 🌟 向指定频道推送消息
     * @param channel 频道名称(对应URL路径)
     * @param message 消息内容
     */
    public void pushToChannel(String channel, String message) {
        CopyOnWriteArraySet<WebSocketSession> sessions = PushWebSocketHandler.channelSessions.get(channel);
        if (sessions != null) {
            sessions.forEach(session -> {
                try {
                    if (session.isOpen()) {
                        session.sendMessage(new TextMessage(message));
                    }
                } catch (IOException e) {
                    // 处理异常
                }
            });
        }
    }

    /**
     * 🌟 广播所有频道
     * @param message 消息内容
     */
    public void broadcast(String message) {
        PushWebSocketHandler.channelSessions.values().forEach(sessions -> {
            sessions.forEach(session -> {
                try {
                    if (session.isOpen()) {
                        session.sendMessage(new TextMessage(message));
                    }
                } catch (IOException e) {
                    // 处理异常
                }
            });
        });
    }

    /**
     * 数据格式化
     */
    public void messageDataFormatting(String path, Object data){
        ObjectMapper objectMapper = new ObjectMapper();
        String message = null;
        try {
            message = objectMapper.writeValueAsString(data);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
        this.pushToChannel(path, message);
    }

}

PushWebSocketHandler.java

package com.xnms.data.service.service.impl.websocket;

import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArraySet;

public class PushWebSocketHandler extends TextWebSocketHandler {
    // 🌟 多频道存储结构:channel -> sessions
    static final ConcurrentMap<String, CopyOnWriteArraySet<WebSocketSession>> channelSessions =
            new ConcurrentHashMap<>();

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        String channel = getChannelFromSession(session);
        channelSessions.computeIfAbsent(channel, k -> new CopyOnWriteArraySet<>()).add(session);
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        String channel = getChannelFromSession(session);
        CopyOnWriteArraySet<WebSocketSession> sessions = channelSessions.get(channel);
        if (sessions != null) {
            sessions.remove(session);
            if (sessions.isEmpty()) {
                channelSessions.remove(channel);
            }
        }
    }

    private String getChannelFromSession(WebSocketSession session) {
        return (String) session.getAttributes().get("channel");
    }
}

WebSocketConfig.java

package com.xnms.data.service.service.impl.websocket;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;

import java.util.Map;

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(pushWebSocketHandler(), "/{channel}")
                .addInterceptors(new ChannelInterceptor())
                .setAllowedOrigins("*");
    }

    @Bean
    public WebSocketHandler pushWebSocketHandler() {
        return new PushWebSocketHandler();
    }

    /**
     * 🌟 频道拦截器(用于获取路径参数)
     */
    private static class ChannelInterceptor extends HttpSessionHandshakeInterceptor {
        @Override
        public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
                                       WebSocketHandler wsHandler, Map<String, Object> attributes) {
            String channel = ((ServletServerHttpRequest) request).getServletRequest().getRequestURI()
                    .split("/")[1];
            attributes.put("channel", channel);
            try {
                return super.beforeHandshake(request, response, wsHandler, attributes);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }
}

RepeaterNetStateEntity.java

package com.xnms.data.service.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class RepeaterNetStateEntity {

    private String repeaterId;

    private int rptState;

    private String serialNo;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刘大猫.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值