No.1 搭建环境
1.1、下载tomcat源码
进入tomcat官网:https://tomcat.apache.org/ 下载对应版本的源码
1.2、导入Eclipse
- ant.jar
- ecj-4.4.jar
- jaxrpc.jar
- wsdl4j-1.5.2.jar
No.2 Tomcat顶层结构
上图大概展示了tomcat的结构,主要包括如下几个模块:
服务器的意思,代表整个tomcat服务器,一个tomcat只有一个Server;
Server中的一个逻辑功能层,一个Server可以包含多个Service;
称作连接器,是Service的核心组件之一,一个Service可以有多个Connector,主要是连接客户端请求;
Service的另一个核心组件,按照层级有Engine,Host,Context,Wrapper四种,一个Service只有一个Engine,其主要作用是执行业务逻辑;
JSP引擎;
会话管理;
No.3 Server
Server是Tomcat最顶层的容器,代表着整个服务器,即一个Tomcat只有一个Server,Server中包含至少一个Service组件,用于提供具体服务。
这个在配置文件中也得到很好的体现(port=”8005” shutdown=”SHUTDOWN”是在8005端口监听到”SHUTDOWN”命令,服务器就会停止)。
tomcat中定义了一个Server接口,其声明如下:
- public interface Server extends Lifecycle {
它继承了Lifecycle接口,这样当调用start()和stop()方法时,所有已定义的Services也会启动或停止。
No.4 Service
前面我们讲过,一个Server至少包含一个Service组件来提供具体的服务。
那Service的基本功能大致是接收客户端的请求,然后解析请求,完成相应的业务逻辑,然后把处理后的结果返回给客户端。
一般会提供两个节本方法,一个start打开服务Socket连接,监听服务端口,一个stop停止服务释放网络资源。
tomcat中定义一个Service接口,其声明如下:
- public interface Service extends Lifecycle {
一个Server可以包含多个Service(它们相互独立,只是公用一个JVM及类库),一个Service负责维护多个Connector和一个Container。
No.5 Connector
Connector是连接器,用于接受请求并将请求封装成Request和Response,然后交给Container进行处理,Container处理完之后在交给Connector返回给客户端。
server.xml默认配置了两个Connector:
- <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443"/>
- <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
Connector在tomcat中的设计大致如下:
Endpoint由于是处理底层的Socket网络连接,因此Endpoint是用来实现TCP/IP协议的
Processor用于将Endpoint接收到的Socket封装成Request,Processor用来实现HTTP协议的
Adapter充当适配器,用于将Request转换为ServletRequest交给Container进行具体的处理
No.6 Container
Container 用于封装和管理 Servlet,以及具体处理 Request 请求,在Container内部包含了4个子容器,4个子容器的作用分别是:
引擎,用来管理多个站点,一个Service最多只能有一个Engine;
代表一个站点,也可以叫虚拟主机,通过配置Host就可以添加站点;
代表一个应用程序,对应着平时开发的一套程序,或者一个WEB-INF目录以及下面的web.xml文件;
每一Wrapper封装着一个Servlet;
No.7 tomcat启动流程
tomcat的启动流程很标准化,入口是BootStrap,统一按照生命周期管理接口Lifecycle的定义进行启动。
首先,调用init()方法逐级初始化,接着调用start()方法进行启动,同时,每次调用伴随着生命周期状态变更事件的触发。
- public static void main(String args[]) {
- if (daemon == null) {
- // Don't set daemon until init() has completed
- Bootstrap bootstrap = new Bootstrap();
- try {
- bootstrap.init();
- } catch (Throwable t) {
- handleThrowable(t);
- t.printStackTrace();
- return;
- }
- daemon = bootstrap;
- } else {
- // When running as a service the call to stop will be on a new
- // thread so make sure the correct class loader is used to prevent
- // a range of class not found exceptions.
- Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
- }
- try {
- String command = "start";
- if (args.length > 0) {
- command = args[args.length - 1];
- }
- if (command.equals("startd")) {
- args[args.length - 1] = "start";
- daemon.load(args);
- daemon.start();
- } else if (command.equals("stopd")) {
- args[args.length - 1] = "stop";
- daemon.stop();
- } else if (command.equals("start")) {
- daemon.setAwait(true);
- daemon.load(args);
- daemon.start();
- } else if (command.equals("stop")) {
- daemon.stopServer(args);
- } else if (command.equals("configtest")) {
- daemon.load(args);
- if (null==daemon.getServer()) {
- System.exit(1);
- }
- System.exit(0);
- } else {
- log.warn("Bootstrap: command \"" + command + "\" does not exist.");
- }
- } catch (Throwable t) {
- // Unwrap the Exception for clearer error reporting
- if (t instanceof InvocationTargetException &&
- t.getCause() != null) {
- t = t.getCause();
- }
- handleThrowable(t);
- t.printStackTrace();
- System.exit(1);
- }
- }
- public void init() throws Exception {
- // 1、设置catalina.home的配置:将catalina.home系统属性设置为当前工作目录(如果尚未设置)。
- setCatalinaHome();
- // 2、设置catalina.base的配置:如果没有设置的话,将当前的工作目录为了catalina.base的设置
- setCatalinaBase();
- // 3、初始化类加载器:commonLoader、catalinaLoader、sharedLoader
- initClassLoaders();
- Thread.currentThread().setContextClassLoader(catalinaLoader);
- SecurityClassLoad.securityClassLoad(catalinaLoader);
- // 加载我们的启动类并调用其process()方法
- if (log.isDebugEnabled())
- log.debug("Loading startup class");
- //4、加载启动类
- Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
- //5、实例化启动类
- Object startupInstance = startupClass.newInstance();
- if (log.isDebugEnabled())
- log.debug("Setting startup class properties");
- //6、设置方法参数
- String methodName = "setParentClassLoader";
- Class<?> paramTypes[] = new Class[1];
- paramTypes[0] = Class.forName("java.lang.ClassLoader");
- Object paramValues[] = new Object[1];
- paramValues[0] = sharedLoader;
- Method method = startupInstance.getClass().getMethod(methodName, paramTypes);
- // 7、调用启动类的setParentClassLoader方法设置共享扩展类加载器
- method.invoke(startupInstance, paramValues);
- catalinaDaemon = startupInstance;
- }
- /**
- * Start the Catalina daemon.
- */
- public void start() throws Exception {
- // 如果启动类为实例化,则调用init()方法
- if( catalinaDaemon==null ) init();
- //获取启动类的start方法
- Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);
- //调用启动类的start方法,即调用org.apache.catalina.startup.Catalina的start()方法
- method.invoke(catalinaDaemon, (Object [])null);
- }
具体时序图如下:
总结
整个Tomcat从代码的角度来看,就是这样的:
在没有SpringBoot内嵌有Tomcat之前,我们都是将项目打为War包放在Tomcat的webapp...
1.曾经爱过你的人,前世一定和你有缘,不要语言虐待。如果他今生真的负你了,那...
缓存误用 缓存,是互联网分层架构中,非常重要的一个部分,通常用它来降低数据库...
被业界誉为CAE领域的奥斯卡盛会第十五届中国CAE工程分析技术年会,于8月17-18 在...
对于很多客户来说,都会误以为高防服务器安全性足够高,不需要进行过多的安全手...
各公司都希望在适应新的工作、员工管理和客户服务方式的同时,尽可能地抓住所有...
智能化大潮风起云涌,行业的智能化转型升级正在成为中国经济新旧动能转换的核心...
戴尔的支持服务于2008年首次推出ProSupport服务,市场的反应使其成为一项备受好...
也许许多工作员会觉得新建站时只必须网站空间或是VPS就就行了,但在事实上并非那...
如今,越来越多的企业在业务场景是使用 Elasticsearch(下文统一称为 ES) 存储自...