写在前面

此异常非彼异常,标题所说的异常是业务上的异常。

最近做了一个需求,消防的设备巡检,如果巡检发现异常,通过手机端提交,后台的实时监控页面实时获取到该设备的信息及位置,然后安排员工去处理。

因为需要服务端主动向客户端发送消息,所以很容易的就想到了用WebSocket来实现这一功能。

WebSocket就不做介绍了,上链接:https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket

前端略微复杂,需要在一张位置分布图上进行鼠标描点定位各个设备和根据不同屏幕大小渲染,本文不做介绍,只是简单地用页面样式进行效果呈现。

绿色代表正常,红色代表异常

预期效果,未接收到请求前----->id为3的提交了异常,id为3的王五变成了红色

    

实现:

前端:

直接贴代码

  1. 1 <!DOCTYPE html>
  2. 2 <html>
  3. 3 <head>
  4. 4 <meta charset="utf-8" />
  5. 5 <title>实时监控</title>
  6. 6 </head>
  7. 7 <style>
  8. 8 .item {
  9. 9 display: flex;
  10. 10 border-bottom: 1px solid #000000;
  11. 11 justify-content: space-between;
  12. 12 width: 30%;
  13. 13 line-height: 50px;
  14. 14 height: 50px;
  15. 15 }
  16. 16
  17. 17 .item span:nth-child(2){
  18. 18 margin-right: 10px;
  19. 19 margin-top: 15px;
  20. 20 width: 20px;
  21. 21 height: 20px;
  22. 22 border-radius: 50%;
  23. 23 background: #55ff00;
  24. 24 }
  25. 25 .nowI{
  26. 26 background: #ff0000 !important;
  27. 27 }
  28. 28 </style>
  29. 29 <body>
  30. 30 <div id="app">
  31. 31 <div v-for="item in list" class="item">
  32. 32 <span>{{item.id}}.{{item.name}}</span>
  33. 33 <span :class='item.state==-1?"nowI":""'></span>
  34. 34 </div>
  35. 35 </div>
  36. 36 </body>
  37. 37 <script src="./js/vue.min.js"></script>
  38. 38 <script type="text/javascript">
  39. 39 var vm = new Vue({
  40. 40 el: "#app",
  41. 41 data: {
  42. 42 list: [{
  43. 43 id: 1,
  44. 44 name: '张三',
  45. 45 state: 1
  46. 46 },
  47. 47 {
  48. 48 id: 2,
  49. 49 name: '李四',
  50. 50 state: 1
  51. 51 },
  52. 52 {
  53. 53 id: 3,
  54. 54 name: '王五',
  55. 55 state: 1
  56. 56 },
  57. 57 {
  58. 58 id: 4,
  59. 59 name: '韩梅梅',
  60. 60 state: 1
  61. 61 },
  62. 62 {
  63. 63 id: 5,
  64. 64 name: '李磊',
  65. 65 state: 1
  66. 66 },
  67. 67 ]
  68. 68 }
  69. 69 })
  70. 70
  71. 71 var webSocket = null;
  72. 72 if ('WebSocket' in window) {
  73. 73 //创建WebSocket对象
  74. 74 webSocket = new WebSocket("ws://localhost:18801/webSocket/" + getUUID());
  75. 75
  76. 76 //连接成功
  77. 77 webSocket.onopen = function() {
  78. 78 console.log("已连接");
  79. 79 webSocket.send("消息发送测试")
  80. 80 }
  81. 81 //接收到消息
  82. 82 webSocket.onmessage = function(msg) {
  83. 83 //处理消息
  84. 84 var serverMsg = msg.data;
  85. 85 var t_id = parseInt(serverMsg) //服务端发过来的消息,ID,string需转化为int类型才能比较
  86. 86 for (var i = 0; i < vm.list.length; i++) {
  87. 87 var item = vm.list[i];
  88. 88 if(item.id == t_id){
  89. 89 item.state = -1;
  90. 90 vm.list.splice(i,1,item)
  91. 91 break;
  92. 92 }
  93. 93 }
  94. 94 };
  95. 95
  96. 96 //关闭事件
  97. 97 webSocket.onclose = function() {
  98. 98 console.log("websocket已关闭");
  99. 99 };
  100. 100 //发生了错误事件
  101. 101 webSocket.onerror = function() {
  102. 102 console.log("websocket发生了错误");
  103. 103 }
  104. 104 } else {
  105. 105 alert("很遗憾,您的浏览器不支持WebSocket!")
  106. 106 }
  107. 107
  108. 108 function getUUID() { //获取唯一的UUID
  109. 109 return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
  110. 110
  111. 111 var r = Math.random() * 16 | 0,
  112. 112 v = c == 'x' ? r : (r & 0x3 | 0x8);
  113. 113 return v.toString(16);
  114. 114 });
  115. 115 }
  116. 116 </script>
  117. 117 </html>

后端:

项目结构是这样子的,后面的代码关键注释都有,就不重复描述了

1、新建SpringBoot工程,选择web和WebSocket依赖

2、配置application.yml

  1. #端口
  2. server:
  3. port: 18801
  4.  
  5. #密码,因为接口不需要权限,所以加了个密码做校验
  6. mySocket:
  7. myPwd: jae_123

3、WebSocketConfig配置类

  1. 1 @Configuration
  2. 2 public class WebSocketConfig {
  3. 3
  4. 4 /**
  5. 5 * 注入一个ServerEndpointExporter,该Bean会自动注册使用@ServerEndpoint注解申明的websocket endpoint
  6. 6 */
  7. 7 @Bean
  8. 8 public ServerEndpointExporter serverEndpointExporter(){
  9. 9 return new ServerEndpointExporter();
  10. 10 }
  11. 11 }

4、WebSocketServer类,用来进行服务端和客户端之间的交互

  1. 1 /**
  2. 2 * @author jae
  3. 3 * @ServerEndpoint("/webSocket/{uid}") 前端通过此URI与后端建立链接
  4. 4 */
  5. 5
  6. 6 @ServerEndpoint("/webSocket/{uid}")
  7. 7 @Component
  8. 8 public class WebSocketServer {
  9. 9
  10. 10 private static Logger log = LoggerFactory.getLogger(WebSocketServer.class);
  11. 11
  12. 12 //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
  13. 13 private static final AtomicInteger onlineNum = new AtomicInteger(0);
  14. 14
  15. 15 //concurrent包的线程安全Set,用来存放每个客户端对应的WebSocketServer对象。
  16. 16 private static CopyOnWriteArraySet<Session> sessionPools = new CopyOnWriteArraySet<Session>();
  17. 17
  18. 18 /**
  19. 19 * 有客户端连接成功
  20. 20 */
  21. 21 @OnOpen
  22. 22 public void onOpen(Session session, @PathParam(value = "uid") String uid){
  23. 23 sessionPools.add(session);
  24. 24 onlineNum.incrementAndGet();
  25. 25 log.info(uid + "加入webSocket!当前人数为" + onlineNum);
  26. 26 }
  27. 27
  28. 28 /**
  29. 29 * 连接关闭调用的方法
  30. 30 */
  31. 31 @OnClose
  32. 32 public void onClose(Session session) {
  33. 33 sessionPools.remove(session);
  34. 34 int cnt = onlineNum.decrementAndGet();
  35. 35 log.info("有连接关闭,当前连接数为:{}", cnt);
  36. 36 }
  37. 37
  38. 38 /**
  39. 39 * 发送消息
  40. 40 */
  41. 41 public void sendMessage(Session session, String message) throws IOException {
  42. 42 if(session != null){
  43. 43 synchronized (session) {
  44. 44 session.getBasicRemote().sendText(message);
  45. 45 }
  46. 46 }
  47. 47 }
  48. 48
  49. 49 /**
  50. 50 * 群发消息
  51. 51 */
  52. 52 public void broadCastInfo(String message) throws IOException {
  53. 53 for (Session session : sessionPools) {
  54. 54 if(session.isOpen()){
  55. 55 sendMessage(session, message);
  56. 56 }
  57. 57 }
  58. 58 }
  59. 59
  60. 60 /**
  61. 61 * 发生错误
  62. 62 */
  63. 63 @OnError
  64. 64 public void onError(Session session, Throwable throwable){
  65. 65 log.error("发生错误");
  66. 66 throwable.printStackTrace();
  67. 67 }
  68. 68
  69. 69 }

5、WebSocketController类,用于进行接口测试

  1. 1 @RestController
  2. 2 @RequestMapping("/open/socket")
  3. 3 public class WebSocketController {
  4. 4
  5. 5 @Value("${mySocket.myPwd}")
  6. 6 public String myPwd;
  7. 7
  8. 8 @Autowired
  9. 9 private WebSocketServer webSocketServer;
  10. 10
  11. 11 /**
  12. 12 * 手机客户端请求接口
  13. 13 * @param id 发生异常的设备ID
  14. 14 * @param pwd 密码(实际开发记得加密)
  15. 15 * @throws IOException
  16. 16 */
  17. 17 @PostMapping(value = "/onReceive")
  18. 18 public void onReceive(String id,String pwd) throws IOException {
  19. 19 if(pwd.equals(myPwd)){ //密码校验一致(这里举例,实际开发还要有个密码加密的校验的),则进行群发
  20. 20 webSocketServer.broadCastInfo(id);
  21. 21 }
  22. 22 }
  23. 23
  24. 24 }

测试

1、打开前端页面,进行WebSocket连接

控制台输出,连接成功

2、因为是模拟数据,所以全部显示正常,没有异常提交时的页面呈现

3、接下来,我们用接口测试工具Postman提交一个异常

 注意id为3的这个数据的状态变化

我们可以看到,id为3的王五状态已经变成异常的了,实时通讯成功。

参考:https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket

最后

工作中有这方面关于实时监控的需求,可以参考一下哦。

不足之处欢迎指出,觉得有用的话就点个推荐吧!

SpringBoot+WebSocket实时监控异常的更多相关文章

  1. Zookeeper+websocket实现对分布式服务器的实时监控(附源码下载)

    ​ 我就是个封面 Zookeeper简介 Zookeeper是Hadoop的一个子项目,它是分布式系统中的协调系统. 简单来说就是一个Zookeeper注册同步中心,内部结构为一个树形目录,每个节点上 ...

  2. Jmeter实时监控+SpringBoot接口性能实战

    性能测试 Jmeter实时监控+SpringBoot接口性能实战 自动化 SpringBoot Java Jmeter实时监控+SpringBoot接口性能实战 一.实验目的及实验环境 1.1.实验目 ...

  3. SpringBoot 2.0 + InfluxDB+ Sentinel 实时监控数据存储

    前言 阿里巴巴提供的控制台只是用于演示 Sentinel 的基本能力和工作流程,并没有依赖生产环境中所必需的组件,比如持久化的后端数据库.可靠的配置中心等.目前 Sentinel 采用内存态的方式存储 ...

  4. Springboot监控之二:Spring Boot Admin对Springboot服务进行监控

    概述 Spring Boot 监控核心是 spring-boot-starter-actuator 依赖,增加依赖后, Spring Boot 会默认配置一些通用的监控,比如 jvm 监控.类加载.健 ...

  5. 通过micrometer实时监控线程池的各项指标

    通过micrometer实时监控线程池的各项指标 前提 最近的一个项目中涉及到文件上传和下载,使用到JUC的线程池ThreadPoolExecutor,在生产环境中出现了某些时刻线程池满负载运作,由于 ...

  6. Android(Linux)实时监控串口数据

    之前在做WinCE车载方案时,曾做过一个小工具TraceMonitor,用于显示WinCE系统上应用程序的调试信息,特别是在实车调试时,用于监控和显示CAN盒与主机之间的串口数据.因为需要抢占市场先机 ...

  7. 如何做实时监控?—— 参考 Spring Boot 实现

    随着 微服务 的流行,相比较以前一个大型应用程序搞定所有需求,我们现在更倾向于把大型应用程序切分成多个微服务,服务之间通过 RPC 调用.微服务架构的好处非常多,例如稳定的服务变化较少,不会被非稳定服 ...

  8. 改造断路器集群监控Hystrix Turbine实现自动注册消费者、实时监控多个服务

    在上一篇文章中,我们搭建了Hystrix Dashoard,对指定接口进行监控.但是只能对一个接口进行监听,功能比较局限: Turbine:汇总系统内多个服务的数据并显示到 Hystrix Dashb ...

  9. 虎牙数万主播同时在线直播的秘密,CDN推流日志上行实时监控

    6 月 10 日,又拍云 Open Talk | 2018 音视频技术沙龙·深圳站 顺利落幕,来自虎牙的直播运维研发架构师张波在沙龙上做了<基于CDN推流日志的主播上行实时监控及其自动化解密&g ...

随机推荐

  1. hdfs中数据迁移

    1.hdfs集群间数据迁移 hadoop distcp hdfs://192.128.112.66:8020/user/hive/warehouse/data.db/dwi_xxxx_d  /user ...

  2. [题解] SPOJ GSS1 - Can you answer these queries I

    [题解] SPOJ GSS1 - Can you answer these queries I · 题目大意 要求维护一段长度为 \(n\) 的静态序列的区间最大子段和. 有 \(m\) 次询问,每次 ...

  3. 5M1E,软件质量管理最佳解决方案

    - 如何做好一个产品? - 用户.需求.文化.价值.设计.流程,这些因素缺一不可.- 那么,如何做好产品的质量管理?- 人.机器.物料.方法.环境.测量,这些因素同样缺一不可.能够影响产品质量波动的因 ...

  4. 微信小程序基础知识笔记

    微信小程序笔记 文件构成 全局文件 app.json 小程序全局配置文件,必要,自动生成 app.js 小程序入口JS文件,一般只需申明全局变量.处理生命周期以及版本升级即可,必要 app.wxss ...

  5. Java clone() 方法克隆对象——深拷贝与浅拷贝

    基本数据类型引用数据类型特点 1.基本数据类型的特点:直接存储在栈(stack)中的数据 2.引用数据类型的特点:存储的是该对象在栈中引用,真实的数据存放在堆内存里 引用数据类型在栈中存储了指针,该指 ...

  6. spring boot 系列之七:SpringBoot整合Mybatis

    springboot已经很流行,但是它仍需要搭配一款ORM框架来实现数据的CRUD,之前已经分享过JdbcTemplete和JPA的整合,本次分享下Mybatis的整合. 对于mybatis的使用,需 ...

  7. Django中使用MySQL数据库的连接配置

    1. 安装pymysql pip install pymysql 2. 导入 # 在与 settings.py 同级目录下的 __init__.py 中引入模块和进行配置 import pymysql ...

  8. 20210811 Dove 打扑克,Cicada 与排序,Cicada 拿衣服

    考场 开考感觉 T3 比较可做.T1 看上去不难但毫无思路. 先想了 25min T3,想到一个确定左端点,二分最长的右端点,甚至想到了用猫树维护区间 or and...上厕所回来发现假了,没有单调性 ...

  9. IPsec NAT-T说明和环境搭建

    1. IPsec与NAT的关系 NAT作为一个IPV4的地址转换协议,它最初的目的是用来最解决IPv4地址不足的问题.通过NAT协议,局域网内的多个主机可以共同使用一个公网地址,这在很大程度上减轻了I ...

  10. JS014. toFixed( )调试踩坑 - 浏览器思维 点常量 & 点运算符

    Number.prototype.toFixed( ) 在观察toFixed()丢失精度问题,和对toFixed()方法重写的调试过程时,发现toFixed()对Number的识别有它自己的规则,并找 ...