使用spring boot +WebSocket实现(后台主动)消息推送 (一)

最近有一个优化需求,将浏览器轮询获取后台消息改成实时获取并且不轮询,轮询太膈应了。

然后我就找各种资料,一开始同事推荐dwz,后来发现不太适用于目前的项目(也许能实现只是我不知道如何实现)。

后来了解到WebSocket,网上看了很多文档都是类似聊天室的场景,并不适用。在此,我主要介绍下 服务器主动推送,由服务端来触发而不是浏览器端发起。

WebSocket 主要能实现的场景:

1、网页聊天室

2、服务器消息实时通知

WebSocket 使用方法应该有很多,在次介绍下使用  spring boot(集成了tomcat8)+h5 环境下的实现。

话不多说,直接上代码,想深入了解WebSocket 的请查阅相关介绍。

1.pom

[html]  view plain  copy
  1. <dependency>  
  2.            <groupId>org.springframework.boot</groupId>  
  3.            <artifactId>spring-boot-starter-websocket</artifactId>  
  4.        </dependency>  
2. 使用@ServerEndpoint创立websocket endpoint

[java]  view plain  copy
  1. @Configuration  
  2. public class WebSocketConfig {  
  3.     @Bean  
  4.     public ServerEndpointExporter serverEndpointExporter() {  
  5.         return new ServerEndpointExporter();  
  6.     }  
  7.   
  8. }  
3.具体实现类 可自己选择url要不要带参数
[java]  view plain  copy
  1. package com.star.manager.service;  
  2.   
  3. import java.io.IOException;  
  4. import java.util.concurrent.CopyOnWriteArraySet;  
  5.   
  6. import javax.websocket.OnClose;  
  7. import javax.websocket.OnError;  
  8. import javax.websocket.OnMessage;  
  9. import javax.websocket.OnOpen;  
  10. import javax.websocket.Session;  
  11. import javax.websocket.server.ServerEndpoint;  
  12.   
  13. import lombok.extern.slf4j.Slf4j;  
  14.   
  15. import org.springframework.stereotype.Component;  
  16. @Slf4j  
  17. //@ServerEndpoint("/websocket/{user}")  
  18. @ServerEndpoint(value = "/websocket")  
  19. <strong>@Component</strong>  
  20. public class WebSocketServer {  
  21.     //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。  
  22.     private static int onlineCount = 0;  
  23.     //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。  
  24.    <strong> </strong>private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();  
  25.   
  26.     //与某个客户端的连接会话,需要通过它来给客户端发送数据  
  27.     private Session session;  
  28.   
  29.     /** 
  30.      * 连接建立成功调用的方法*/  
  31.     @OnOpen  
  32.     public void onOpen(Session session) {  
  33.         this.session = session;  
  34.         webSocketSet.add(this);     //加入set中  
  35.         addOnlineCount();           //在线数加1  
  36.         log.info("有新连接加入!当前在线人数为" + getOnlineCount());  
  37.         try {  
  38.              sendMessage("连接成功");  
  39.         } catch (IOException e) {  
  40.             log.error("websocket IO异常");  
  41.         }  
  42.     }  
  43.     //  //连接打开时执行  
  44.     //  @OnOpen  
  45.     //  public void onOpen(@PathParam("user") String user, Session session) {  
  46.     //      currentUser = user;  
  47.     //      System.out.println("Connected ... " + session.getId());  
  48.     //  }  
  49.   
  50.     /** 
  51.      * 连接关闭调用的方法 
  52.      */  
  53.     @OnClose  
  54.     public void onClose() {  
  55.         webSocketSet.remove(this);  //从set中删除  
  56.         subOnlineCount();           //在线数减1  
  57.         log.info("有一连接关闭!当前在线人数为" + getOnlineCount());  
  58.     }  
  59.   
  60.     /** 
  61.      * 收到客户端消息后调用的方法 
  62.      * 
  63.      * @param message 客户端发送过来的消息*/  
  64.     @OnMessage  
  65.     public void onMessage(String message, Session session) {  
  66.         log.info("来自客户端的消息:" + message);  
  67.   
  68.         //群发消息  
  69.         for (WebSocketServer item : webSocketSet) {  
  70.             try {  
  71.                 item.sendMessage(message);  
  72.             } catch (IOException e) {  
  73.                 e.printStackTrace();  
  74.             }  
  75.         }  
  76.     }  
  77.   
  78.     /** 
  79.      *  
  80.      * @param session 
  81.      * @param error 
  82.      */  
  83.     @OnError  
  84.     public void onError(Session session, Throwable error) {  
  85.         log.error("发生错误");  
  86.         error.printStackTrace();  
  87.     }  
  88.   
  89.   
  90. <strong>    public void sendMessage(String message) throws IOException {  
  91.         this.session.getBasicRemote().sendText(message);  
  92.     }</strong><strong>  
  93.   
  94.   
  95.     /** 
  96.      * 群发自定义消息 
  97.      * */  
  98.     public static void sendInfo(String message) throws IOException {  
  99.         log.info(message);  
  100.         for (WebSocketServer item : webSocketSet) {  
  101.             try {  
  102.                 item.sendMessage(message);  
  103.             } catch (IOException e) {  
  104.                 continue;  
  105.             }  
  106.         }  
  107.     }</strong>  
  108.   
  109.     public static synchronized int getOnlineCount() {  
  110.         return onlineCount;  
  111.     }  
  112.   
  113.     public static synchronized void addOnlineCount() {  
  114.         WebSocketServer.onlineCount++;  
  115.     }  
  116.   
  117.     public static synchronized void subOnlineCount() {  
  118.         WebSocketServer.onlineCount--;  
  119.     }  
  120. }  

后台触发有多种,controller,定时任务,mq等,这贴一个controller代码

[java]  view plain  copy
  1. @RequestMapping(value="/pushVideoListToWeb",method=RequestMethod.POST,consumes = "application/json")  
  2. public @ResponseBody Map<String,Object> pushVideoListToWeb(@RequestBody Map<String,Object> param) {  
  3.  Map<String,Object> result =new HashMap<String,Object>();  
  4.    
  5.  try {  
  6.      WebSocketServer.sendInfo("有新客户呼入,sltAccountId:"+CommonUtils.getValue(param, "sltAccountId"));  
  7.      result.put("operationResult"true);  
  8.  }catch (IOException e) {  
  9.      result.put("operationResult"true);  
  10.  }  
  11.  return result;  
  12. }  


重要的地方我都加粗了,主要是这段,使用这个方法,可以实现服务器主动推送。

[java]  view plain  copy
  1. public void sendMessage(String message) throws IOException {  
  2.     this.session.getBasicRemote().sendText(message);  
  3. }  
简单说说:

当有新连接进来,记录在静态成员变量webSocketSet里面,相当于一个缓存。后台服务调用sendMessage或sendInfo方法,即可向已登录的客户端推送消息。

4.js(html就不写了,谁边找个能触发这个js的就可以)

[javascript]  view plain  copy
  1. socket = new WebSocket("ws://localhost:9094/starManager/websocket/张三");  
  2.         var socket;  
  3.         if(typeof(WebSocket) == "undefined") {  
  4.             console.log("您的浏览器不支持WebSocket");  
  5.         }else{  
  6.             console.log("您的浏览器支持WebSocket");  
  7.               
  8.             //实现化WebSocket对象,指定要连接的服务器地址与端口  
  9.             //socket = new WebSocket("ws://localhost:9094/starManager/websocket/张三");  
  10.             socket = new WebSocket("ws://localhost:9094/starManager/websocket");  
  11.             //打开事件  
  12.             socket.onopen = function() {  
  13.                 console.log("Socket 已打开");  
  14.                 //socket.send("这是来自客户端的消息" + location.href + new Date());  
  15.             };  
  16.             //获得消息事件  
  17.             socket.onmessage = function(msg) {  
  18.                 console.log(msg.data);  
  19.                 //发现消息进入    调后台获取  
  20.                 getCallingList();  
  21.             };  
  22.             //关闭事件  
  23.             socket.onclose = function() {  
  24.                 console.log("Socket已关闭");  
  25.             };  
  26.             //发生了错误事件  
  27.             socket.onerror = function() {  
  28.                 alert("Socket发生了错误");  
  29.             }  
  30.              $(window).unload(function(){  
  31.                   socket.close();  
  32.                 });  
  33.   
  34. //                                  $("#btnSend").click(function() {  
  35. //                                      socket.send("这是来自客户端的消息" + location.href + new Date());  
  36. //                                  });  
  37. //  
  38. //                                  $("#btnClose").click(function() {  
  39. //                                      socket.close();  
  40. //                                  });  
  41.         }  
  42.                                   
代码就这么多,我的用这些代码就跑的起来。做的时候出现过页面报404等错误,如果也是spring boot+h5,仔细核对下和我代码有无区别,加配置 路径是有ok,问题应该不大。


如果你恰好也有可以用WebSocket实现的类似场景,希望对你有帮助。我实现的这个功能,当然也是网上看资料学习的,得到很多同行的帮助,在次只是整理下,突出主动推送也能实现(之前认为这个只能实现聊天室),再次感谢帮助过我的

同行们,祝你们在写bug路上,越走越远。。。


猜你喜欢

转载自blog.csdn.net/w_wensan/article/details/79961379