@@ -111,7 +111,7 @@ tar -zxf apache-tomcat-8.5.24.tar.gz
111111
112112启动后,访问 ` http://localhost:8080 ` ,可以看到 Tomcat 安装成功的测试页面。
113113
114- ![ img] ( https://raw.githubusercontent.com/dunwu/images/dev /cs/java/javaweb/tools/tomcat/tomcat.png )
114+ ![ img] ( https://raw.githubusercontent.com/dunwu/images/master /cs/java/javaweb/tools/tomcat/tomcat.png )
115115
116116### 2.2. 配置
117117
@@ -364,7 +364,7 @@ public class SimpleTomcatServer {
364364- 设置启动应用的端口、JVM 参数、启动浏览器等。
365365- 成功后,可以访问 ` http://localhost:8080/ ` (当然,你也可以在 url 中设置上下文名称)。
366366
367- ![ img] ( https://raw.githubusercontent.com/dunwu/images/dev /cs/java/javaweb/tools/tomcat/tomcat-intellij-run-config.png )
367+ ![ img] ( https://raw.githubusercontent.com/dunwu/images/master /cs/java/javaweb/tools/tomcat/tomcat-intellij-run-config.png )
368368
369369> ** 说明**
370370>
@@ -374,7 +374,7 @@ public class SimpleTomcatServer {
374374
375375## 3. Tomcat 架构
376376
377- ![ img] ( https://raw.githubusercontent.com/dunwu/images/dev /snap/20201113193431.png )
377+ ![ img] ( https://raw.githubusercontent.com/dunwu/images/master /snap/20201113193431.png )
378378
379379Tomcat 要实现 2 个核心功能:
380380
@@ -402,7 +402,7 @@ Tomcat 支持的应用层协议有:
402402
403403Tomcat 支持多种 I/O 模型和应用层协议。为了实现这点,一个容器可能对接多个连接器。但是,单独的连接器或容器都不能对外提供服务,需要把它们组装起来才能工作,组装后这个整体叫作 Service 组件。Tomcat 内可能有多个 Service,通过在 Tomcat 中配置多个 Service,可以实现通过不同的端口号来访问同一台机器上部署的不同应用。
404404
405- ![ img] ( https://raw.githubusercontent.com/dunwu/images/dev /snap/20201111093124.png )
405+ ![ img] ( https://raw.githubusercontent.com/dunwu/images/master /snap/20201111093124.png )
406406
407407** 一个 Tomcat 实例有一个或多个 Service;一个 Service 有多个 Connector 和 Container** 。Connector 和 Container 之间通过标准的 ServletRequest 和 ServletResponse 通信。
408408
@@ -418,13 +418,13 @@ Tomcat 支持多种 I/O 模型和应用层协议。为了实现这点,一个
418418
419419Tomcat 设计了 3 个组件来实现这 3 个功能,分别是 ** ` EndPoint ` ** 、** ` Processor ` ** 和 ** ` Adapter ` ** 。
420420
421- ![ img] ( https://raw.githubusercontent.com/dunwu/images/dev /snap/20201111101440.png )
421+ ![ img] ( https://raw.githubusercontent.com/dunwu/images/master /snap/20201111101440.png )
422422
423423组件间通过抽象接口交互。这样做还有一个好处是** 封装变化。** 这是面向对象设计的精髓,将系统中经常变化的部分和稳定的部分隔离,有助于增加复用性,并降低系统耦合度。网络通信的 I/O 模型是变化的,可能是非阻塞 I/O、异步 I/O 或者 APR。应用层协议也是变化的,可能是 HTTP、HTTPS、AJP。浏览器端发送的请求信息也是变化的。但是整体的处理逻辑是不变的,EndPoint 负责提供字节流给 Processor,Processor 负责提供 Tomcat Request 对象给 Adapter,Adapter 负责提供 ServletRequest 对象给容器。
424424
425425如果要支持新的 I/O 方案、新的应用层协议,只需要实现相关的具体子类,上层通用的处理逻辑是不变的。由于 I/O 模型和应用层协议可以自由组合,比如 NIO + HTTP 或者 NIO2 + AJP。Tomcat 的设计者将网络通信和应用层协议解析放在一起考虑,设计了一个叫 ProtocolHandler 的接口来封装这两种变化点。各种协议和通信模型的组合有相应的具体实现类。比如:Http11NioProtocol 和 AjpNioProtocol。
426426
427- ![ img] ( https://raw.githubusercontent.com/dunwu/images/dev /snap/20201027091819.png )
427+ ![ img] ( https://raw.githubusercontent.com/dunwu/images/master /snap/20201027091819.png )
428428
429429#### 3.2.1. ProtocolHandler 组件
430430
@@ -444,7 +444,7 @@ EndPoint 是一个接口,对应的抽象实现类是 AbstractEndpoint,而 Ab
444444
445445Processor 是一个接口,定义了请求的处理等方法。它的抽象实现类 AbstractProcessor 对一些协议共有的属性进行封装,没有对方法进行实现。具体的实现有 AJPProcessor、HTTP11Processor 等,这些具体实现类实现了特定协议的解析方法和请求处理方式。
446446
447- ![ img] ( https://raw.githubusercontent.com/dunwu/images/dev /snap/20201113185929.png )
447+ ![ img] ( https://raw.githubusercontent.com/dunwu/images/master /snap/20201113185929.png )
448448
449449从图中我们看到,EndPoint 接收到 Socket 连接后,生成一个 SocketProcessor 任务提交到线程池去处理,SocketProcessor 的 Run 方法会调用 Processor 组件去解析应用层协议,Processor 通过解析生成 Request 对象后,会调用 Adapter 的 Service 方法。
450450
@@ -471,7 +471,7 @@ Tomcat 是怎么确定请求是由哪个 Wrapper 容器里的 Servlet 来处理
471471
472472举例来说,假如有一个网购系统,有面向网站管理人员的后台管理系统,还有面向终端客户的在线购物系统。这两个系统跑在同一个 Tomcat 上,为了隔离它们的访问域名,配置了两个虚拟域名:` manage.shopping.com ` 和` user.shopping.com ` ,网站管理人员通过` manage.shopping.com ` 域名访问 Tomcat 去管理用户和商品,而用户管理和商品管理是两个单独的 Web 应用。终端客户通过` user.shopping.com ` 域名去搜索商品和下订单,搜索功能和订单管理也是两个独立的 Web 应用。如下所示,演示了 url 应声 Servlet 的处理流程。
473473
474- ![ img] ( https://raw.githubusercontent.com/dunwu/images/dev /snap/20201113192022.jpg )
474+ ![ img] ( https://raw.githubusercontent.com/dunwu/images/master /snap/20201113192022.jpg )
475475
476476假如有用户访问一个 URL,比如图中的` http://user.shopping.com:8080/order/buy ` ,Tomcat 如何将这个 URL 定位到一个 Servlet 呢?
477477
@@ -490,7 +490,7 @@ Pipeline-Valve 是责任链模式,责任链模式是指在一个请求处理
490490
491491先来了解一下 Valve 和 Pipeline 接口的设计:
492492
493- ![ img] ( https://raw.githubusercontent.com/dunwu/images/dev /cs/java/javaweb/tools/tomcat/Pipeline与Valve.png )
493+ ![ img] ( https://raw.githubusercontent.com/dunwu/images/master /cs/java/javaweb/tools/tomcat/Pipeline与Valve.png )
494494
495495- 每一个容器都有一个 Pipeline 对象,只要触发这个 Pipeline 的第一个 Valve,这个容器里 Pipeline 中的 Valve 就都会被调用到。但是,不同容器的 Pipeline 是怎么链式触发的呢,比如 Engine 中 Pipeline 需要调用下层容器 Host 中的 Pipeline。
496496- 这是因为 Pipeline 中还有个 getBasic 方法。这个 BasicValve 处于 Valve 链表的末端,它是 Pipeline 中必不可少的一个 Valve,负责调用下层容器的 Pipeline 里的第一个 Valve。
@@ -499,7 +499,7 @@ Pipeline-Valve 是责任链模式,责任链模式是指在一个请求处理
499499- 各层容器对应的 basic valve 分别是 ` StandardEngineValve ` 、` StandardHostValve ` 、 ` StandardContextValve ` 、` StandardWrapperValve ` 。
500500- 由于 Valve 是一个处理点,因此 invoke 方法就是来处理请求的。注意到 Valve 中有 getNext 和 setNext 方法,因此我们大概可以猜到有一个链表将 Valve 链起来了。
501501
502- ![ img] ( https://raw.githubusercontent.com/dunwu/images/dev /cs/java/javaweb/tools/tomcat/请求处理过程.png )
502+ ![ img] ( https://raw.githubusercontent.com/dunwu/images/master /cs/java/javaweb/tools/tomcat/请求处理过程.png )
503503
504504整个调用过程由连接器中的 Adapter 触发的,它会调用 Engine 的第一个 Valve:
505505
@@ -511,7 +511,7 @@ connector.getService().getContainer().getPipeline().getFirst().invoke(request, r
511511
512512### 4.1. Tomcat 的启动过程
513513
514- ![ img] ( https://raw.githubusercontent.com/dunwu/images/dev /snap/20201118145455.png )
514+ ![ img] ( https://raw.githubusercontent.com/dunwu/images/master /snap/20201118145455.png )
515515
5165161 . Tomcat 是一个 Java 程序,它的运行从执行 ` startup.sh ` 脚本开始。` startup.sh ` 会启动一个 JVM 来运行 Tomcat 的启动类 ` Bootstrap ` 。
5175172 . ` Bootstrap ` 会初始化 Tomcat 的类加载器并实例化 ` Catalina ` 。
@@ -731,12 +731,12 @@ ContextConfig 解析 web.xml 顺序:
731731
732732### 4.3. LifeCycle
733733
734- ![ img] ( https://raw.githubusercontent.com/dunwu/images/dev /snap/20201118105012.png )
734+ ![ img] ( https://raw.githubusercontent.com/dunwu/images/master /snap/20201118105012.png )
735735
736736#### 4.3.1. 请求处理过程
737737
738738<div align =" center " >
739- <img src =" https://raw.githubusercontent.com/dunwu/images/dev /cs/java/javaweb/tools/tomcat/请求处理过程.png " width =" 600 " >
739+ <img src =" https://raw.githubusercontent.com/dunwu/images/master /cs/java/javaweb/tools/tomcat/请求处理过程.png " width =" 600 " >
740740</div >
741741
7427421 . 根据 server.xml 配置的指定的 connector 以及端口监听 http、或者 ajp 请求
@@ -747,25 +747,25 @@ ContextConfig 解析 web.xml 顺序:
747747### 4.4. Connector 流程
748748
749749<div align =" center " >
750- <img src =" https://raw.githubusercontent.com/dunwu/images/dev /cs/java/javaweb/tools/tomcat/connector.png " width =" 600 " >
750+ <img src =" https://raw.githubusercontent.com/dunwu/images/master /cs/java/javaweb/tools/tomcat/connector.png " width =" 600 " >
751751</div >
752752
753753#### 4.4.1. 阻塞 IO
754754
755755<div align =" center " >
756- <img src =" https://raw.githubusercontent.com/dunwu/images/dev /cs/java/javaweb/tools/tomcat/阻塞IO.png " width =" 600 " >
756+ <img src =" https://raw.githubusercontent.com/dunwu/images/master /cs/java/javaweb/tools/tomcat/阻塞IO.png " width =" 600 " >
757757</div >
758758
759759#### 4.4.2. 非阻塞 IO
760760
761761<div align =" center " >
762- <img src =" https://raw.githubusercontent.com/dunwu/images/dev /cs/java/javaweb/tools/tomcat/非阻塞IO.png " width =" 600 " >
762+ <img src =" https://raw.githubusercontent.com/dunwu/images/master /cs/java/javaweb/tools/tomcat/非阻塞IO.png " width =" 600 " >
763763</div >
764764
765765#### 4.4.3. IO 多路复用
766766
767767<div align =" center " >
768- <img src =" https://raw.githubusercontent.com/dunwu/images/dev /cs/java/javaweb/tools/tomcat/IO多路复用.png " width =" 600 " >
768+ <img src =" https://raw.githubusercontent.com/dunwu/images/master /cs/java/javaweb/tools/tomcat/IO多路复用.png " width =" 600 " >
769769</div >
770770
771771阻塞与非阻塞的区别在于进行读操作和写操作的系统调用时,如果此时内核态没有数据可读或者没有缓冲空间可写时,是否阻塞。
@@ -775,7 +775,7 @@ IO 多路复用的好处在于可同时监听多个 socket 的可读和可写事
775775#### 4.4.4. Tomcat 各类 Connector 对比
776776
777777<div align =" center " >
778- <img src =" https://raw.githubusercontent.com/dunwu/images/dev /cs/java/javaweb/tools/tomcat/Tomcat各类Connector对比.jpg " width =" 500 " >
778+ <img src =" https://raw.githubusercontent.com/dunwu/images/master /cs/java/javaweb/tools/tomcat/Tomcat各类Connector对比.jpg " width =" 500 " >
779779</div >
780780
781781- JIO:用 java.io 编写的 TCP 模块,阻塞 IO
@@ -796,7 +796,7 @@ Apache Portable Runtime 是一个高度可移植的库,它是 Apache HTTP Serv
796796** NIO 处理相关类**
797797
798798<div align =" center " >
799- <img src =" https://raw.githubusercontent.com/dunwu/images/dev /cs/java/javaweb/tools/tomcat/NIO处理相关类.jpg " width =" 500 " >
799+ <img src =" https://raw.githubusercontent.com/dunwu/images/master /cs/java/javaweb/tools/tomcat/NIO处理相关类.jpg " width =" 500 " >
800800</div >
801801
802802Poller 线程从 EventQueue 获取 PollerEvent,并执行 PollerEvent 的 run 方法,调用 Selector 的 select 方法,如果有可读的 Socket 则创建 Http11NioProcessor,放入到线程池中执行;
@@ -829,7 +829,7 @@ Note:
829829### 4.6. 异步 Servlet
830830
831831<div align =" center " >
832- <img src =" https://raw.githubusercontent.com/dunwu/images/dev /cs/java/javaweb/tools/tomcat/传统Servlet处理流程.png " >
832+ <img src =" https://raw.githubusercontent.com/dunwu/images/master /cs/java/javaweb/tools/tomcat/传统Servlet处理流程.png " >
833833</div >
834834
835835传统流程:
@@ -839,7 +839,7 @@ Note:
839839- 最后,根据处理的结果提交响应,Servlet 线程结束
840840
841841<div align =" center " >
842- <img src =" https://raw.githubusercontent.com/dunwu/images/dev /cs/java/javaweb/tools/tomcat/异步Servlet处理流程.png " >
842+ <img src =" https://raw.githubusercontent.com/dunwu/images/master /cs/java/javaweb/tools/tomcat/异步Servlet处理流程.png " >
843843</div >
844844
845845异步处理流程:
0 commit comments