前言
本文假设读者对于长轮询有一定的了解。
如不了解,可参考如下一些文章:
阻塞long polling
基于Servlet实现的阻塞long polling的例子可参考:
Long Polling长轮询详解
阻塞long polling的问题在于,每个请求会阻塞一个tomcat/jetty线程,并发性非常差。这时候就需要使用非阻塞的接口。
非阻塞long polling
Servlet
从Servlet 3.0开始,支持异步处理请求。使用异步处理doGet方法执行完成后,结果也不会返回到客户端,会等到请求的context被complete才会写回客户端。使用这种方法,可极大提升long polling的并发处理能力。
代码示例可参考:Long Polling长轮询实现进阶
Spring
当前,大多数公司都是使用spring来开发的。spring也支持了异步处理。实现类是DeferredResult
。
下面是使用spring进行异步处理的代码示例:
@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;
}
DeferredResult
支持设置超时,即在后端一致没有数据的情况下,多长时间断开连接。使用的是onTimeout()
方法。
以下是代码示例:
DeferredResult<ResponseEntity<?>> deferredResult = new DeferredResult<>(500l);
deferredResult.onTimeout(() ->
deferredResult.setErrorResult(
ResponseEntity.status(HttpStatus.REQUEST_TIMEOUT)
.body("Request timeout occurred.")));
我们模拟6s后才有数据返回,如下:
ForkJoinPool.commonPool().submit(() -> {
LOG.info("Processing in separate thread");
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
...
}
deferredResult.setResult(ResponseEntity.ok("OK")));
});
但是由于设置了onTimeout()
,500ms时,连接也会断开。
如果后端出现了错误,可以设置onError()
方法修改返回状态码:
deferredResult.onError((Throwable t) -> {
deferredResult.setErrorResult(
ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("An error occurred."));
});