@@ -1 +1,388 @@ | |||||
# spring-boot-demo-websocket | # spring-boot-demo-websocket | ||||
> 此 demo 主要演示了 Spring Boot 如何集成 WebSocket,实现后端主动往前端推送数据。网上大部分websocket的例子都是聊天室,本例主要是推送服务器状态信息。前端页面基于vue和element-ui实现。 | |||||
## 1. 代码 | |||||
### 1.1. pom.xml | |||||
```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"> | |||||
<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 | |||||
```java | |||||
/** | |||||
* <p> | |||||
* WebSocket配置 | |||||
* </p> | |||||
* | |||||
* @package: com.xkcoding.websocket.config | |||||
* @description: WebSocket配置 | |||||
* @author: yangkai.shen | |||||
* @date: Created in 2018-12-14 15:58 | |||||
* @copyright: Copyright (c) 2018 | |||||
* @version: V1.0 | |||||
* @modified: yangkai.shen | |||||
*/ | |||||
@Configuration | |||||
@EnableWebSocket | |||||
@EnableWebSocketMessageBroker | |||||
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { | |||||
@Override | |||||
public void registerStompEndpoints(StompEndpointRegistry registry) { | |||||
// 注册一个 /notification 端点,前端通过这个端点进行连接 | |||||
registry.addEndpoint("/notification") | |||||
//解决跨域问题 | |||||
.setAllowedOrigins("*") | |||||
.withSockJS(); | |||||
} | |||||
@Override | |||||
public void configureMessageBroker(MessageBrokerRegistry registry) { | |||||
//定义了一个客户端订阅地址的前缀信息,也就是客户端接收服务端发送消息的前缀信息 | |||||
registry.enableSimpleBroker("/topic"); | |||||
} | |||||
} | |||||
``` | |||||
### 1.3. 服务器相关实体 | |||||
> 此部分实体 参见包路径 [com.xkcoding.websocket.model](./src/main/java/com/xkcoding/websocket/model) | |||||
### 1.4. ServerTask.java | |||||
```java | |||||
/** | |||||
* <p> | |||||
* 服务器定时推送任务 | |||||
* </p> | |||||
* | |||||
* @package: com.xkcoding.websocket.task | |||||
* @description: 服务器定时推送任务 | |||||
* @author: yangkai.shen | |||||
* @date: Created in 2018-12-14 16:04 | |||||
* @copyright: Copyright (c) 2018 | |||||
* @version: V1.0 | |||||
* @modified: yangkai.shen | |||||
*/ | |||||
@Slf4j | |||||
@Component | |||||
public class ServerTask { | |||||
@Autowired | |||||
private SimpMessagingTemplate wsTemplate; | |||||
/** | |||||
* 按照标准时间来算,每隔 2s 执行一次 | |||||
*/ | |||||
@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 | |||||
```html | |||||
<!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. 运行方式 | |||||
1. 启动 `SpringBootDemoWebsocketApplication.java` | |||||
2. 访问 http://localhost:8080/demo/server.html | |||||
## 3. 运行效果 | |||||
 | |||||
 | |||||
 | |||||
 | |||||
## 4. 参考 | |||||
### 4.1. 后端 | |||||
1. Spring Boot 整合 Websocket 官方文档:https://docs.spring.io/spring/docs/5.1.2.RELEASE/spring-framework-reference/web.html#websocket | |||||
2. 服务器信息采集 oshi 使用:https://github.com/oshi/oshi | |||||
### 4.2. 前端 | |||||
1. vue.js 语法:https://cn.vuejs.org/v2/guide/ | |||||
2. element-ui 用法:http://element-cn.eleme.io/#/zh-CN | |||||
3. stomp.js 用法:https://github.com/jmesnil/stomp-websocket | |||||
4. sockjs 用法:https://github.com/sockjs/sockjs-client |
@@ -0,0 +1,36 @@ | |||||
package com.xkcoding.websocket.controller; | |||||
import cn.hutool.core.lang.Dict; | |||||
import com.xkcoding.websocket.model.Server; | |||||
import com.xkcoding.websocket.payload.ServerVO; | |||||
import com.xkcoding.websocket.util.ServerUtil; | |||||
import org.springframework.web.bind.annotation.GetMapping; | |||||
import org.springframework.web.bind.annotation.RequestMapping; | |||||
import org.springframework.web.bind.annotation.RestController; | |||||
/** | |||||
* <p> | |||||
* 服务器监控Controller | |||||
* </p> | |||||
* | |||||
* @package: com.xkcoding.websocket.controller | |||||
* @description: 服务器监控Controller | |||||
* @author: yangkai.shen | |||||
* @date: Created in 2018-12-17 10:22 | |||||
* @copyright: Copyright (c) 2018 | |||||
* @version: V1.0 | |||||
* @modified: yangkai.shen | |||||
*/ | |||||
@RestController | |||||
@RequestMapping("/server") | |||||
public class ServerController { | |||||
@GetMapping | |||||
public Dict serverInfo() throws Exception { | |||||
Server server = new Server(); | |||||
server.copyTo(); | |||||
ServerVO serverVO = ServerUtil.wrapServerVO(server); | |||||
return ServerUtil.wrapServerDict(serverVO); | |||||
} | |||||
} |
@@ -28,10 +28,10 @@ public class CpuVO { | |||||
CpuVO vo = new CpuVO(); | CpuVO vo = new CpuVO(); | ||||
vo.data.add(new KV("核心数", cpu.getCpuNum())); | vo.data.add(new KV("核心数", cpu.getCpuNum())); | ||||
vo.data.add(new KV("CPU总的使用率", cpu.getTotal())); | vo.data.add(new KV("CPU总的使用率", cpu.getTotal())); | ||||
vo.data.add(new KV("CPU系统使用率", cpu.getSys())); | |||||
vo.data.add(new KV("CPU用户使用率", cpu.getUsed())); | |||||
vo.data.add(new KV("CPU当前等待率", cpu.getWait())); | |||||
vo.data.add(new KV("CPU当前空闲率", cpu.getFree())); | |||||
vo.data.add(new KV("CPU系统使用率", cpu.getSys() + "%")); | |||||
vo.data.add(new KV("CPU用户使用率", cpu.getUsed() + "%")); | |||||
vo.data.add(new KV("CPU当前等待率", cpu.getWait() + "%")); | |||||
vo.data.add(new KV("CPU当前空闲率", cpu.getFree() + "%")); | |||||
return vo; | return vo; | ||||
} | } | ||||
} | } |
@@ -26,9 +26,10 @@ public class JvmVO { | |||||
public static JvmVO create(Jvm jvm) { | public static JvmVO create(Jvm jvm) { | ||||
JvmVO vo = new JvmVO(); | JvmVO vo = new JvmVO(); | ||||
vo.data.add(new KV("当前JVM占用的内存总数(M)", jvm.getTotal())); | |||||
vo.data.add(new KV("JVM最大可用内存总数(M)", jvm.getMax())); | |||||
vo.data.add(new KV("JVM空闲内存(M)", jvm.getFree())); | |||||
vo.data.add(new KV("当前JVM占用的内存总数(M)", jvm.getTotal() + "M")); | |||||
vo.data.add(new KV("JVM最大可用内存总数(M)", jvm.getMax() + "M")); | |||||
vo.data.add(new KV("JVM空闲内存(M)", jvm.getFree() + "M")); | |||||
vo.data.add(new KV("JVM使用率", jvm.getUsage() + "%")); | |||||
vo.data.add(new KV("JDK版本", jvm.getVersion())); | vo.data.add(new KV("JDK版本", jvm.getVersion())); | ||||
vo.data.add(new KV("JDK路径", jvm.getHome())); | vo.data.add(new KV("JDK路径", jvm.getHome())); | ||||
vo.data.add(new KV("JDK启动时间", jvm.getStartTime())); | vo.data.add(new KV("JDK启动时间", jvm.getStartTime())); | ||||
@@ -26,9 +26,10 @@ public class MemVO { | |||||
public static MemVO create(Mem mem) { | public static MemVO create(Mem mem) { | ||||
MemVO vo = new MemVO(); | MemVO vo = new MemVO(); | ||||
vo.data.add(new KV("内存总量", mem.getTotal())); | |||||
vo.data.add(new KV("已用内存", mem.getUsed())); | |||||
vo.data.add(new KV("剩余内存", mem.getFree())); | |||||
vo.data.add(new KV("内存总量", mem.getTotal() + "G")); | |||||
vo.data.add(new KV("已用内存", mem.getUsed() + "G")); | |||||
vo.data.add(new KV("剩余内存", mem.getFree() + "G")); | |||||
vo.data.add(new KV("使用率", mem.getUsage() + "%")); | |||||
return vo; | return vo; | ||||
} | } | ||||
} | } |
@@ -35,7 +35,7 @@ public class SysFileVO { | |||||
item.add(new KV("总大小", sysFile.getTotal())); | item.add(new KV("总大小", sysFile.getTotal())); | ||||
item.add(new KV("剩余大小", sysFile.getFree())); | item.add(new KV("剩余大小", sysFile.getFree())); | ||||
item.add(new KV("已经使用量", sysFile.getUsed())); | item.add(new KV("已经使用量", sysFile.getUsed())); | ||||
item.add(new KV("资源的使用率", sysFile.getUsage())); | |||||
item.add(new KV("资源的使用率", sysFile.getUsage() + "%")); | |||||
vo.data.add(item); | vo.data.add(item); | ||||
} | } | ||||
@@ -1,10 +1,12 @@ | |||||
package com.xkcoding.websocket.task; | package com.xkcoding.websocket.task; | ||||
import cn.hutool.core.date.DateUtil; | import cn.hutool.core.date.DateUtil; | ||||
import cn.hutool.core.lang.Dict; | |||||
import cn.hutool.json.JSONUtil; | import cn.hutool.json.JSONUtil; | ||||
import com.xkcoding.websocket.common.WebSocketConsts; | import com.xkcoding.websocket.common.WebSocketConsts; | ||||
import com.xkcoding.websocket.model.Server; | import com.xkcoding.websocket.model.Server; | ||||
import com.xkcoding.websocket.payload.ServerVO; | import com.xkcoding.websocket.payload.ServerVO; | ||||
import com.xkcoding.websocket.util.ServerUtil; | |||||
import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||
import org.springframework.beans.factory.annotation.Autowired; | import org.springframework.beans.factory.annotation.Autowired; | ||||
import org.springframework.messaging.simp.SimpMessagingTemplate; | import org.springframework.messaging.simp.SimpMessagingTemplate; | ||||
@@ -33,17 +35,17 @@ public class ServerTask { | |||||
private SimpMessagingTemplate wsTemplate; | private SimpMessagingTemplate wsTemplate; | ||||
/** | /** | ||||
* 按照标准时间来算,每隔 10s 执行一次 | |||||
* 按照标准时间来算,每隔 2s 执行一次 | |||||
*/ | */ | ||||
@Scheduled(cron = "0/10 * * * * ?") | |||||
@Scheduled(cron = "0/2 * * * * ?") | |||||
public void websocket() throws Exception { | public void websocket() throws Exception { | ||||
log.info("【推送消息】开始执行:{}", DateUtil.formatDateTime(new Date())); | log.info("【推送消息】开始执行:{}", DateUtil.formatDateTime(new Date())); | ||||
// 查询服务器状态 | // 查询服务器状态 | ||||
Server server = new Server(); | Server server = new Server(); | ||||
server.copyTo(); | server.copyTo(); | ||||
ServerVO serverVO = new ServerVO(); | |||||
serverVO.create(server); | |||||
wsTemplate.convertAndSend(WebSocketConsts.PUSH_SERVER, JSONUtil.toJsonStr(serverVO)); | |||||
ServerVO serverVO = ServerUtil.wrapServerVO(server); | |||||
Dict dict = ServerUtil.wrapServerDict(serverVO); | |||||
wsTemplate.convertAndSend(WebSocketConsts.PUSH_SERVER, JSONUtil.toJsonStr(dict)); | |||||
log.info("【推送消息】执行结束:{}", DateUtil.formatDateTime(new Date())); | log.info("【推送消息】执行结束:{}", DateUtil.formatDateTime(new Date())); | ||||
} | } | ||||
} | } |
@@ -0,0 +1,48 @@ | |||||
package com.xkcoding.websocket.util; | |||||
import cn.hutool.core.lang.Dict; | |||||
import com.xkcoding.websocket.model.Server; | |||||
import com.xkcoding.websocket.payload.ServerVO; | |||||
/** | |||||
* <p> | |||||
* 服务器转换工具类 | |||||
* </p> | |||||
* | |||||
* @package: com.xkcoding.websocket.util | |||||
* @description: 服务器转换工具类 | |||||
* @author: yangkai.shen | |||||
* @date: Created in 2018-12-17 10:24 | |||||
* @copyright: Copyright (c) 2018 | |||||
* @version: V1.0 | |||||
* @modified: yangkai.shen | |||||
*/ | |||||
public class ServerUtil { | |||||
/** | |||||
* 包装成 ServerVO | |||||
* | |||||
* @param server server | |||||
* @return ServerVO | |||||
*/ | |||||
public static ServerVO wrapServerVO(Server server) { | |||||
ServerVO serverVO = new ServerVO(); | |||||
serverVO.create(server); | |||||
return serverVO; | |||||
} | |||||
/** | |||||
* 包装成 Dict | |||||
* | |||||
* @param serverVO serverVO | |||||
* @return Dict | |||||
*/ | |||||
public static Dict wrapServerDict(ServerVO serverVO) { | |||||
Dict dict = Dict.create() | |||||
.set("cpu", serverVO.getCpu().get(0).getData()) | |||||
.set("mem", serverVO.getMem().get(0).getData()) | |||||
.set("sys", serverVO.getSys().get(0).getData()) | |||||
.set("jvm", serverVO.getJvm().get(0).getData()) | |||||
.set("sysFile", serverVO.getSysFile().get(0).getData()); | |||||
return dict; | |||||
} | |||||
} |
@@ -3,24 +3,68 @@ | |||||
<head> | <head> | ||||
<meta charset="UTF-8"> | <meta charset="UTF-8"> | ||||
<title>服务器信息</title> | <title>服务器信息</title> | ||||
<!-- import CSS --> | |||||
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css"> | |||||
<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> | </head> | ||||
<body> | <body> | ||||
<div id="app"> | <div id="app"> | ||||
<el-container> | <el-container> | ||||
<el-header> | <el-header> | ||||
<el-button @click="_initSockJs">手动连接</el-button> | |||||
<el-button @click="_destroySockJs" danger>断开连接</el-button> | |||||
<el-button @click="_initSockJs" type="primary" :disabled="isConnected">手动连接</el-button> | |||||
<el-button @click="_destroySockJs" type="danger" :disabled="!isConnected">断开连接</el-button> | |||||
</el-header> | </el-header> | ||||
<el-main> | <el-main> | ||||
<el-row v-if="server.cpu.length > 0"> | |||||
<el-col :span="16" :offset="4"> | |||||
<el-card class="box-card"> | |||||
<el-row :gutter="20"> | |||||
<el-col :span="12"> | |||||
<el-card> | |||||
<div slot="header"> | <div slot="header"> | ||||
<span>CPU信息</span> | <span>CPU信息</span> | ||||
</div> | </div> | ||||
<el-table :data="server.cpu[0].data" style="width: 100%"> | |||||
<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 prop="key" label="属性"> | ||||
</el-table-column> | </el-table-column> | ||||
<el-table-column prop="value" label="值"> | <el-table-column prop="value" label="值"> | ||||
@@ -29,16 +73,47 @@ | |||||
</el-card> | </el-card> | ||||
</el-col> | </el-col> | ||||
</el-row> | </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-main> | ||||
</el-container> | </el-container> | ||||
</div> | </div> | ||||
</body> | </body> | ||||
<script src="js/sockjs.min.js"></script> | <script src="js/sockjs.min.js"></script> | ||||
<script src="js/stomp.js"></script> | <script src="js/stomp.js"></script> | ||||
<!-- import Vue before Element --> | |||||
<script src="https://unpkg.com/vue/dist/vue.js"></script> | |||||
<!-- import JavaScript --> | |||||
<script src="https://unpkg.com/element-ui/lib/index.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> | <script> | ||||
const wsHost = "http://localhost:8080/demo/notification"; | const wsHost = "http://localhost:8080/demo/notification"; | ||||
const wsTopic = "/topic/server"; | const wsTopic = "/topic/server"; | ||||
@@ -47,29 +122,38 @@ | |||||
el: '#app', | el: '#app', | ||||
data: function () { | data: function () { | ||||
return { | return { | ||||
isConnected: false, | |||||
stompClient: {}, | stompClient: {}, | ||||
socket: {}, | socket: {}, | ||||
server: { | server: { | ||||
cpu: [], | cpu: [], | ||||
jvm: [], | |||||
mem: [], | mem: [], | ||||
sysFile: [], | |||||
sys: [] | |||||
jvm: [], | |||||
sys: [], | |||||
sysFile: [] | |||||
} | } | ||||
} | } | ||||
}, | }, | ||||
methods: { | methods: { | ||||
_getServerInfo() { | |||||
axios.get('/demo/server') | |||||
.then((response) => { | |||||
this.server = response.data | |||||
}); | |||||
}, | |||||
_initSockJs() { | _initSockJs() { | ||||
this._getServerInfo(); | |||||
this.socket = new SockJS(wsHost); | this.socket = new SockJS(wsHost); | ||||
this.stompClient = Stomp.over(this.socket); | this.stompClient = Stomp.over(this.socket); | ||||
this.stompClient.connect({}, (frame) => { | this.stompClient.connect({}, (frame) => { | ||||
console.log('websocket连接成功:' + frame); | console.log('websocket连接成功:' + frame); | ||||
this.isConnected = true; | |||||
this.$message('websocket服务器连接成功'); | this.$message('websocket服务器连接成功'); | ||||
// 另外再注册一下消息推送 | // 另外再注册一下消息推送 | ||||
this.stompClient.subscribe(wsTopic, function (response) { | |||||
this.server = JSON.parse(response); | |||||
this.stompClient.subscribe(wsTopic, (response) => { | |||||
this.server = JSON.parse(response.body); | |||||
}); | }); | ||||
}); | }); | ||||
}, | }, | ||||
@@ -80,6 +164,12 @@ | |||||
this.socket.close(); | this.socket.close(); | ||||
this.stompClient = {}; | this.stompClient = {}; | ||||
this.socket = {}; | this.socket = {}; | ||||
this.isConnected = false; | |||||
this.server.cpu = []; | |||||
this.server.mem = []; | |||||
this.server.jvm = []; | |||||
this.server.sys = []; | |||||
this.server.sysFile = []; | |||||
} | } | ||||
console.log('websocket断开成功!'); | console.log('websocket断开成功!'); | ||||
this.$message.error('websocket断开成功!'); | this.$message.error('websocket断开成功!'); | ||||