引言
本文主要介绍下环境准备、启动加载的配置文件的涵义和start.jar的加载原理。
环境准备
1、下载jetty-distribution-8.1.7.v20120910源码包并加入eclipse工程classpath中
2、准备一个war工程
3、拷贝到jetty的webapp目录
4、jetty源码打断点,并在jetty的start.jar目录下执行: java -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8090 -DEOS_DEBUG=true -jar start.jar,注意端口号。
5、要研究初始化可以在server的doStart断点,研究一次请求过程可以在server的handler断点。
配置文件
1、start.ini
指导jetty启动时需要加载的配置文件与顺序。
#etc/jetty-jmx.xml etc/jetty.xml etc/jetty-annotations.xml # etc/jetty-ssl.xml # etc/jetty-requestlog.xml etc/jetty-deploy.xml #etc/jetty-overlay.xml etc/jetty-webapps.xml etc/jetty-contexts.xml etc/jetty-testrealm.xml
这些配置文件的顺序不能错,比如若是将jetty-deploy.xml置于最下面将导致jetty-webapps无法初始化的错误。简单介绍下几个配置的作用:
1)jmx.xml
去掉该注释,容器中的某些类便能在运行时受管理;比如:如果工程需要在不重启的情况下重新读取某配置文件,可以通过信号量的方式或者jmx的方式。这因为该功能管理着工程中需要被管理的类,因此置于最上方。
2)jetty.xml
这个就不解释了,该配制文件就对应了server类,管理着connection,thread pool,handler等。
3)deploy.xml
该配置文件对应于上篇博客描述的deployService,在jetty中就是deployManager,部署管理器,因此如果这玩意位置错了,下面的webapps.xml自然就报错了。
4)webapps.xml
war包的部署器,webappcontext便于基于此生产出来的。
5)contexts.xml
另一种部署器,主要是读取部署目录下的xml文件,不知道用在哪里,先略过。
2、jetty.xml
<Configure id="Server" class="org.eclipse.jetty.server.Server"> <!-- =========================================================== --> <!-- Server Thread Pool --> <!-- =========================================================== --> <Set name="ThreadPool"> <!-- Default queued blocking threadpool --> <New class="org.eclipse.jetty.util.thread.QueuedThreadPool"> <Set name="minThreads">10</Set> <Set name="maxThreads">200</Set> <Set name="detailedDump">false</Set> </New> </Set> <!-- =========================================================== --> <!-- Set connectors --> <!-- =========================================================== --> <Call name="addConnector"> <Arg> <New class="org.eclipse.jetty.server.nio.SelectChannelConnector"> <Set name="host"><Property name="jetty.host" /></Set> <Set name="port"><Property name="jetty.port" default="8888"/></Set> <Set name="maxIdleTime">300000</Set> <Set name="Acceptors">2</Set> <Set name="statsOn">false</Set> <Set name="confidentialPort">8443</Set> <Set name="lowResourcesConnections">20000</Set> <Set name="lowResourcesMaxIdleTime">5000</Set> </New> </Arg> </Call> <!-- =========================================================== --> <!-- Set handler Collection Structure --> <!-- =========================================================== --> <Set name="handler"> <New id="Handlers" class="org.eclipse.jetty.server.handler.HandlerCollection"> <Set name="handlers"> <Array type="org.eclipse.jetty.server.Handler"> <Item> <New id="Contexts" class="org.eclipse.jetty.server.handler.ContextHandlerCollection"/> </Item> <Item> <New id="DefaultHandler" class="org.eclipse.jetty.server.handler.DefaultHandler"/> </Item> </Array> </Set> </New> </Set> </Configure>
可以看出默认的io为nio,hanler是server中比较核心的方法,Contexts是一个webappcontext的集合,请求进来的时候Contexts负责将请求分发给具体的app;而DefaultHandler即返回404错误页面,其实从这个handlers就可以看出来,请求会一次经过这个handler,一旦被hanlder即提交退出,直到不被handler走到了DefaultHandler里面返回404的页面。这里面其实自定义handler加载handlers的头部处理一些静态资源的访问,这样就直接绕过了应用webappcontext的访问了,加快了静态资源的处理速度。eg:RerourceHandler.
3、deploy.xml
<Configure id="Server" class="org.eclipse.jetty.server.Server"> <Call name="addBean"> <Arg> <New id="DeploymentManager" class="org.eclipse.jetty.deploy.DeploymentManager"> <Set name="contexts"> <Ref id="Contexts" /> </Set> <Call name="setContextAttribute"> <Arg>org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern</Arg> <Arg>.*/servlet-api-[^/]*\.jar$</Arg> </Call> </New> </Arg> </Call> </Configure>
调用addBean方法,Ref = contexts 即上面server中已经new出来的 ContextHandlerCollection,持有该contexts旨在注册所有由该类生产出来的一个个app(即WebAppContextHandler)。
4、webapps.xml
<Configure id="Server" class="org.eclipse.jetty.server.Server"> <Ref id="DeploymentManager"> <Call id="webappprovider" name="addAppProvider"> <Arg> <New class="org.eclipse.jetty.deploy.providers.WebAppProvider"> <Set name="monitoredDirName"><Property name="jetty.home" default="." />/webapps</Set> <Set name="defaultsDescriptor"><Property name="jetty.home" default="."/>/etc/webdefault.xml</Set> <Set name="scanInterval">1</Set> <Set name="contextXmlDir"><Property name="jetty.home" default="." />/contexts</Set> <Set name="extractWars">true</Set> </New> </Arg> </Call> </Ref> </Configure>
war工程的部署器,人如其名:WebAppProvider。从下面配的参数不难看出其主要功能,它主要是负责扫描目录下的war工程并生产ContextHandler(其实就是servletContext,对应于一个个app),而WebAppProvider又是受上面deployManager管理,生产出来的ContextHandler被deployManager注册到ContextHandlerCollection中。
start.jar加载原理
1、解析命令行参数
List arguments = new ArrayList(); for (String arg1 : args) { if ((arg1.startsWith("--ini=")) || (arg1.equals("--ini"))) { arguments.addAll(loadStartIni(arg1.substring(6))); } else if (arg1.startsWith("--config=")) { this._startConfig = arg1.substring(9); } else { arguments.add(arg1); } } if (!(ini)){ arguments.addAll(0, loadStartIni(null));}
先是解析命令行的参数,最后将ini配置文件merge起来。
2、定位加载资源并依据配置文件反射调用相应的类
// For all arguments, load properties or parse XMLs XmlConfiguration last = null; Object[] obj = new Object[args.length]; for ( int i = 0; i < args.length; i++ ) { if ( args[i].toLowerCase().endsWith( ".properties" ) ) { properties.load( Resource.newResource( args[i] ).getInputStream() ); } else { XmlConfiguration configuration = new XmlConfiguration( Resource.newResource( args[i] ).getURL() ); if ( last != null ) configuration.getIdMap().putAll( last.getIdMap() ); if ( properties.size() > 0 ) configuration.setProperties( properties ); obj[i] = configuration.configure(); last = configuration; } } // For all objects created by XmlConfigurations, start them if they are lifecycles. for ( int i = 0; i < args.length; i++ ) { if ( obj[i] instanceof LifeCycle ) { LifeCycle lc = (LifeCycle) obj[i]; if ( !lc.isRunning() ) lc.start(); } }分为两步,先是依据xm描述的内容依次解析xml并处理逻辑,期间会生成很多个实例,如果实例实现了LifeCycle接口再依次调用start方法,因此server就是在此被start起来了。
嵌入式Jetty的启动
很简单的有木有,该server只有一个webapp的handler,因此只能服务一个应用,而且错误页面自然也不是我们常见的404页面,虽然6000W做不了杀人机器,6000块也是可以做便宜的王太太百科全书的嘛!