最近在学习有个配置中心(nacos 和 apollo),配置中心在配置变更时通过 http 长连接的方式进行通知。
(1)配置客户端定时向配置中心发送请求获取最新配置(apollo客户端会像服务端发送长轮训http请求,超时时间60秒,当超时后返回客户端一个304 httpstatus,表明配置没有变更,客户端继续这个步骤重复发起请求,当有发布配置的时候,服务端会调用DeferredResult.setResult返回200状态码,然后轮训请求会立即返回(不会超时),客户端收到响应结果后,会发起请求获取变更后的配置信息。
(2)当服务器配置变更时会通过与客户端建立的长连接立即通知客户端。
HTTP是无状态的 ,也就是说,浏览器和服务器每进行一次HTTP操作就要建立一次连接,当任务结束就中断连接。如果客户端浏览器访问的某个HTML或其他类型的Web页中包含其他Web资源,如JavaScript文件、图像文件、CSS文件等,此时就要建立一个新的HTTP会话。
HTTP1.1和HTTP1.0相比较而言,最大的区别就是增加了持久连接支持,但还是无状态的或者说是不可靠的。如果浏览器或服务器在其头信息中加入了这行代码:Connection:keep-alive,TCP连接在发送后将仍保持打开状态,于是浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立连接的时间,还节约了带宽。
实现长连接要求客户端和服务端都支持长连接。
如果web服务器端看到这里的值为“Keep-Alive”,或者看到请求使用的是HTTP 1.1(HTTP 1.1默认进行持久连接),它就可以利用持久连接的优点,当页面包含多个元素时(例如Applet,图片),显著地减少下载所需要的时间。要实现这一点, web服务器需要在返回给客户端HTTP头信息中发送一个Content-Length(返回信息正文的长度)头,最简单的实现方法是:先把内容写入ByteArrayOutputStream,然 后在正式写出内容之前计算它的大小
无论客户端浏览器 (Internet Explorer) 还是 Web 服务器具有较低的 KeepAlive 值,它都将是限制因素。例如,如果客户端的超时值是两分钟,而 Web 服务器的超时值是一分钟,则最大超时值是一分钟。客户端或服务器都可以是限制因素
在header中加入 --Connection:keep-alive
在HTTp协议请求和响应中加入这条就能维持长连接。
再封装HTTP消息数据体的消息应用就显的非常简单易用
支持:
在Servlet 3.0中,我们可以从HttpServletRequest对象中获得一个AsyncContext对象,该对象构成了异步处理的上下文,Request和Response对象都可从中获取。AsyncContext可以从当前线程传给另外的线程,并在新的线程中完成对请求的处理并返回结果给客户端,初始线程便可以还回给容器线程池以处理更多的请求。如此,通过将请求从一个线程传给另一个线程处理的过程便构成了Servlet 3.0中的异步处理。
示例:
浏览器请求地址:http://localhost:8080/data 不会立即返回结果,客户端与服务端建立长连接,5秒服务端处理结束后返回结果。
@SpringBootApplication
@RestController
public class ConfigClientApplication {
@RequestMapping("/data")
public DeferredResult getData(){
DeferredResult result = new DeferredResult<>();
Thread thread = new Thread(() ->{
try {
//与客户端建立长连接之后 5秒之后返回结果
Thread.sleep(5000);
result.setResult("hello world");
}catch (Exception e){
}
});
thread.start();
return result;
}
public static void main(String[] args) {
SpringApplication.run(ConfigClientApplication.class, args);
}
}
实现原理:
Controller 返回结果 DeferredResult
public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue == null) {
mavContainer.setRequestHandled(true);
} else {
DeferredResultAdapter adapter = this.getAdapterFor(returnValue.getClass());
if (adapter == null) {
throw new IllegalStateException("Could not find DeferredResultAdapter for return value type: " + returnValue.getClass());
} else {
DeferredResult> result = adapter.adaptToDeferredResult(returnValue);
WebAsyncUtils.getAsyncManager(webRequest).startDeferredResultProcessing(result, new Object[]{mavContainer});
}
}
}
在 WebAsyncManager 中 会进行各种拦截器处理运行,并返回结果
public void startDeferredResultProcessing(
final DeferredResult> deferredResult, Object... processingContext) throws Exception {
Assert.notNull(deferredResult, "DeferredResult must not be null");
Assert.state(this.asyncWebRequest != null, "AsyncWebRequest must not be null");
Long timeout = deferredResult.getTimeoutValue();
if (timeout != null) {
this.asyncWebRequest.setTimeout(timeout);
}
List interceptors = new ArrayList();
interceptors.add(deferredResult.getInterceptor());
interceptors.addAll(this.deferredResultInterceptors.values());
interceptors.add(timeoutDeferredResultInterceptor);
final DeferredResultInterceptorChain interceptorChain = new DeferredResultInterceptorChain(interceptors);
this.asyncWebRequest.addTimeoutHandler(new Runnable() {
@Override
public void run() {
try {
interceptorChain.triggerAfterTimeout(asyncWebRequest, deferredResult);
}
catch (Throwable ex) {
setConcurrentResultAndDispatch(ex);
}
}
});
if (this.asyncWebRequest instanceof StandardServletAsyncWebRequest) {
((StandardServletAsyncWebRequest) this.asyncWebRequest).setErrorHandler(
new StandardServletAsyncWebRequest.ErrorHandler() {
@Override
public void handle(Throwable ex) {
deferredResult.setErrorResult(ex);
}
});
}
this.asyncWebRequest.addCompletionHandler(new Runnable() {
@Override
public void run() {
interceptorChain.triggerAfterCompletion(asyncWebRequest, deferredResult);
}
});
interceptorChain.applyBeforeConcurrentHandling(this.asyncWebRequest, deferredResult);
startAsyncProcessing(processingContext);
try {
interceptorChain.applyPreProcess(this.asyncWebRequest, deferredResult);
deferredResult.setResultHandler(new DeferredResultHandler() {
@Override
public void handleResult(Object result) {
result = interceptorChain.applyPostProcess(asyncWebRequest, deferredResult, result);
setConcurrentResultAndDispatch(result);
}
});
}
catch (Throwable ex) {
setConcurrentResultAndDispatch(ex);
}
}
简单理解为最终通过异步返回结果,客户端与服务端之间通过长连接来保持通信。
参考文章:
https://www.cnblogs.com/code-sayhi/articles/10191526.html
https://blog.csdn.net/qq_42164670/article/details/83214016