SpringBoot集成WebSocket长连接实际应用详解

 更新时间:2020年06月11日 09:59:54   转载 作者:有追求的程序狗  
这篇文章主要介绍了SpringBoot集成WebSocket长连接实际应用详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

前言:

一、WebSocket之初出茅驴

官方定义:WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。是真正的双向平等对话,属于服务器推送技术的一种。

太官方啦,还是博主过来翻译一下吧 :WebSocket技术只需要service和client建立一次连接,就能实现服务器和客户端双方相互频繁的发送请求和通信!(简单加粗暴的翻译有木有,哈哈!)

WebSocket经典的使用场景:网站在线聊天系统、弹幕系统…

臣附议:webSocket技术无法做到向下兼容,不兼容低版本的IE,因此依赖于浏览器版本,这也正是webSocket非常显著的缺陷。

二、WebSocket的出现到底为我们解决了什么实际问题?

在传统的b/s架构中,要实现服务器向client进行实时消息推送功能,市场上常用的解决方案大致分为三类:

定时轮询 客户端以一定的时间间隔向服务端发出请求
长轮询 如果服务器没有可以立即返回给客户端的数据,则不会立刻返回一个空结果
流技术 客户端隐藏的窗口向服务端发出一个长轮询的请求

长轮询机制:

综合这几种方案,您会发现这些目前我们所使用的所谓的实时技术并不是真正的实时技术,它们只是在用 Ajax 方式来模拟实时的效果,定时轮询需要实时获取取服务端信息的应用时, client需要频繁轮询server,为了拿到最新信息, client一直这样循环下去server如果一直没有新的消息, client的大多请求都是属于无效请求, 导致会带来很多无谓的网络传输,所以这是一种非常低效的实时方案。长轮询对服务器造成的压力非常大,并且如果服务端的数据变更非常频繁的话,这种方式无异于定时轮询。所以为了解决传统http请求的实际问题,WebSocket技术应运而生!下面博主给张图让大家生动的理解传统HTTP和WebSocket的差异化:

三、博主使用WebSocket的场景

博主最进在公司调用第三方影像采集系统,由于影像状态是异步返回给业务系统的,导致当业务系统收到第三方回调后,对于前台用户体验来说是无感知的,因此前台必须刷新页面才能获取到影像最新状态。这时候由service主动向client实时发送影像采集状态的通知是最好不过的方案!在上述提到的常用解决方案,像轮询这种比较low的实现,博主作为技术宅,肯定是不会作为技术选型的,哈哈…

四、不多bb,上代码!

本项目是基于SpringBoot环境开发

1、导入websocket坐标

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

2、封装WebSocketUtil工具类,用于提供对session链接、断开连接、推送消息的简单控制。

 public class WebsocketUtil {
  /**
   * 记录当前在线的Session
   */
  private static final Map<String, Session> ONLINE_SESSION = new ConcurrentHashMap<> ();

  /**
   * 添加session
   * @param userId
   * @param session
   */
  public static void addSession(String userId, Session session){
    // 此处只允许一个用户的session链接。一个用户的多个连接,我们视为无效。
    ONLINE_SESSION.putIfAbsent ( userId, session );
  }

  /**
   * 关闭session
   * @param userId
   */
  public static void removeSession(String userId){
    ONLINE_SESSION.remove ( userId );
  }

  /**
   * 给单个用户推送消息
   * @param session
   * @param message
   */
  public static void sendMessage(Session session, String message){
    if(session == null){
      return;
    }

    // 同步
    RemoteEndpoint.Async async = session.getAsyncRemote ();
    async.sendText ( message );
  }

  /**
   * 向所有在线人发送消息
   * @param message
   */
  public static void sendMessageForAll(String message) {
    //jdk8 新方法
    ONLINE_SESSION.forEach((sessionId, session) -> sendMessage(session, message));
  }
}

3、 WebSocketController

如上,已经创建好了简单的session管理和消息管理,接下来要使用注解的方式,使用SpringBoot的websocket包提供的方法,实现OnOpen、OnClose、OnMessage三个方法,再实现一个OnError方法来应对异常。代码段如下:

/**
 * websocket接口处理类
 */
@Component
@ServerEndpoint ( value = "/chat/{userid}" )
public class WebsocketController {

  /**
   * 连接事件,加入注解
   * @param userId
   * @param session
   */
  @OnOpen
  public void onOpen( @PathParam ( value = "userid" ) String userId, Session session ) {
    String message ="[" + userId + "]加入聊天室!!";

    // 添加到session的映射关系中
    WebsocketUtil.addSession ( userId, session );
    // 广播通知,某用户上线了
    WebsocketUtil.sendMessageForAll ( message );
  }

  /**
   * 连接事件,加入注解
   * 用户断开链接
   * @param userId
   * @param session
   */
  @OnClose
  public void onClose(@PathParam ( value = "userId" ) String userId, Session session ) {
    String message ="[" + userId + "]退出了聊天室...";

    // 删除映射关系
    WebsocketUtil.removeSession ( userId );
    // 广播通知,用户下线了
    WebsocketUtil.sendMessageForAll ( message );
  }

  /**
   * 当接收到用户上传的消息
   * @param userId
   * @param session
   */
  @OnMessage
  public void onMessage(@PathParam ( value = "userId" ) String userId, Session session ,String message) {
    String msg ="[" + userId + "]:"+message;

    // 直接广播
    WebsocketUtil.sendMessageForAll ( msg );
  }

  /**
   * 处理用户活连接异常
   * @param session
   * @param throwable
   */
  @OnError
  public void onError(Session session, Throwable throwable) {
    try {
      session.close();
    } catch (IOException e) {
      e.printStackTrace();
    }
    throwable.printStackTrace();
  }
}

4、添加 ServerEndpointExporter 启动Bean

public class DemoApplication {

  public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
  }

  /**
   * 会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
   * 要注意,如果使用独立的servlet容器,
   * 而不是直接使用springboot的内置容器,
   * 就不要注入ServerEndpointExporter,因为它将由容器自己提供和管理。
   */
  @Bean
  public ServerEndpointExporter serverEndpointExporter() {
    return new ServerEndpointExporter();
  }
}

那些年踩过的坑:
注意:在websocketEndpoint中,使用@Autowired一些列注解注入Bean时候,一直无法注入,报空指针。原因在于spring管理的都是单例(singleton),和 websocket (多对象)相冲突。
解决办法:通过上下文获取bean实例:从Spring上下文获取bean实例的方法

到此这篇关于SpringBoot集成WebSocket长连接实际应用详解的文章就介绍到这了,更多相关SpringBoot WebSocket长连接内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • springboot使用消息中间件

    springboot使用消息中间件

    这篇文章主要介绍了springboot使用消息中间件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-10-10
  • Intellij Idea中进行Mybatis逆向工程的实现

    Intellij Idea中进行Mybatis逆向工程的实现

    这篇文章主要介绍了Intellij Idea中进行Mybatis逆向工程的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-05-05
  • Spring boot搭建邮件服务的完整步骤

    Spring boot搭建邮件服务的完整步骤

    这篇文章主要给大家介绍了关于Spring boot搭建邮件服务的完整步骤,文中通过示例代码介绍的非常详细,对大家学习或者使用Spring boot具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-09-09
  • 通过一个map替换字符串中指定的字符变量方法

    通过一个map替换字符串中指定的字符变量方法

    下面小编就为大家带来一篇通过一个map替换字符串中指定的字符变量方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-03-03
  • Spring Boot 整合mybatis 与 swagger2

    Spring Boot 整合mybatis 与 swagger2

    之前使用springMVC+spring+mybatis,总是被一些繁琐的xml配置,还经常出错,下面把以前的一些ssm项目改成了spring boot + mybatis,相对于来说优点太明显了,具体内容详情大家通过本文学习吧
    2017-08-08
  • springboot+vue部署按照及运行方法

    springboot+vue部署按照及运行方法

    在本篇文章里小编给大家整理了关于springboot+vue部署按照及运行方法和实例内容,需要的朋友们学习参考下。
    2020-01-01
  • 在SpringBoot中通过jasypt进行加密解密的方法

    在SpringBoot中通过jasypt进行加密解密的方法

    今天小编就为大家分享一篇关于在SpringBoot中通过jasypt进行加密解密的方法,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-01-01
  • Java最全文件操作实例汇总

    Java最全文件操作实例汇总

    这篇文章主要介绍了Java最全文件操作,总结分析了大量实例,详细汇总了Java针对文件的各种常用操作,需要的朋友可以参考下
    2015-11-11
  • Java网络编程实现的简单端口扫描器示例

    Java网络编程实现的简单端口扫描器示例

    这篇文章主要介绍了Java网络编程实现的简单端口扫描器,涉及Java网络编程Socket组建、swing组建及多线程相关操作技巧,需要的朋友可以参考下
    2018-04-04
  • Spring Security Remember me使用及原理详解

    Spring Security Remember me使用及原理详解

    这篇文章主要介绍了Spring Security Remember me使用及原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-09-09

最新评论