🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
### EndPoint `EndPoint`是通信端点,即通信监听的接口,是具体的 Socket 接收和发送处理器,是对传输层的抽象,因此`EndPoint`是用来实现`TCP/IP`协议数据读写的,本质调用操作系统的 socket 接口。 `EndPoint`是一个接口,对应的抽象实现类是`AbstractEndpoint`,而`AbstractEndpoint`的具体子类,比如在`NioEndpoint`和`Nio2Endpoint`中,有两个重要的子组件:`Acceptor`和`SocketProcessor`。 其中 Acceptor 用于监听 Socket 连接请求。`SocketProcessor`用于处理`Acceptor`接收到的`Socket`请求,它实现`Runnable`接口,在`Run`方法里调用应用层协议处理组件`Processor`进行处理。为了提高处理能力,`SocketProcessor`被提交到线程池来执行 我们知道,对于 Java 的多路复用器的使用,无非是两步: 1. 创建一个 Seletor,在它身上注册各种感兴趣的事件,然后调用 select 方法,等待感兴趣的事情发生。 2. 感兴趣的事情发生了,比如可以读了,这时便创建一个新的线程从 Channel 中读数据。 在 Tomcat 中`NioEndpoint`则是`AbstractEndpoint`的具体实现,里面组件虽然很多,但是处理逻辑还是前面两步。它一共包含`LimitLatch`、`Acceptor`、`Poller`、`SocketProcessor`和`Executor`共 5 个组件,分别分工合作实现整个 TCP/IP 协议的处理。 * LimitLatch 是连接控制器,它负责控制最大连接数,NIO 模式下默认是 10000,达到这个阈值后,连接请求被拒绝。 * `Acceptor`跑在一个单独的线程里,它在一个死循环里调用`accept`方法来接收新连接,一旦有新的连接请求到来,`accept`方法返回一个`Channel`对象,接着把`Channel`对象交给 Poller 去处理。 * `Poller`的本质是一个`Selector`,也跑在单独线程里。`Poller`在内部维护一个`Channel`数组,它在一个死循环里不断检测`Channel`的数据就绪状态,一旦有`Channel`可读,就生成一个`SocketProcessor`任务对象扔给`Executor`去处理。 * SocketProcessor 实现了 Runnable 接口,其中 run 方法中的`getHandler().process(socketWrapper, SocketEvent.CONNECT_FAIL);`代码则是获取 handler 并执行处理 socketWrapper,最后通过 socket 获取合适应用层协议处理器,也就是调用 Http11Processor 组件来处理请求。Http11Processor 读取 Channel 的数据来生成 ServletRequest 对象,Http11Processor 并不是直接读取 Channel 的。这是因为 Tomcat 支持同步非阻塞 I/O 模型和异步 I/O 模型,在 Java API 中,相应的 Channel 类也是不一样的,比如有 AsynchronousSocketChannel 和 SocketChannel,为了对 Http11Processor 屏蔽这些差异,Tomcat 设计了一个包装类叫作 SocketWrapper,Http11Processor 只调用 SocketWrapper 的方法去读写数据。 * `Executor`就是线程池,负责运行`SocketProcessor`任务类,`SocketProcessor`的`run`方法会调用`Http11Processor`来读取和解析请求数据。我们知道,`Http11Processor`是应用层协议的封装,它会调用容器获得响应,再把响应通过`Channel`写出。 工作流程如下所示: ![](https://img.kancloud.cn/b7/bf/b7bfa5da4f936a18e0f4e49863630b14_1658x1156.png)