spring、tomcat是如何配合完成websocket

综述

像IM这一类web系统,需要有机制知道是否有新消息,没有websocket前主要靠轮训。
轮训频率设得过高,有效轮训率低,不仅消耗网络资源,还占用cpu资源;轮训频率设得过低,又会造成消息延时较大。
为此诞生了websocket,消息可以由服务端主动推送到客户端,不仅实时性高,效率也是拉满。
为了尽量减少对现有系统进行改造,websocket是在建立在http的基础之上的,这样不仅可以复用http的端口,服务端及客户端的改造都更小。

websocket协议

spring、tomcat是如何配合完成websocket_第1张图片

  1. 握手阶段:如上所述websocket是基于http,主要是握手阶段仍使用http协议,当HTTP处理器发现头部带“Upgrade: websocket”,则认为连接是要升级到websocket的协议;如果支持websocket则返回允许升级到websocket的响应。
  2. 通信阶段:客户端接受到服务端允许升级协议的响应后,则认为握手完成,后续都以websocket格式发送报文。

websocke报文格式如下(图片来源https://zhuanlan.zhihu.com/p/407711596):
spring、tomcat是如何配合完成websocket_第2张图片

websocket协议本身很简单,各个字段的含义可以参考其他文章,这里不再详述,本文主要分析tomcat如何实现websocket以及spring boot如何基于tomcat集成websocket。
本文基于tomcat NIO模式进行分析。
分析前可以大致梳理一下,有哪些关键点。

  1. tomcat NIO模式下,会为每个请求分配一个线程进行处理,websocket是长连接,这个线程是否会与websocket连接绑定,而一直被同一个websocket占有。
  2. websocket协议升级是在那个点触发的。
  3. tomcat是如何在http的基础之上支持websocket
  4. tomcat的websocket是如何暴露接口给spring去集成的。

tomcat NIO模式下线程模型

spring、tomcat是如何配合完成websocket_第3张图片
各个线程的初始化详见org.apache.tomcat.util.net.NioEndpoint#startInternal

  1. Acceptor线程主逻辑用于调用accept方法接受请求,生成socket并在Poller中注册
  2. Poller线程主逻辑是调用select方法,获取可读写的socket,并创建SocketProcessor,并丢给Processor线程进行处理。
  3. Processor线程主要逻辑是根据协议解析socket中的数据,并调用servlet容器进行业务处理。

tomcat NIO模式下请求处理流程

spring、tomcat是如何配合完成websocket_第4张图片
本文不详细解析tomcat的内部实现,http协议本身很简单,没有握手的过程,仅仅只是简单的请求/应答。理论上,只需要 接受请求 -> 解析报文 -> 丢给servlet处理 这几个过程。
tomcat为了实现更丰富的功能抽象出Engine,Host,Context等概念,为了便于理解我们简化一下tomcat模型,我们将CoyoteAdapter到StandardWrapper当成servlet容器内部的行为,将其合并为Servlet Container,其功能就是将request路由到正确的servlet进行处理。
那么请求的处理过程可以简化成
在这里插入图片描述

  1. SocktProcessor主要做协议解析,将socket中的字节流转换成一帧帧HTTP报文
  2. servlet container主要请求路由,跟url信息转发到正确的servlet进行处理
  3. FilterChain这个是servlet规范中的FilterChain,在请求在交给servlet处理前,会经过一系列的Filter进行处理
  4. servlet这个就不解释了。

websocket协议升级涉及的点

  1. websocket与http有点区别,websocket是长连接(虽然http也可以配置 keepalive在实现长连接,但是服务端并不保证客户端一直没有发请求的情况下仍然保持连接),正常情况下只有双方其中一端显式关闭连接,才能结束这个socket。所以SocktProcessor要了解到该socket连接是有升级过协议的;不仅如此因为websocket是基于http,共用端口,所以SocktProcessor也要感知到socket连接是升级过协议,这样完成握手后的请求,要由websocket的协议处理器去做协议解析。
  2. websocket协议本身并没有鉴权等设计,这个需要委托给握手阶段的http报文的处理。为此握手阶段的报文要当成正常的http协议处理,需要走所有已配置的Filter;所以握手阶段的处理一定会走到servlet容器里面,而且spring应该会注册专门的handler去处理升级前的http报文。
  3. servlet处理模型与websocket不一致,所以框架里面应该会有注册websocket的handler的逻辑。

待续

你可能感兴趣的:(tomcat,websocket,spring,tomcat)