Skip to content

Commit 0d391fd

Browse files
committed
✨ spring-boot-demo-websocket 完成
1 parent 894f73d commit 0d391fd

17 files changed

+598
-33
lines changed

spring-boot-demo-websocket/README.md

+387
Original file line numberDiff line numberDiff line change
@@ -1 +1,388 @@
11
# spring-boot-demo-websocket
2+
3+
> 此 demo 主要演示了 Spring Boot 如何集成 WebSocket,实现后端主动往前端推送数据。网上大部分websocket的例子都是聊天室,本例主要是推送服务器状态信息。前端页面基于vue和element-ui实现。
4+
5+
## 1. 代码
6+
7+
### 1.1. pom.xml
8+
9+
```xml
10+
<?xml version="1.0" encoding="UTF-8"?>
11+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
12+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
13+
<modelVersion>4.0.0</modelVersion>
14+
15+
<artifactId>spring-boot-demo-websocket</artifactId>
16+
<version>1.0.0-SNAPSHOT</version>
17+
18+
<name>spring-boot-demo-websocket</name>
19+
<description>Demo project for Spring Boot</description>
20+
21+
<parent>
22+
<groupId>com.xkcoding</groupId>
23+
<artifactId>spring-boot-demo</artifactId>
24+
<version>1.0.0-SNAPSHOT</version>
25+
</parent>
26+
27+
<properties>
28+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
29+
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
30+
<java.version>1.8</java.version>
31+
<oshi.version>3.9.1</oshi.version>
32+
</properties>
33+
34+
<dependencies>
35+
<dependency>
36+
<groupId>org.springframework.boot</groupId>
37+
<artifactId>spring-boot-starter-web</artifactId>
38+
</dependency>
39+
40+
<dependency>
41+
<groupId>org.springframework.boot</groupId>
42+
<artifactId>spring-boot-starter-websocket</artifactId>
43+
</dependency>
44+
45+
<dependency>
46+
<groupId>org.springframework.boot</groupId>
47+
<artifactId>spring-boot-starter-test</artifactId>
48+
<scope>test</scope>
49+
</dependency>
50+
51+
<dependency>
52+
<groupId>com.github.oshi</groupId>
53+
<artifactId>oshi-core</artifactId>
54+
<version>${oshi.version}</version>
55+
</dependency>
56+
57+
<dependency>
58+
<groupId>cn.hutool</groupId>
59+
<artifactId>hutool-all</artifactId>
60+
</dependency>
61+
62+
<dependency>
63+
<groupId>com.google.guava</groupId>
64+
<artifactId>guava</artifactId>
65+
</dependency>
66+
67+
<dependency>
68+
<groupId>org.projectlombok</groupId>
69+
<artifactId>lombok</artifactId>
70+
<optional>true</optional>
71+
</dependency>
72+
</dependencies>
73+
74+
<build>
75+
<finalName>spring-boot-demo-websocket</finalName>
76+
<plugins>
77+
<plugin>
78+
<groupId>org.springframework.boot</groupId>
79+
<artifactId>spring-boot-maven-plugin</artifactId>
80+
</plugin>
81+
</plugins>
82+
</build>
83+
84+
</project>
85+
```
86+
87+
### 1.2. WebSocketConfig.java
88+
89+
```java
90+
/**
91+
* <p>
92+
* WebSocket配置
93+
* </p>
94+
*
95+
* @package: com.xkcoding.websocket.config
96+
* @description: WebSocket配置
97+
* @author: yangkai.shen
98+
* @date: Created in 2018-12-14 15:58
99+
* @copyright: Copyright (c) 2018
100+
* @version: V1.0
101+
* @modified: yangkai.shen
102+
*/
103+
@Configuration
104+
@EnableWebSocket
105+
@EnableWebSocketMessageBroker
106+
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
107+
108+
@Override
109+
public void registerStompEndpoints(StompEndpointRegistry registry) {
110+
// 注册一个 /notification 端点,前端通过这个端点进行连接
111+
registry.addEndpoint("/notification")
112+
//解决跨域问题
113+
.setAllowedOrigins("*")
114+
.withSockJS();
115+
}
116+
117+
@Override
118+
public void configureMessageBroker(MessageBrokerRegistry registry) {
119+
//定义了一个客户端订阅地址的前缀信息,也就是客户端接收服务端发送消息的前缀信息
120+
registry.enableSimpleBroker("/topic");
121+
}
122+
123+
}
124+
```
125+
126+
### 1.3. 服务器相关实体
127+
128+
> 此部分实体 参见包路径 [com.xkcoding.websocket.model](./src/main/java/com/xkcoding/websocket/model)
129+
130+
### 1.4. ServerTask.java
131+
132+
```java
133+
/**
134+
* <p>
135+
* 服务器定时推送任务
136+
* </p>
137+
*
138+
* @package: com.xkcoding.websocket.task
139+
* @description: 服务器定时推送任务
140+
* @author: yangkai.shen
141+
* @date: Created in 2018-12-14 16:04
142+
* @copyright: Copyright (c) 2018
143+
* @version: V1.0
144+
* @modified: yangkai.shen
145+
*/
146+
@Slf4j
147+
@Component
148+
public class ServerTask {
149+
@Autowired
150+
private SimpMessagingTemplate wsTemplate;
151+
152+
/**
153+
* 按照标准时间来算,每隔 2s 执行一次
154+
*/
155+
@Scheduled(cron = "0/2 * * * * ?")
156+
public void websocket() throws Exception {
157+
log.info("【推送消息】开始执行:{}", DateUtil.formatDateTime(new Date()));
158+
// 查询服务器状态
159+
Server server = new Server();
160+
server.copyTo();
161+
ServerVO serverVO = ServerUtil.wrapServerVO(server);
162+
Dict dict = ServerUtil.wrapServerDict(serverVO);
163+
wsTemplate.convertAndSend(WebSocketConsts.PUSH_SERVER, JSONUtil.toJsonStr(dict));
164+
log.info("【推送消息】执行结束:{}", DateUtil.formatDateTime(new Date()));
165+
}
166+
}
167+
```
168+
169+
### 1.5. server.html
170+
171+
```html
172+
<!DOCTYPE html>
173+
<html lang="en">
174+
<head>
175+
<meta charset="UTF-8">
176+
<title>服务器信息</title>
177+
<link href="https://cdnjs.cloudflare.com/ajax/libs/element-ui/2.4.11/theme-chalk/index.css" rel="stylesheet">
178+
<style>
179+
.el-row {
180+
margin-bottom: 20px;
181+
}
182+
183+
.el-row:last-child {
184+
margin-bottom: 0;
185+
}
186+
187+
.sysFile {
188+
margin-bottom: 5px;
189+
}
190+
191+
.sysFile:last-child {
192+
margin-bottom: 0;
193+
}
194+
</style>
195+
</head>
196+
<body>
197+
<div id="app">
198+
<el-container>
199+
<el-header>
200+
<el-button @click="_initSockJs" type="primary" :disabled="isConnected">手动连接</el-button>
201+
<el-button @click="_destroySockJs" type="danger" :disabled="!isConnected">断开连接</el-button>
202+
</el-header>
203+
<el-main>
204+
<el-row :gutter="20">
205+
<el-col :span="12">
206+
<el-card>
207+
<div slot="header">
208+
<span>CPU信息</span>
209+
</div>
210+
<el-table size="small" border :data="server.cpu" style="width: 100%">
211+
<el-table-column prop="key" label="属性">
212+
</el-table-column>
213+
<el-table-column prop="value" label="">
214+
</el-table-column>
215+
</el-table>
216+
</el-card>
217+
</el-col>
218+
<el-col :span="12">
219+
<el-card>
220+
<div slot="header">
221+
<span>内存信息</span>
222+
</div>
223+
<el-table size="small" border :data="server.mem" style="width: 100%">
224+
<el-table-column prop="key" label="属性">
225+
</el-table-column>
226+
<el-table-column prop="value" label="">
227+
</el-table-column>
228+
</el-table>
229+
</el-card>
230+
</el-col>
231+
</el-row>
232+
<el-row>
233+
<el-col :span="24">
234+
<el-card>
235+
<div slot="header">
236+
<span>服务器信息</span>
237+
</div>
238+
<el-table size="small" border :data="server.sys" style="width: 100%">
239+
<el-table-column prop="key" label="属性">
240+
</el-table-column>
241+
<el-table-column prop="value" label="">
242+
</el-table-column>
243+
</el-table>
244+
</el-card>
245+
</el-col>
246+
</el-row>
247+
<el-row>
248+
<el-col :span="24">
249+
<el-card>
250+
<div slot="header">
251+
<span>Java虚拟机信息</span>
252+
</div>
253+
<el-table size="small" border :data="server.jvm" style="width: 100%">
254+
<el-table-column prop="key" label="属性">
255+
</el-table-column>
256+
<el-table-column prop="value" label="">
257+
</el-table-column>
258+
</el-table>
259+
</el-card>
260+
</el-col>
261+
</el-row>
262+
<el-row>
263+
<el-col :span="24">
264+
<el-card>
265+
<div slot="header">
266+
<span>磁盘状态</span>
267+
</div>
268+
<div class="sysFile" v-for="(item,index) in server.sysFile" :key="index">
269+
<el-table size="small" border :data="item" style="width: 100%">
270+
<el-table-column prop="key" label="属性">
271+
</el-table-column>
272+
<el-table-column prop="value" label="">
273+
</el-table-column>
274+
</el-table>
275+
</div>
276+
</el-card>
277+
</el-col>
278+
</el-row>
279+
</el-main>
280+
</el-container>
281+
</div>
282+
</body>
283+
<script src="js/sockjs.min.js"></script>
284+
<script src="js/stomp.js"></script>
285+
<script src="https://cdn.bootcss.com/vue/2.5.21/vue.min.js"></script>
286+
<script src="https://cdnjs.cloudflare.com/ajax/libs/element-ui/2.4.11/index.js"></script>
287+
<script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.min.js"></script>
288+
<script>
289+
const wsHost = "http://localhost:8080/demo/notification";
290+
const wsTopic = "/topic/server";
291+
292+
const app = new Vue({
293+
el: '#app',
294+
data: function () {
295+
return {
296+
isConnected: false,
297+
stompClient: {},
298+
socket: {},
299+
server: {
300+
cpu: [],
301+
mem: [],
302+
jvm: [],
303+
sys: [],
304+
sysFile: []
305+
}
306+
}
307+
},
308+
methods: {
309+
_getServerInfo() {
310+
axios.get('/demo/server')
311+
.then((response) => {
312+
this.server = response.data
313+
});
314+
},
315+
_initSockJs() {
316+
this._getServerInfo();
317+
this.socket = new SockJS(wsHost);
318+
this.stompClient = Stomp.over(this.socket);
319+
320+
this.stompClient.connect({}, (frame) => {
321+
console.log('websocket连接成功:' + frame);
322+
this.isConnected = true;
323+
this.$message('websocket服务器连接成功');
324+
325+
// 另外再注册一下消息推送
326+
this.stompClient.subscribe(wsTopic, (response) => {
327+
this.server = JSON.parse(response.body);
328+
});
329+
});
330+
},
331+
_destroySockJs() {
332+
if (this.stompClient != null) {
333+
this.stompClient.disconnect();
334+
this.socket.onclose;
335+
this.socket.close();
336+
this.stompClient = {};
337+
this.socket = {};
338+
this.isConnected = false;
339+
this.server.cpu = [];
340+
this.server.mem = [];
341+
this.server.jvm = [];
342+
this.server.sys = [];
343+
this.server.sysFile = [];
344+
}
345+
console.log('websocket断开成功!');
346+
this.$message.error('websocket断开成功!');
347+
}
348+
},
349+
mounted() {
350+
this._initSockJs();
351+
},
352+
beforeDestroy() {
353+
this._destroySockJs();
354+
}
355+
356+
})
357+
</script>
358+
</html>
359+
```
360+
361+
## 2. 运行方式
362+
363+
1. 启动 `SpringBootDemoWebsocketApplication.java`
364+
2. 访问 http://localhost:8080/demo/server.html
365+
366+
## 3. 运行效果
367+
368+
![image-20181217110240322](assets/image-20181217110240322-5015760.png)
369+
370+
![image-20181217110304065](assets/image-20181217110304065-5015784.png)
371+
372+
![image-20181217110328810](assets/image-20181217110328810-5015808.png)
373+
374+
![image-20181217110336017](assets/image-20181217110336017-5015816.png)
375+
376+
## 4. 参考
377+
378+
### 4.1. 后端
379+
380+
1. Spring Boot 整合 Websocket 官方文档:https://docs.spring.io/spring/docs/5.1.2.RELEASE/spring-framework-reference/web.html#websocket
381+
2. 服务器信息采集 oshi 使用:https://github.com/oshi/oshi
382+
383+
### 4.2. 前端
384+
385+
1. vue.js 语法:https://cn.vuejs.org/v2/guide/
386+
2. element-ui 用法:http://element-cn.eleme.io/#/zh-CN
387+
3. stomp.js 用法:https://github.com/jmesnil/stomp-websocket
388+
4. sockjs 用法:https://github.com/sockjs/sockjs-client
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading

0 commit comments

Comments
 (0)