当前位置: 首页 > 科技观察

【应用服务器】一分钟掌握Tomcat应用服务器

时间:2023-03-12 09:52:08 科技观察

No.1搭建环境1.1,下载tomcat源码进入tomcat官网:https://tomcat.apache.org/下载对应版本的源码1.2、导入Eclipse,新建一个Java项目,将Tomcat源码包中java目录下的文件复制到src目录下,导入外部依赖包ant.jarecj-4.4.jarjaxrpc.jarwsdl4j-1.5.2。jarNo.2Tomcat的顶层结构上图大致展示了tomcat的结构。主要包括以下几个模块:Server:服务器的意思,代表整个tomcat服务器,一个tomcat只有一个Server;Service:Server中的一个逻辑功能层,一个Server可以包含多个Service;Connector:简称连接器,是Service的核心组件之一,一个Service可以有多个Connector,主要连接客户端请求;Container:Service的另一个核心组件,按级别分为Engine、Host、Context、Wrapper四种,一个Service只有一个Engine,主要作用是执行业务逻辑;Jasper:JSP引擎;Session:会话管理;No.3ServerServer是Tomcat的顶级容器,代表整个服务器,即一个Tomcat只有一个Server,而Server至少包含一个Service组件。提供特定服务。这在配置文件中也有很好的体现(port=”8005”shutdown=”SHUTDOWN”就是在8005端口监听”SHUTDOWN”命令,服务器就会停止)。在tomcat中定义了一个Server接口,它的声明如下:publicinterfaceServerextendsLifecycle{它继承了Lifecycle接口,这样当start()和stop()方法被调用时,所有定义的Services也会启动或停止。它的标准实现是:org.apache.catalina.core.StandardServer类。Server元素表示整个Catalinaservlet容器。它的属性代表了整个servlet容器的特性。服务器可能包含一个或多个服务,以及一组命名资源。它的具体实现应该在其构造函数中向ServerFactory类注册一个(单例)实例。No.4Service前面我们提到,一个Server至少包含一个Service组件来提供特定的服务。Service的基本功能大致就是接收客户端的请求,然后解析请求,完成相应的业务逻辑,然后将处理后的结果返回给客户端。一般提供两种节省开销的方法,一种是start打开服务Socket连接,监听服务端口,另一种是stop停止服务,释放网络资源。tomcat中定义了一个Service接口,它的声明如下:publicinterfaceServiceextendsLifecycle{一个Service是一个或多个Connector的集合,这些Connector共享一个Container来处理请求。Connector负责处理请求监听,Container负责处理请求处理。从conf/server.xml文件的配置我们可以知道,Service相当于Connector和Engine组件的包装器,建立一个或多个Connector与一个Engine之间的关联关系。在默认配置文件中,定义了一个名为Catalina的服务,它将两个连接器HTTP/1.1和AJP/1.3与一个名为Catalina的引擎相关联。一个Server可以包含多个Service(它们相互独立,但共享一个JVM和类库),一个Service负责维护多个Connector和一个Container。No.5ConnectorConnector是一个连接器,用来接受请求并封装成Request和Response,然后交给Container处理。Container处理完成后,交给Connector,返回给客户端。server.xml默认配置了两个Connector:监听端口8080,端口值可以修改,connectionTimeout定义了连接超时时间,单位是毫秒,redirectPort定义了ssl重定向接口,按照上面的配置,Connector会转发ssl请求到8443端口。监听8009端口,AJP是ApacheJservProtocol,它会处理Tomcat和Apachehttp服务器,这个连接服务器用来处理我们同时使用Tomcat和Apachehttp服务器的情况,比如在同一台物理服务器上部署一个Apachehttp服务器和多个Tomcat服务器,处理静态资源和负载均衡时通过Apache服务器,不同的Tomcat实例需要AJP监听不同的端口。tomcat中Connector的设计大致如下:Connector使用ProtocolHandler来处理请求,不同的ProtocolHandler代表不同的连接类型。ProtocolHandler由三个Component组成:Endpoint、Processor、AdapterEndpoint是用来处理底层Socket网络连接的,所以Endpoint是用来实现TCP/IP协议的。Processor用于将Endpoint收到的Socket封装成Request,Processor用于实现HTTP协议的Adapter。适配器用于将Request转化为ServletRequest,交给Container进行具体处理。No.6ContainerContainer用于封装和管理Servlet,具体处理Request请求。Container内部有4个子容器,4个子容器的作用是:Engine:引擎,用于管理多个站点。一个Service最多只能有一个Engine;Host:代表站点,也可以称为虚拟主机。站点可以通过配置Host来添加;Context:表示一个应用程序,对应通常开发的一组程序,或者一个WEB-INF目录和后面的web.xml文件;Wrapper:每个Wrapper封装了一个Servlet;No.7tomcat启动流程tomcat的启动流程非常规范,入口是BootStrap,统一根据生命周期管理接口Lifecycle的定义启动。首先调用init()方法逐步初始化,然后调用start()方法启动。同时,每次调用都伴随着生命周期状态变化事件的触发。org.apache.catalina.startup.Bootstrap的程序入口方法,具体如下:publicstaticvoidmain(Stringargs[]){if(daemon==null){//Don'tsetdaemonuntilinit()hascompletedBootstrapbootstrap=newBootstrap();try{bootstrap.init();}catch(Throwablet){handleThrowable(t);t.printStackTrace();return;}daemon=bootstrap;}else{//Whenrunningasaservicethecalltostopwillbeonanew//threadsomakesurethecorrectclassloaderisusedtoprevent//arangeofclassnotfoundexceptions.Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);}try{Stringcommand="start";if(args.length>0){command=args[args.length-1];}if(command.equals("startd")){args[args.length-1]="start";daemon.load(args);daemon.start();}elseif(command.equals("stopd")){args[args.length-1]="stop";daemon.stop();}elseif(command.equals("start")){daemon.setAwait(true);daemon.load(args);daemon.start();}elseif(command.equals("stop")){daemon.stopServer(args);}elseif(command.equals("configtest")){daemon.load(args);if(null==daemon.getServer()){System.exit(1);}System.exit(0);}else{log.warn("Bootstrap:command\""+command+"\"doesnotexist.");}}catch(Throwablet){//UnwraptheExceptionforcleareererrorreportingif(instanceofInvocationTargetException&&t.getCause()!=null){t=t.getCause();}handleThrowable(t);t.printStackTrace();System.exit(1);}}org.apache.catalina.startup.Bootstrap的初始化方法,具体实现如下:publicvoidinit()throwsException{//1,设置catalina.home配置:将catalina.home系统属性设置为当前工作目录(如果尚未设置)setCatalinaHome();//2.设置catalina.base的配置:如果不设置,则设置当前工作目录为catalina.basesetCatalinaBase();//3.初始化类加载器:commonLoader,catalinaLoader,sharedLoaderinitClassLoaders();Thread.currentThread().setContextClassLoader(catalinaLoader);SecurityClassLoad.securityClassLoad(catalinaLoader);//加载我们的启动类并调用它的process()方法if(log.isDebugEnabled())log.debug("加载启动类");//4.加载启动类ClassstartupClass=catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");//5.实例化启动类ObjectstartupInstance=startupClass.newInstance();if(log.isDebugEnabled())log.debug("Settingstartupclassproperties");//6.设置方法参数StringmethodName="setParentClassLoader";ClassparamTypes[]=newClass[1];paramTypes[0]=Class.forName("java.lang.ClassLoader");ObjectparamValues[]=newObject[1];paramValues[0]=sharedLoader;Methodmethod=startupInstance.getClass().getMethod(methodName,paramTypes);//7.调用启动类共享扩展类加载器方法的setParentClassLoader方法设置d.invoke(startupInstance,paramValues);catalinaDaemon=startupInstance;}org.apache.catalina.startup.Bootstrap的start()方法实现如下:/***StarttheCatalinadaemon.*/publicvoidstart()throwsException{//Ifstart如果类被实例化,则调用init()方法if(catalinaDaemon==null)init();//获取启动类的start方法Methodmethod=catalinaDaemon.getClass().getMethod("start",(Class[])null);//调用启动类的start方法,即调用org.apache.catalina.startup.Catalina的start()方法method.invoke(catalinaDaemon,(Object[])null);}具体时序图如下:总结一下整个Tomcat从代码来看,大概是这样的: