spring-boot-demo-websocket
https://gitee.com/xkcoding/spring-boot-demo
此 demo 主要演示了 Spring Boot 如何集成 WebSocket,实现后端主动往前端推送数据。网上大部分websocket的例子都是聊天室,本例主要是推送服务器状态信息。前端页面基于vue和element-ui实现。
1. 代码
1.1. pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| <?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"> <modelVersion>4.0.0</modelVersion>
<artifactId>spring-boot-demo-websocket</artifactId> <version>1.0.0-SNAPSHOT</version>
<name>spring-boot-demo-websocket</name> <description>Demo project for Spring Boot</description>
<parent> <groupId>com.xkcoding</groupId> <artifactId>spring-boot-demo</artifactId> <version>1.0.0-SNAPSHOT</version> </parent>
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <oshi.version>3.9.1</oshi.version> </properties>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
<dependency> <groupId>com.github.oshi</groupId> <artifactId>oshi-core</artifactId> <version>${oshi.version}</version> </dependency>
<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> </dependency>
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> </dependency>
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies>
<build> <finalName>spring-boot-demo-websocket</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
</project>
|
1.2. WebSocketConfig.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
@Configuration @EnableWebSocket @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/notification") .setAllowedOrigins("*") .withSockJS(); }
@Override public void configureMessageBroker(MessageBrokerRegistry registry) { registry.enableSimpleBroker("/topic"); }
}
|
1.3. 服务器相关实体
此部分实体 参见包路径 com.xkcoding.websocket.model
1.4. ServerTask.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
@Slf4j @Component public class ServerTask { @Autowired private SimpMessagingTemplate wsTemplate;
@Scheduled(cron = "0/2 * * * * ?") public void websocket() throws Exception { log.info("【推送消息】开始执行:{}", DateUtil.formatDateTime(new Date())); Server server = new Server(); server.copyTo(); ServerVO serverVO = ServerUtil.wrapServerVO(server); Dict dict = ServerUtil.wrapServerDict(serverVO); wsTemplate.convertAndSend(WebSocketConsts.PUSH_SERVER, JSONUtil.toJsonStr(dict)); log.info("【推送消息】执行结束:{}", DateUtil.formatDateTime(new Date())); } }
|
1.5. server.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>服务器信息</title> <link href="https://cdnjs.cloudflare.com/ajax/libs/element-ui/2.4.11/theme-chalk/index.css" rel="stylesheet"> <style> .el-row { margin-bottom: 20px; }
.el-row:last-child { margin-bottom: 0; }
.sysFile { margin-bottom: 5px; }
.sysFile:last-child { margin-bottom: 0; } </style> </head> <body> <div id="app"> <el-container> <el-header> <el-button @click="_initSockJs" type="primary" :disabled="isConnected">手动连接</el-button> <el-button @click="_destroySockJs" type="danger" :disabled="!isConnected">断开连接</el-button> </el-header> <el-main> <el-row :gutter="20"> <el-col :span="12"> <el-card> <div slot="header"> <span>CPU信息</span> </div> <el-table size="small" border :data="server.cpu" style="width: 100%"> <el-table-column prop="key" label="属性"> </el-table-column> <el-table-column prop="value" label="值"> </el-table-column> </el-table> </el-card> </el-col> <el-col :span="12"> <el-card> <div slot="header"> <span>内存信息</span> </div> <el-table size="small" border :data="server.mem" style="width: 100%"> <el-table-column prop="key" label="属性"> </el-table-column> <el-table-column prop="value" label="值"> </el-table-column> </el-table> </el-card> </el-col> </el-row> <el-row> <el-col :span="24"> <el-card> <div slot="header"> <span>服务器信息</span> </div> <el-table size="small" border :data="server.sys" style="width: 100%"> <el-table-column prop="key" label="属性"> </el-table-column> <el-table-column prop="value" label="值"> </el-table-column> </el-table> </el-card> </el-col> </el-row> <el-row> <el-col :span="24"> <el-card> <div slot="header"> <span>Java虚拟机信息</span> </div> <el-table size="small" border :data="server.jvm" style="width: 100%"> <el-table-column prop="key" label="属性"> </el-table-column> <el-table-column prop="value" label="值"> </el-table-column> </el-table> </el-card> </el-col> </el-row> <el-row> <el-col :span="24"> <el-card> <div slot="header"> <span>磁盘状态</span> </div> <div class="sysFile" v-for="(item,index) in server.sysFile" :key="index"> <el-table size="small" border :data="item" style="width: 100%"> <el-table-column prop="key" label="属性"> </el-table-column> <el-table-column prop="value" label="值"> </el-table-column> </el-table> </div> </el-card> </el-col> </el-row> </el-main> </el-container> </div> </body> <script src="js/sockjs.min.js"></script> <script src="js/stomp.js"></script> <script src="https://cdn.bootcss.com/vue/2.5.21/vue.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/element-ui/2.4.11/index.js"></script> <script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.min.js"></script> <script> const wsHost = "http://localhost:8080/demo/notification"; const wsTopic = "/topic/server";
const app = new Vue({ el: '#app', data: function () { return { isConnected: false, stompClient: {}, socket: {}, server: { cpu: [], mem: [], jvm: [], sys: [], sysFile: [] } } }, methods: { _getServerInfo() { axios.get('/demo/server') .then((response) => { this.server = response.data }); }, _initSockJs() { this._getServerInfo(); this.socket = new SockJS(wsHost); this.stompClient = Stomp.over(this.socket);
this.stompClient.connect({}, (frame) => { console.log('websocket连接成功:' + frame); this.isConnected = true; this.$message('websocket服务器连接成功');
this.stompClient.subscribe(wsTopic, (response) => { this.server = JSON.parse(response.body); }); }); }, _destroySockJs() { if (this.stompClient != null) { this.stompClient.disconnect(); this.socket.onclose; this.socket.close(); this.stompClient = {}; this.socket = {}; this.isConnected = false; this.server.cpu = []; this.server.mem = []; this.server.jvm = []; this.server.sys = []; this.server.sysFile = []; } console.log('websocket断开成功!'); this.$message.error('websocket断开成功!'); } }, mounted() { this._initSockJs(); }, beforeDestroy() { this._destroySockJs(); }
}) </script> </html>
|
2. 运行方式
- 启动
SpringBootDemoWebsocketApplication.java
- 访问 http://localhost:8080/demo/server.html
4. 参考
4.1. 后端
- Spring Boot 整合 Websocket 官方文档:https://docs.spring.io/spring/docs/5.1.2.RELEASE/spring-framework-reference/web.html#websocket
- 服务器信息采集 oshi 使用:https://github.com/oshi/oshi
4.2. 前端
- vue.js 语法:https://cn.vuejs.org/v2/guide/
- element-ui 用法:http://element-cn.eleme.io/#/zh-CN
- stomp.js 用法:https://github.com/jmesnil/stomp-websocket
- sockjs 用法:https://github.com/sockjs/sockjs-client
- axios.js 用法:https://github.com/axios/axios#example