(四)Tomcat分析
来源:互联网 发布:国服mac版魔兽世界 编辑:程序博客网 时间:2024/05/22 02:02
Tomcat的顶层结构及启动过程
Tomcat的顶层结构
Tomcat中最顶层的容器叫Server,代表整个服务器,Server中包含至少一个Service。Service主要包含两部分:Connector和Container。Connector用于处理连接相关的事情,并提供Socket与request、response的转换,Container用于封装和管理Servlet,以及具体处理request请求。一个Tomcat中只有一个Server,一个Server可以包含多个Service,一个Service只有一个Container,但可以有多个Connectors(因为一个服务器可以有多个连接,如同时提供http和https连接,也可以提供相同协议不同端口的连接),结构图如下:
Tomcat里的Server由org.apache.catalina.startup.Catalina来管理,Catalina是整个Tomcat的管理类,它里面的三个方法load、start、stop分别用来管理整个服务器的生命周期,load方法用于根据conf/server.xml文件创建Server并调用Server的init方法进行初始化,start方法用于启动服务器,start和stop方法在内部分别调用了Server的start和stop方法,load方法内部调用了Server的init方法,这三个方法都会按容器的结构逐层调用相应的方法,比如,Server的start方法中会调用所有Service中的start方法,Service中的start方法又会调用所有包含的Connectors和Container的start方法,这样整个服务器就启动了,init和stop方法也一样,就是Tomcat生命周期的管理方式。Catalina还有个方法,await方法,Catalina中的await方法直接调用了Service的await方法,这份方法的的作用就是进入一个循环,让主线程不会退出。
不过Tomcat的入口main方法并不在Catalina类里,而是在org.apache.catalina.startup.Bootstrap中。Bootstrap的作用类似一个CatalinaAdaptor,具体处理过程还是使用Catalina来完成的,这么做好处是可以把启动的入口和具体的管理类分开,从而可以很方便创建出多种启动方式,每种启动方式只需写一个相应的CatalinaAdaptor就可以了。
Bootstrap的启动过程
Bootstrap是Tomcat的入口,正常情况下启动Tomcat就是调用Bootstrap的main方法,代码:
//org.apache.catalina.startup.Bootstrappublic static void main(String args[]){ //先新建一个Bootstrap if(deamon == null){ Bootstrap bootstrap = new Bootstrap(); try{ //初始化了ClassLoader,并用ClassLoader创建了Catlina示例,赋给catalinaDeamon变量 bootstrap.init(); }catch(Throwable t){ handleThrowable(t); t.printStackTrace(); return; } daemon = bootstrap; }else{ Thread.currentThread().setContextClassLoader(deamon.catalinaLoader); } try{ String command = "start"; if(args.length > 0){ command = args[args.length - 1]; } if(command.equals("startd")){ args[args.length - 1] = "start"; deamon.load(args); deamon.start(); }else if(command.equals("stopd")){ args[args.length - 1] = "stop"; deamon.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")){ deamon.load(args); if(null == daemon.getServer()){ System.exit(1); } System.exit(0); }else{ log.warn("Boostrap:command \""+command+"\" does not exist."); } }catch(Throwable t){ if(t instanceof InvocationTargetException && t.getCause() != null){ t = t.getCause(); } handleThrowable(t); t.printStackTrace(); System.exit(1); }}
main只有两部分内容:首先创建了Bootstrap,并执行init方法初始化;然后处理main方法传入的命令,如果args参数为空,默认执行start。
在init方法里初始化了ClassLoader,并用ClassLoader创建了Catalina示例,然后赋给catalinaDaemon变量,后面对命令的操作都要使用catalinaDaemon来具体执行。
对start命令的处理调用了三个方法:setAwait(true)、load(args)和start()。这三个方法内部调用了Catalina的相应方法进行具体执行,只不过是用反射来调用的。start方法(另外两个方法会处理一些参数,调用方法类似)的代码:
//org.apache.catalina.startup.Bootstrappublic void start() throws Exception{ if(catalinaDaemon == null) init(); Method method = catalinaDaemon.getClass().getMethod("start",(Class[])null); method.invoke(catalinaDaemon,(Object[])null);}
首先判断catalinaDaemon有没有初始化,如果没有则调用init方法对其进行初始化,然后使用Method进行反射调用Catalina的start方法。Method是java.lang.reflect包里的类,代表一个具体的方法,可以使用其中的invoke方法来执行所代表的方法,invoke方法由两个参数,第一个参数是Method方法所在的实体,第二个参数是可变参数用于Method方法执行时所需要的参数,所以上面的调用相当于((Catalina)catalinaDaemon).start()。setAwait和load也用类似的方法调用了Catalina中的setAwait和load方法。
Catalina的启动过程
Catalina的启动过程主要是调用setAwait、load和start方法来完成的。setAwait方法用于设置Server启动完成后是否进入等待状态的标志,如果为true则进入,否则不进入;load方法用于加载配置文件,创建并初始化Server;start方法用于启动服务器。
setAwait方法:
//org.apache.catalina.startup.Catalinapublic void setAwait(boolean b){ await = b;}
设置await属性的值,await属性会在start方法中的服务器启动完之后使用它来判断是否进入等待状态。
Catalina的laod方法根据conf/server.xml创建了Server对象,并赋值给server属性(具体解析操作时通过开源项目Digester完成的),然后调用了server的init方法,如下:
//org.apache.catalina.startup.Catalinapublic void load(){ long t1 = System.nanoTime(); //省略创建server代码,创建过程使用Digester完成 try{ getServer().init(); }catch(LifecycleException e){ if(Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")){ throw new java.lang.Error(e); }else{ log.error("Catalina.start",e); } } long t2 = System.nanoTime(); if(log.isInfoEnabled()){ //启动过程中,控制台可以看到 log.info("Initialization processed in "+((t2-t1)/1000000)+" ms"); }}
Catalina的start方法主要调用了server的start方法启动服务器,并根据await属性判断是否让程序进入了等待状态:
//org.apache.catalina.satrtup.Catalinapublic void start(){ if(getServer() == null){ load(); } long t1 = System.nanoTime(); try{ //调用Server的start方法启动服务器 getServer().satrt(); }catch(LifecycleException e){ log.fail(sm.getString("catalina.serverStartFail"),e); try{ getServer().destroy(); }catch(LifecycleException el){ log.debug("destory() failed for failed Server ",el); } return; } long t2 = System.nanoTime(); if(log.isInfoEnabled()){ log.info("Server startup in "+ ((t2-t1)/1000000) +" ms"); } //此处省略了注册关闭钩子代码 //进入等待状态 if(await){ await(); stop(); }}
首先判断Server是否已经存在了,如果不存在则调用load方法来初始化Server,然后调用Server的start方法来启动服务器,最后注册了关闭钩子并根据await属性判断是否进入等待状态,之前我们已将这里的await属性设置为true了,所以需要进入等待状态。Server的await方法内部会执行一个while循环,这样程序就停到了await方法,当await方法里的while循环退出时,就会执行stop方法,从而关闭服务器。
Server的启动过程
Server接口中提供了addService(Service service)、removeService(Service service)来添加和删除Service,Server的init方法和start方法分别循环调用了每个Servce的init方法和start方法来启动所有Service。
Server的默认实现是org.apache.catalina.core.StandardServer,StandardServer继承自LifecycleMBeanBase,LifecycleMBeanBase又继承自LifecycleBase,init和start方法就定义在了LifecycleBase中,LifecycleBase里的init方法和start方法又调用了initInternal方法和startInternal方法,这两个方法都是模板方法,由子类具体实现,所以调用StandardServer的init和start方法时会执行StandardServer自己的initInternal和startInternal方法,这就是Tomcat生命周期的管理方式,StandardServer中的initInternal和startInternal方法分别循环调用了每一个service的start和init方法:
//org.apache.catalina.core.StandardServerprotected void startInternal() throws LifecycleException{ ...... sychrnized(servicesLock){ for(int i = 0;i<services.length;i++){ services[i].start(); } }}protected void initInternal() throws LifecycleException{ ...... for(int i = 0;i<services.length;i++){ services[i].init(); }}
除了startInternal和initInternal方法,StandardServer中还实现了await方法,Catalina中就是调用它让服务器进入等待状态的,核心代码:
//org.apache.catalina.core.StandardServerpublic void await(){ //如果端口为-2则不进入循环,直接返回 if(port == -2){ return; } //如果端口为-1则进入循环,而且无法通过网络退出 if(port == -1){ try{ awaitThread = Thread.currentThread(); while(!stopAwait){ try{ Thread.sleep(10000); }catch(InterruptedException ex){ //continue and check the flag } } }finally{ awaitThread = null; } return; } //如果端口不是-1和-2(应该大于0),则会新建一个监听关闭命令的ServerSocket awaitSocket = new ServerSocket(port,1,InetAddress.getByName(address)); while(!stopAwait){ ServerSocket serverSocket = awaitSocket; if(serverSocket == null){ break; } Socket socket = null; StringBuilder command = new StringBuilder(); InputStream stream; socket = serverSocket.accept(); socket.setSoTimeOut(10*1000); stream = socket.getInputStream(); //检查在指定端口接收到的命令是否和shutdown命令匹配 boolean match = command.toString().equals(shutdown); //如果匹配则跳出循环 if(match){ break; } }}
await方法较长,这里省略了一些处理异常、关闭Socket以及对接收到数据处理的代码。处理大概逻辑是首先判断端口号,然后根据port的值分为三种处理方法:
- port为-2,则会直接退出,不进入循环。
- port为-1,则会进入一个while(!stopAwait)的循环,并且在内部没有break跳出的语句,stopAwait标志只有调用了stop方法才会设置为true,所以port为-1时只有在外部调用stop方法擦灰退出循环。
- port为其他值,则也会进入一个while(!stopAwait)的循环,不过同时会在port所在端口启动一个ServerSocket来监听关闭命令,如果接收到了则会使用break跳出循环。这里端口port和关闭命令shutdown是在conf/server.xml文件中配置Server时设置的,默认设置如下:
<!-- server.xml --><Server port="8005" shutdown="SHUTDOWN">
这时会在8005端口监听“SHUTDOWN”命令,如果接收到了就会关闭Tomcat。如果不想使用网络命令来关闭服务器可以将端口设置为-1。另外await方法中从端口接收到数据后还会进行简单处理,如果接收到的数据中有ASCII码小于32的(ASCII中32以下的为控制符)则会从小于32的那个字符截断并丢弃后面的数据。
Service的启动过程
- (四)Tomcat分析
- tomcat源码分析学习笔记(四)
- Tomcat架构(四)
- Tomcat源码分析(四)------ Request和Response处理的全过程
- Tomcat源码分析(四)------ Request和Response处理的全过程
- Tomcat源码分析(四)------ Request和Response处理的全过程
- Tomcat源码分析(四)------ Request和Response处理的全过程 .
- Tomcat源码分析(四)------ Request和Response处理的全过程
- Tomcat中文处理(四)
- ngx_hash分析(四)
- Uboot分析(四)
- Netmap分析(四)
- tomcat请求处理分析(四) 监听请求轮询处理
- Tomcat 架构分析(四) Servlet和Jsp模块
- tomcat(四)
- Tomcat关闭过程(Tomcat源码解析四)
- Tomcat关闭过程(Tomcat源码解析四)
- Tomcat(四):MSM实现Tomcat的session集群
- logistic分类(logistic回归 LR)
- 自定义win32窗口的消息无法监测,窗口注册类不能被注销。
- isset()、empty()、is_null()的区别
- 进阶高级!帮你做能落地的界面之TAB的小短线
- Spring事务管理基础概念讲解 (重点)
- (四)Tomcat分析
- (hdu 1907)John (Misère Nim,Nim博弈的变形)
- Android settings使用详解之设置优先网络
- css3(2)
- git 常见命令记录
- SWF播放器object DEMO
- deep-visualization-toolbox可视化安装
- 模仿微信最近联系人列表长按功能
- WPF 的毛玻璃效果