客户端接收消息的几种方式

前言

系统的业务中,网页需要弹窗"报警信息"。前端获取数据的方式通过轮询调后端接口。也考虑过WebSocket 的方式,但好像兼容性不太好。现在发现还有其他更优的方式,在此记录一下。

网页端收服务端的消息的方式

一、轮询拉取

客户端间隔的发送ajax请求服务器的数据。

  • 优点:实现简单
  • 缺点:
    • 实时性比较差,最大时差就是前端的间隔
    • 效率低:如果长时间没有消息,客户端还是在轮询请求。浪费服务端的资源
二、建立长连接
  • 如果要兼顾实时性和效率,长连接是最佳之选,PC端聊天软件基本都是使用长连接。网页端常见的实现长连接的方式有两种:WebSocket和FlashSocket
  • 缺点:兼容性差,有些浏览器不支持
三、Http长轮询 (通用方式)

通过HTTP短连接拼装长连接,具体是通过“夯住”“只收推送通知”的“通知连接”来实现的,能够做到消息的实时性到达。

客户端向服务器发送Ajax请求,服务器接到请求后hold住连接,直到有新消息才返回响应信息并关闭连接,客户端处理完响应信息后再向服务器发送新的请求

在浏览器和服务端建立了一条"通知连接"的特点:

  • 这是一条browser发往web-server的HTTP连接
  • 这条连接只用来收取推送通知
  • 不像普通的“请求-响应”式HTTP请求,这个HTTP会被服务端夯住,直到有推送通知到达,或者超过约定的时间

对于HTTP请求,为了提高效率,一般来说browser和web-server都会有一些设置,如果一条HTTP请求长时间没有数据(例如,150秒),会被断开。“通知连接”为了不被browser和web-server粗暴断开,一般会设置一个约定阈值(例如,小于150秒),由系统返回一个空消息,以便“优雅返回”。

浏览器发起通知连接时,如果服务端的队列里面有消息,则服务端立刻把消息返回。如果这条连接达到了"约定阈值"要断开时,服务端返回空消息维持连接。当服务端返回消息(空的或者真实消息)的同时,浏览器会瞬间再发起连接。在消息过去和浏览器返回的时间差时有消息时,服务端将新的消息放入队列(发生概率很小)

  • 缺点:服务器hold连接会消耗资源
实现Http长轮询

使用 DeferredResult 异步请求处理

@GetMapping("/async-deferredresult")
public DeferredResult<ResponseEntity<?>> handleReqDefResult(Model model) {
    LOG.info("Received async-deferredresult request");
    DeferredResult<ResponseEntity<?>> output = new DeferredResult<>();
     
    ForkJoinPool.commonPool().submit(() -> {
        LOG.info("Processing in separate thread");
        try {
            Thread.sleep(6000); // 模拟延迟六秒返回
        } catch (InterruptedException e) {
        }
        output.setResult(ResponseEntity.ok("ok"));
    });
     
    LOG.info("servlet thread freed");
    return output;
}


// 设置超时,即在后端一致没有数据的情况下,多长时间断开连接。使用的是onTimeout()方法。
deferredResult.onTimeout(() -> 
  deferredResult.setErrorResult(
    ResponseEntity.status(HttpStatus.REQUEST_TIMEOUT)
      .body("Request timeout occurred.")));
      
 
 // 如果后端出现了错误,可以设置onError()方法修改返回状态码:     
deferredResult.onError((Throwable t) -> {
    deferredResult.setErrorResult(
      ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
        .body("An error occurred."));
})      
      

参考资料

原文地址:https://www.cnblogs.com/wei57960/p/12372232.html