Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache、Sun 和其他一些公司及个人共同开发而成。
了解Tomcat架构原理之前首先复习下HTTP协议的一些内容。
HTTP协议是一种工作在应用层的B/S模式协议,同时HTTP协议是根据TCP/IP协议来传递数据的(HTML文件、图片、查询结果等),HTTP协议不涉及数据包传输,规定了b/s通信格式。
1.首先用户请求browser
2.b会向t服务器发送tcp连接请求
3.t服务器接收到请求并且建立连接 4.b生成HTTP样式的数据包 5.b将请求的数据包发送给t
6.t解析请求 7.t执行请求 8.生成HTTP格式的数据包,response给b 9.发送给b
10.b解析response数据包 11.b将html响应给用户。
这就是bs模式的大致流程,其中还有根据DNS服务器寻找服务器的过程,这里不展开。
HTTP服务器处理请求的模式
浏览器发给tomcat HTTP格式的请求,HTTP服务器收到请求后,需要调用服务端程序(java类)来处理。
HTTP服务器,也就是 这个蓝色的大块儿,并不会直接调用java类,其将请求交给Servlet容器,容器通过接口再来调用java类,达到了HTTP服务器 和 java业务类 耦合的目的。Servlet 容器+接口称为Servlet规范类,tomcat规范实现了Servlet容器。
Servlet容器工作流程
用户请求某个资源时,HTTP服务器将用户请求封装为一个ServletRequest对象,然后发送给Servlet容器,Servlet容器根据url和servlet的映射关系找到具体的servlet,然后初始化,调用Service 业务类处理用户请求后,封装一个ServletResponse对象给HTTP服务器。
整个过程可以概括为 :定位->加载Servlet->调用业务类->响应
大致了解了上面两个过程,继续看Tomcat大概的一个整体架构
Tomcat整体架构
Tomcat服务器实现了两个功能
1)处理Socket请求,负责网络字节流Request和Resonse两个对象的转换
2)加载和管理Servlet,把比如根据映射找到具体的业务类,处理具体的Requset请求。
tomcat架构师把tomcat大致做成了这样的一个模型
要实现(1)功能的处理请求就要有一个连接器,负责对外交流,然后将连接器可以将请求交给容器来处理。
连接器+容器就组成了一个Service
这里注意到,tomcat中是可以有多个Servlet Service服务的,并且要注意一个容易是可以对应多个连接器的。
然后把Tomcat拆成两个部分来看(1)连接器Coyote (2)容器Catalina
连接器Coyote
是神么:Coyote,tomcat服务器提供的供客户端访问的外部接口,通过coyote与服务器建立连接、发送请求并且接受响应。
作用:负责具体协议和IO的相关操作,注意这里Requst是在catalina中进一步被封装为ServletRequest对象的。
可以说Coyote就是将具体的Servlet操作交给catalina的一个部件。
Tomcat支持的IO模型
Tom8.5后移除了对bio模型的支持,tomcat8.5采用NIO模型
Tomcat支持的协议
http协议1.0
* 链接后,只能获取一个web资源。
* 链接后,发送请求,服务器做出响应,链接立即断开。
http协议1.1(使用)
* 链接后,只能获取多个web资源。
* 链接后,不会马上断开。
协议分层:
连接器coyote+容器构成了一个Service组件。(BIO NIO APR模型以后再具体看吧)
Tomcat中有不同的Servlet可以实现通过不同端口来访问同一个host上部署的不同web程序。
Coyote组件
注意这个ProtoolHandler模块。
简单来说连接器用过Endpiont模块来实现TCP/IP协议,通过Processor模块来实现HTTP协议。然后通过Adapter来将Request请求具体的封装为ServletRequest对象交给容器。
Catalina
配置Tomcat环境的时候用到了这个Catalina_home,PowerShell测试环境是否成功的时候也用到了catalina.bat这个批处理文件,现在可以知道其所以然了,可以说这个Catalina完全就是Tomcat的核心。Coyote是一个协议解析的过程,而Catalina是一个逻辑执行的过程。
下载Tomcat官网的源码,导入idea这里可以看到Catalina的一个大概结构。
Catalina结构
从这里开始我就被这无限的套娃+套娃+套娃整的有点晕了。
这里已经和前面对应上了。
Catalina是Servlet容器的一个具体实现,而Tomcat只是Servlet的容器,这大概就是这个Tomcat的本质了。
这是一个Servcie的类的具体实现,今天就不看别人手撕代码了,以后自己再撕。
在我的理解下这个Connector就是Coyote的一个实现。这样就和前面的Coyote+容器模式对应上了。
然后具体来看这个Container容器。
Container容器
Engine就是引擎,Host代表主机或者说虚拟主机,也就是ip地址或者localhost吧,context代表Web应用程序,wrapper代表具体要实现的Servlet。
这里又开始套娃,一个Service下只能又一个引擎,而一个引擎中可以有多个Host,一个Host中可以有多个Context,一个Context中可以有多个Wrapper,把这些翻译成上面的地址web程序Servlet那一套也是完全一样。
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!-- 注册-->
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.first.servlet.HellowServlet</servlet-class>
</servlet>
<!-- 请求路径-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
Wrapper对应这个最简单的HelloServlet中的<url-pattern>
当我们在浏览器的地址栏里输入http://localhost:8080/we/index时[假设我部署在webapps目录下的项目名为we]
就会匹配到我们指定的<url-pattern>中,即/index然后一步一步找到对应的<servlet-class>
那我们输入的URL:http://localhost:8080/we/index又是如何与<url-pattern>中的/index匹配的呢?
首先我们要知道URL的组成
http://localhost:8080 我们可以理解为是我们的服务器地址,而该地址之后的部分我们统称为:RequestURI
RequestURI是我们需要重点注意的部分,其又可以分解为几部分
/we 是我们的ServletConext的上下文地址,我们称为ServletContext Path,可以简单理解为部署项目时的webapps目录下的项目名
/index 是我们的Servlet的地址,我们称为Servlet Path,这里就是需要与我们的<url-pattern>匹配的内容
注:在/index后边我们还可以跟其他的信息,例如:/index?name=admin&pass=admin 这是其中一种明文表示的方式
这样就完全和前面对应了起来。
Tomcat源代码中这些具体实现,这也是和前面的Catalina结构相对应的。
Tomcat的启动流程
·流程
图也不是自己画的,理解了就好。
可以看到Tomcat启动的时候首先启动了bin目录下的Startup.bat,当然linux服务器中可能就是Startup.sh了。
sublineText打开这个Startup.bat,找到里面set "EXECUTABLE=%CATALINA_HOME%\bin\catalina.bat",又再次套娃,
在catalina.bat中找到这行set MAINCLASS=org.apache.catalina.startup.Bootstrap,然后在这里调用了MainClass%_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%,这样Tomcat就算是启动了,然后bootstrap完成后续对象的初始化,形成了一个Executor线程池,Enigne后面的类全部标准初始化后再start全部的方法。
这个过程完成后,Coyote的接受发送端口就准备好了,当然,catalina内部的组件也全部准备就绪了。
这个ProtocolHandler就是前面Coyote连接器的一个组件
这里的init()应该就是初始化endpoint tomcat要监听的端口(server.xml文件中的配置)
源码大概看下,这些组件全部存在issd的生命流程,所以它们基于Lifecycle这个接口,实现了一个生命周期的接口。
然后这些是每个组件的实现类,大概看下以后在撕。
最后是一个请求在Tomcat中执行的流程。
Tomcat请求处理请求流程
<!-- 注册-->
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.first.servlet.HellowServlet</servlet-class>
</servlet>
<!-- 请求路径-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
此处的映射应该指的就是WEB.XML文件夹的 <servlet><servlet-mapping>代码行。
直接上图
从URL层面来看一个请求具体的请求流程
从Tomcat组件方面看
左边红框部分其实就是Coyote,而右边的部分就是容器了。
其中有构造一个过滤器链条,等过滤链条组装完成后,依次执行每个Filter。
最后Servlet给出Resopnse给浏览器,浏览器只能解析静态HTML资源,也就是需要解析的HTTP协议数据包,经过解析后将HTML响应给用户。
参考:https://blog.csdn.net/ly823260355/article/details/104181278
b站:https://www.bilibili.com/video/BV1dJ411N7Um?p=15
希望以后可以再手撕Tomcat哈哈。就快能回答这个输入网址到敲回车整个前后端发生了什么这个能写篇小作文的问题了。
ccccc