寫在前面
此異常非彼異常,標題所說的異常是業務上的異常。
最近做了一個需求,消防的設備巡檢,如果巡檢發現異常,通過手機端提交,后台的實時監控頁面實時獲取到該設備的信息及位置,然后安排員工去處理。
因為需要服務端主動向客戶端發送消息,所以很容易的就想到了用WebSocket來實現這一功能。
WebSocket就不做介紹了,上鏈接:https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket
前端略微復雜,需要在一張位置分布圖上進行鼠標描點定位各個設備和根據不同屏幕大小渲染,本文不做介紹,只是簡單地用頁面樣式進行效果呈現。
綠色代表正常,紅色代表異常
預期效果,未接收到請求前----->id為3的提交了異常,id為3的王五變成了紅色
實現:
前端:
直接貼代碼
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8" /> 5 <title>實時監控</title> 6 </head> 7 <style> 8 .item { 9 display: flex; 10 border-bottom: 1px solid #000000; 11 justify-content: space-between; 12 width: 30%; 13 line-height: 50px; 14 height: 50px; 15 } 16 17 .item span:nth-child(2){ 18 margin-right: 10px; 19 margin-top: 15px; 20 width: 20px; 21 height: 20px; 22 border-radius: 50%; 23 background: #55ff00; 24 } 25 .nowI{ 26 background: #ff0000 !important; 27 } 28 </style> 29 <body> 30 <div id="app"> 31 <div v-for="item in list" class="item"> 32 <span>{{item.id}}.{{item.name}}</span> 33 <span :class='item.state==-1?"nowI":""'></span> 34 </div> 35 </div> 36 </body> 37 <script src="./js/vue.min.js"></script> 38 <script type="text/javascript"> 39 var vm = new Vue({ 40 el: "#app", 41 data: { 42 list: [{ 43 id: 1, 44 name: '張三', 45 state: 1 46 }, 47 { 48 id: 2, 49 name: '李四', 50 state: 1 51 }, 52 { 53 id: 3, 54 name: '王五', 55 state: 1 56 }, 57 { 58 id: 4, 59 name: '韓梅梅', 60 state: 1 61 }, 62 { 63 id: 5, 64 name: '李磊', 65 state: 1 66 }, 67 ] 68 } 69 }) 70 71 var webSocket = null; 72 if ('WebSocket' in window) { 73 //創建WebSocket對象 74 webSocket = new WebSocket("ws://localhost:18801/webSocket/" + getUUID()); 75 76 //連接成功 77 webSocket.onopen = function() { 78 console.log("已連接"); 79 webSocket.send("消息發送測試") 80 } 81 //接收到消息 82 webSocket.onmessage = function(msg) { 83 //處理消息 84 var serverMsg = msg.data; 85 var t_id = parseInt(serverMsg) //服務端發過來的消息,ID,string需轉化為int類型才能比較 86 for (var i = 0; i < vm.list.length; i++) { 87 var item = vm.list[i]; 88 if(item.id == t_id){ 89 item.state = -1; 90 vm.list.splice(i,1,item) 91 break; 92 } 93 } 94 }; 95 96 //關閉事件 97 webSocket.onclose = function() { 98 console.log("websocket已關閉"); 99 }; 100 //發生了錯誤事件 101 webSocket.onerror = function() { 102 console.log("websocket發生了錯誤"); 103 } 104 } else { 105 alert("很遺憾,您的瀏覽器不支持WebSocket!") 106 } 107 108 function getUUID() { //獲取唯一的UUID 109 return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { 110 111 var r = Math.random() * 16 | 0, 112 v = c == 'x' ? r : (r & 0x3 | 0x8); 113 return v.toString(16); 114 }); 115 } 116 </script> 117 </html>
后端:
項目結構是這樣子的,后面的代碼關鍵注釋都有,就不重復描述了
1、新建SpringBoot工程,選擇web和WebSocket依賴
2、配置application.yml
#端口
server:
port: 18801
#密碼,因為接口不需要權限,所以加了個密碼做校驗
mySocket:
myPwd: jae_123
3、WebSocketConfig配置類
1 @Configuration 2 public class WebSocketConfig { 3 4 /** 5 * 注入一個ServerEndpointExporter,該Bean會自動注冊使用@ServerEndpoint注解申明的websocket endpoint 6 */ 7 @Bean 8 public ServerEndpointExporter serverEndpointExporter(){ 9 return new ServerEndpointExporter(); 10 } 11 }
4、WebSocketServer類,用來進行服務端和客戶端之間的交互
1 /** 2 * @author jae 3 * @ServerEndpoint("/webSocket/{uid}") 前端通過此URI與后端建立鏈接 4 */ 5 6 @ServerEndpoint("/webSocket/{uid}") 7 @Component 8 public class WebSocketServer { 9 10 private static Logger log = LoggerFactory.getLogger(WebSocketServer.class); 11 12 //靜態變量,用來記錄當前在線連接數。應該把它設計成線程安全的。 13 private static final AtomicInteger onlineNum = new AtomicInteger(0); 14 15 //concurrent包的線程安全Set,用來存放每個客戶端對應的WebSocketServer對象。 16 private static CopyOnWriteArraySet<Session> sessionPools = new CopyOnWriteArraySet<Session>(); 17 18 /** 19 * 有客戶端連接成功 20 */ 21 @OnOpen 22 public void onOpen(Session session, @PathParam(value = "uid") String uid){ 23 sessionPools.add(session); 24 onlineNum.incrementAndGet(); 25 log.info(uid + "加入webSocket!當前人數為" + onlineNum); 26 } 27 28 /** 29 * 連接關閉調用的方法 30 */ 31 @OnClose 32 public void onClose(Session session) { 33 sessionPools.remove(session); 34 int cnt = onlineNum.decrementAndGet(); 35 log.info("有連接關閉,當前連接數為:{}", cnt); 36 } 37 38 /** 39 * 發送消息 40 */ 41 public void sendMessage(Session session, String message) throws IOException { 42 if(session != null){ 43 synchronized (session) { 44 session.getBasicRemote().sendText(message); 45 } 46 } 47 } 48 49 /** 50 * 群發消息 51 */ 52 public void broadCastInfo(String message) throws IOException { 53 for (Session session : sessionPools) { 54 if(session.isOpen()){ 55 sendMessage(session, message); 56 } 57 } 58 } 59 60 /** 61 * 發生錯誤 62 */ 63 @OnError 64 public void onError(Session session, Throwable throwable){ 65 log.error("發生錯誤"); 66 throwable.printStackTrace(); 67 } 68 69 }
5、WebSocketController類,用於進行接口測試
1 @RestController 2 @RequestMapping("/open/socket") 3 public class WebSocketController { 4 5 @Value("${mySocket.myPwd}") 6 public String myPwd; 7 8 @Autowired 9 private WebSocketServer webSocketServer; 10 11 /** 12 * 手機客戶端請求接口 13 * @param id 發生異常的設備ID 14 * @param pwd 密碼(實際開發記得加密) 15 * @throws IOException 16 */ 17 @PostMapping(value = "/onReceive") 18 public void onReceive(String id,String pwd) throws IOException { 19 if(pwd.equals(myPwd)){ //密碼校驗一致(這里舉例,實際開發還要有個密碼加密的校驗的),則進行群發 20 webSocketServer.broadCastInfo(id); 21 } 22 } 23 24 }
測試
1、打開前端頁面,進行WebSocket連接
控制台輸出,連接成功
2、因為是模擬數據,所以全部顯示正常,沒有異常提交時的頁面呈現
3、接下來,我們用接口測試工具Postman提交一個異常
注意id為3的這個數據的狀態變化
我們可以看到,id為3的王五狀態已經變成異常的了,實時通訊成功。
參考:https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket
最后
工作中有這方面關於實時監控的需求,可以參考一下哦。
不足之處歡迎指出,覺得有用的話就點個推薦吧!