WebSocket 实现后端消息推送

前后端环境:Spring Boot@2.7.x + Vue@2.x

WebSocket 概念

HTTP 协议是一种无状态的、无连接的、单向的应用层协议。它采用了请求/响应模型。通信请求只能由客户端发起,服务端对请求做出应答处理。

这种通信模型有一个弊端:HTTP 协议无法实现服务器主动向客户端发起消息。

这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。大多数 Web 应用程序将通过频繁的异步 JavaScript 和 XML(AJAX)请求实现长轮询。轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。

WebSocket 就是这样发明的。WebSocket 连接允许客户端和服务器之间进行全双工通信,以便任一方都可以通过建立的连接将数据推送到另一端。WebSocket 只需要建立一次连接,就可以一直保持连接状态。这相比于轮询方式的不停建立连接显然效率要大大提高。

服务端集成

加入 Maven 依赖

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

WebSocketConfig.java

/*** imports ***/

/**
 * WebSocket 配置类
 */
@Configuration
@EnableWebSocket
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
  @Override
  public void registerStompEndpoints(StompEndpointRegistry registry) {
    // 注册一个 /notification 端点,前端通过这个端点进行连接
    registry.addEndpoint("/notification")
      // 解决跨域问题
      .setAllowedOrigins("*").withSockJS();
  }

  @Override
  public void configureMessageBroker(MessageBrokerRegistry registry) {
    // 定义了一个客户端订阅地址的前缀信息,也就是客户端接收服务端发送消息的前缀信息
    registry.enableSimpleBroker("/topic");
  }
}

WebSocketTask.java

/*** imports ***/

/**
 * 服务器定时推送任务
 */
@Slf4j
@Component
public class WebSocketTask {
  @Autowired
  private SimpMessagingTemplate wsTemplate;

  @Autowired
  private ReceiverDataService receiverDataService;

  /**
     * 按照标准时间来算,每隔 2s 执行一次
     */
  //    @Scheduled(cron = "0/10 * * * * ?")
  public void websocket() throws Exception {
    log.info("【推送消息】开始执行:{}", DateUtil.formatDateTime(new Date()));
    // destination, payload
    wsTemplate.convertAndSend("/topic/receiver", "message content");
    log.info("【推送消息】执行结束:{}", DateUtil.formatDateTime(new Date()));
  }
}

客户端集成

引入 sock.jsstomp.js

<!-- WebSocket -->
<script src="static/js/sockjs.min.js"></script>
<script src="static/js/stomp.js"></script>

修改 vue 文件

<template>
  <!-- ... -->
</template>

<script>
const wsHost = "http://localhost:8081/notification"
const wsTopic = "/topic/receiver"

export default {
  name: '',
  data() {
    return {
      // stomp.js
      isConnected: false,
      stompClient: {},
      socket: {},
      receiveData: {},
      receiverSize: 0
    }
  },
  mounted() {
    this._initSockJs()
  },
  beforeDestroy() {
    this._destroySockJs()
  },
  methods: {
    // stomp.js
    _initSockJs() {
      this.socket = new SockJS(wsHost)
      this.stompClient = Stomp.over(this.socket)

      this.stompClient.connect({}, frame => {
        console.log('WebSocket 连接成功:' + frame)
        this.isConnected = true
        // Message.success('WebSocket 服务器连接成功')
        // 另外再注册一下消息推送
        this.stompClient.subscribe(wsTopic, res => {
          console.log(JSON.parse(res.body))
        })
      })
    },
    _destroySockJs() {
      if (this.stompClient != null) {
        this.stompClient.disconnect()
        this.socket.onclose
        this.socket.close()
        this.stompClient = {}
        this.socket = {}
        this.isConnected = false
      }
      // Message.warning('WebSocket 断开成功!')
      console.log('WebSocket 断开成功!')
    }
  }
}
</script>

<style />

WebSocket 实现后端消息推送
http://lpxz.work/posts/56834/
作者
LPxz
发布于
2022年5月5日
许可协议