SpringBoot+WebSocket实现消息推送功能

 

背景

项目中经常会用到消息推送功能,关于推送技术的实现,我们通常会联想到轮询、comet长连接技术,虽然这些技术能够实现,但是需要反复连接,对于服务资源消耗过大,随着技术的发展,HtML5定义了WebSocket协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。本文将介绍如何采用websocket实现消息推送。

 

WebSocket简介

WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端。浏览器和服务器仅需一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

 

协议原理

Websocket协议基于Http协议,针对Http协议进行了相关的改善,且Websocket协议也需要建立TCP连接来实现数据传输,具体实现如下图:

说明:

  • 客户端发起http请求,经过3次握手后,建立起TCP连接;http请求里存放WebSocket支持的版本号等信息,如:Upgrade、Connection、WebSocket-Version等。
  • 服务器收到客户端的握手请求后,同样采用HTTP协议回馈数据
  • 客户端收到连接成功的消息后,开始借助于TCP传输信道进行全双工通信.

 

WebSocket与HTTP协议的区别

相同点:都是一样基于TCP的,都是可靠性传输协议。都是应用层协议。

不同点:

  • WebSocket是双向通信协议,可以双向发送或接受信息,而HTTP是单向协议
  • WebSocket需要浏览器和服务器握手进行建立连接的,而http是浏览器发起向服务器的连接。

 

WebSocket特点

  • 建立在TCP协议之上,服务器端的实现比较容易。
  • 数据格式比较轻量,性能开销小,通信高效。
  • 支持多种数据格式,可以发送文本、二进制数据。
  • 客户端可以与任意服务器通信,无同源限制。

 

应用场景

  • 即时聊天通信
  • 在线协同编辑/编辑
  • 实时数据流的拉取与推送
  • 实时地图位置

 

系统集成Websocket

jar包引入

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.2.4.RELEASE</version>
  <relativePath/>
</parent>

<dependency>
    <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

Websocket配置

@Configuration
public class WebSocketConfig
{
  @Bean
  public ServerEndpointExporter serverEndpointExporter() {
      return new ServerEndpointExporter();
  }
}

具体实现

@ServerEndpoint(value="/websocket/{uid}")
@Component
public class WebSocketServer
{
  private Logger logger = LoggerFactory.getLogger(WebSocketServer.class);
  
  private static final AtomicInteger onlineCount = new AtomicInteger(0);
  
  private static CopyOnWriteArraySet<Session> sessionSet = new CopyOnWriteArraySet<Session>();
  
  @OnOpen
  public void onOpen(Session session,@PathParam("uid") String uid) 
  {
      logger.info("open message uid:{}",uid);
      sessionSet.add(session);
      onlineCount.incrementAndGet();
      logger.info("窗口开始监听uid:{},当前在线人数:{}",uid,onlineCount);
  }
  
  @OnClose
  public void onClose(Session session) 
  {
      String sessionId=session.getId();
      logger.info("sessionid:{} close",sessionId);
      sessionSet.remove(this);
      int count=onlineCount.decrementAndGet();
      logger.info("有一连接关闭!当前在线人数为:{}",count);
  }

  @OnError
  public void onError(Session session, Throwable error) 
  {
      logger.error("消息发生错误:{},Session ID: {}",error.getMessage(),session.getId());
  }
  
 
  public void batchSendMesasge(String uid,String message) throws IOException 
  {
      logger.info("推送消息到窗口:{},推送内容:{}",uid,message);
      for(Session session:sessionSet){
          sendMessage(session, message);
      }
  }
  
  public void sendMessage(Session session, String message) throws IOException {
      
      if(session!=null)
      {
          synchronized (session) {
              session.getBasicRemote().sendText(message);
          }
      }
  }

}

说明: @OnOpen :当有新的WebSocket连接进入时调用 @OnClose:当有WebSocket连接关闭时调用 @OnError :当有WebSocket抛出异常时调用 @OnMessage:当接收到字符串消息时,对该方法进行回调

测试示例

@Controller
public class WebScoketController
{
 @Autowired
 private WebSocketServer webSocketServer;

  @ResponseBody
  @RequestMapping("/sendMessage")
  public String batchMessage(String uid,String message) 
  {  
      Map<String, String> map =new HashMap<String, String>();
      try 
      {
          map.put("code", "200");
          webSocketServer.batchSendMesasge(uid,message);
      } 
      catch (Exception e) 
      {
          map.put("code", "-1");
          map.put("message", e.getMessage());
      }
      return JSON.toJSONString(map);
  } 
  
  @GetMapping("/enter")
  public String enter() 
  { 
      return "webscoketTest.html";
  }
}

页面请求websocket

<!DOCTYPE HTML>
<html>
 <head>
 <meta charset="utf-8">
 <title>websocket test</title>
  
    <script type="text/javascript">
          if ("WebSocket" in window)
          {
          	console.log("您的浏览器支持 WebSocket!");
             
             var ws = new WebSocket("ws://127.0.0.1:9092/websocket/1234");
             console.log('ws连接状态:' + ws.readyState);
             
             //打开
             ws.onopen = function()
             {
                ws.send("message test");
                console.log("mesage sending");
             };
              
              //发送消息
             ws.onmessage = function (evt) 
             { 
                var received_msg = evt.data;
                alert(received_msg);
             };
              
              //关闭
             ws.onclose = function()
             { 
                // 关闭 websocket
                console.log("socket is close"); 
             };
          }
          
          else
          {
          	 console.log("您的浏览器不支持 WebSocket!");
          }
    </script>
 </head>
</html>

测试效果

启动程序:运行http://localhost:9092/enter 进入页面开启websocket。

用户发送消息:http://localhost:9092/sendMessage?uid=1235&message=this%20is%20message1

执行的结果如下:

open message uid:1234
[nio-9092-exec-2] c.s.f.w.controller.WebSocketServer: 窗口开始监听uid:1234,当前在线人数:1
[nio-9092-exec-5] c.s.f.w.controller.WebSocketServer: 推送消息到窗口:1234,推送内容:this is message

以上就是SpringBoot+WebSocket实现消息推送功能的详细内容,更多关于SpringBoot WebSocket消息推送的资料请关注编程宝库其它相关文章!

 正文最近新到了一个项目,用到定时任务的地方是真滴多。就稍微研究了一下,来做个demo。其实定时任务使用很广泛也很方便,之前做的人事管理项目,就会定期执行定时任务计算工资,对于一个sa ...