jetty启动分析 jetty启动命令:java -jar /usr/alibaba/jetty/start.jar -Djetty.home=/usr/alibaba/jetty --ini=/home/admin/deploy/jetty/start.ini 如果加上--help参数,则显示jetty启动的参数设置和配置设置,如下: #java -jar /usr/alibaba/jetty/start.jar -Djetty.home=/usr/alibaba/jetty --ini=/home/admin/deploy/jetty/start.ini --help properties={jetty.home=/usr/alibaba/install/jetty-distribution-7.2.0, path=/home/admin/deploy/jetty/lib/ext, OPTIONS=Server,ajp,ext,jmx,jsp,resources,websocket, lib=/home/admin/deploy/jetty/lib/ext}启动时所加载的配置选项。 Usage: java -jar start.jar [options...] [properties...] [configs...] The start.jar builds a classpath and executes a main java class with a classloader built from that classpath. By default the start.jar mechanism is configured to start the jetty server, but it can be configured to start any java main class. start.java里面会执行main.java这个类的main函数,由这个函数去读取start.conf配置,这个配置文件配置的jetty的相关环境变量和配置文件路径,以 及接下来需要执行的main类。 Command Line Options: --help This help / usage information. 显示帮助信息 --version Print the version information for Jetty and dependent jars, then exit. 显示jetty的版本 --list-options List the details of each classpath OPTION 显示启动需要的options信息,包括:=Server,ajp,ext,jmx,jsp,resources,websocket等,可以自己指定jetty启动的特性选项。每个option的实现是在独立的jar里, 这里配置上了jetty启动时就会去load这个jar包。 --list-config List the start.config file. 显示main类启动的配置,对应配置文件start.config --dry-run Print the command line that the start.jar generates, then exit. This may be used to generate command lines when the start.ini includes -X or -D arguments. 显示用java -jar执行时的classpath路径等完整的java命令信息,在调试模式下有用 // Show Command Line to execute Jetty --exec Run the generated command line (see --dry-run) in a sub processes. This can be used when start.ini contains -X or -D arguments, but creates an extra JVM instance. 重新启动一个jvm进程 // Show Command Line to execute Jetty --stop Stop the running Jetty instance. 停止jetty进程 --daemon Start in daemon mode with stderr and stdout redirected to ${jetty.log}/start.log 是否开启守护进程模式 --config=<file> Specify an alternate start.config file. The default is the start.config file inside the start.jar. The default can also be specified with the START system property. 重新指定config配置的路径 --ini=<file> Load command line arguments from a file. If no --ini options are specified, then the start.ini file will be read if it exists. A --ini option with no file indicates that start.ini should not be read. 启动jetty进程的配置 --pre=<file> Specify a configuration file that is to be processed before any configuration files listed in start.ini 在加载start.ini里面执行的xml配置文件之前需要先加载的配置文件 System Properties: 启动时可以设置jetty的系统环境变量 These are set with a command line like "java -Dname=value ..." and are accessible via the java.lang.System#getProperty(String) API. Some key system properties are: org.eclipse.jetty.util.log.class=[class] jetty加载的日志类 A Low Level Jetty Logger Implementation to use (default: org.eclipse.jetty.util.log.Slf4jLog) org.eclipse.jetty.util.log.DEBUG=[boolean] 是否打开日志debug模式 Debug logging for the stderr and javautil Loggers. Slf4j and other loggers must be separately configured for debug. (default: false) org.eclipse.jetty.util.log.IGNORED=[boolean] 是否打开日志的ignored模式 Ignored exceptions are logged, independent of DEBUG settings (default: false) org.eclipse.jetty.util.log.SOURCE=[boolean]源代码的位置是否打印在错误日志上 The source location of logs is logged in the stderr Logger. (default: false) com.sun.management.jmxremote 是否开通JMX功能 Enable remote JMX management in Sun JVMS. Properties: jetty启动的参数设置 These are set with a command line like "java -jar start.jar name=value" and only affect the start mechanism. Some of these are defined in the default start.config and will not be available if another configuration file is used. NOTE: Not all properties are listed here: path=[directory] An additional class path element to add to the started class path. Typically this is used to add directories of classes and/or resources lib=[directory] An additional library directory to add to the started class path. This must be a (deep) directory of jars STOP.PORT=[number] The port to use to stop the running Jetty server. Required along with STOP.KEY if you want to use the --stop option above. STOP.KEY=[alphanumeric] The passphrase defined to stop the server. Requried along with STOP.PORT if you want to use the --stop option above. DEBUG=true Enable debug on the start mechanism and sets the org.eclipse.jetty.util.log.stderr.DEBUG system property to true. (default: false) OPTIONS=[option,option,...] options列表 Enable classpath OPTIONS. Each options represents one or more jars to be added to the classpath. The options are defined in the start.config file and can be listed with --help or --list-options. By convention, options starting with a capital letter (eg Server) are aggregations of other available options. Available OPTIONS: All Client Server ajp annotations client default deploy ext jmx jndi jsp jta plus policy resources rewrite security server servlet servlets setuid webapp websocket xml Available Configurations: 默认配置列表 By convention, configuration files are kept in $JETTY_HOME/etc. The known configuration files are: etc/jetty-ajp.xml ajp支持配置 etc/jetty-bio-ssl.xml 阻塞io的ssl支持 etc/jetty-bio.xml 阻塞io etc/jetty-contexts.xml contexts上下文配置支持 etc/jetty-debug.xml debug模式支持 etc/jetty-deploy.xml war部署支持 etc/jetty-fileserver.xml 文件服务器支持 etc/jetty-ipaccess.xml ip过滤支持 etc/jetty-jmx.xml jmx支持 etc/jetty-logging.xml 日志系统支持 etc/jetty-plus.xml 增强功能支持 jaas jndi etc/jetty-policy.xml java的policy权限支持 etc/jetty-proxy.xml 代理服务器支持 etc/jetty-requestlog.xml cookie log日志支持 etc/jetty-rewrite.xml url rewrite支持 etc/jetty-ssl.xml ssl支持 etc/jetty-stats.xml 统计功能支持 etc/jetty-testrealm.xml 配置权限登录service etc/jetty-webapps.xml web app功能支持 etc/jetty-xinetd.xml 使用inetd/xinetd配置jetty etc/jetty.xml 基础配置 Defaults: 默认配置 A start.ini file may be used to specify default arguments to start.jar, which are used if no command line arguments are provided and override the defaults in the start.config file. If --ini options are provided on the command line, then start.ini will no be read. The current start.ini arguments are: OPTIONS=Server,jsp,jmx,resources,websocket,ext etc/jetty.xml etc/jetty-deploy.xml etc/jetty-webapps.xml etc/jetty-contexts.xml etc/jetty-testrealm.xml main函数启动:org.eclipse.jetty.start.main 需要配置启动参数,start.ini的路径 config类的说明: The behaviour of Main is controlled by the <code>"org/eclipse/start/start.config main类调用org.eclipse.jetty.start.config去加载所有的配置和配置文件,然后通过加载org/eclipse/start/start.config,这个配置文件里面有两行: # The main class to run org.eclipse.jetty.xml.XmlConfiguration.class ${start.class}.class property start.class 然后main类就回反射start.config配置的这个class类去执行它的main方法, org.eclipse.jetty.xml.XmlConfiguration这个类就是具体解析jetty.xml等所有xml配置文件中配置, 都是关于server类的配置,这个类通过解析所有的xml文件通过反射去生成最终的org.eclipse.jetty.server.server(这个类就是jetty的服务器最核心的类) XmlConfiguration.java的main方法代码: public static void main( final String[] args ) //args就是start.ini里面配置的加载的xml文件 { AccessController.doPrivileged( new PrivilegedAction() { public Object run() { try { Properties properties=null; // Look for properties from start.jar try { Class<?> config = XmlConfiguration.class.getClassLoader().loadClass("org.eclipse.jetty.start.Config"); properties=(Properties)config.getMethod("getProperties").invoke(null); Log.debug("org.eclipse.jetty.start.Config properties = {}",properties); //加载config里面的环境变量 } catch(NoClassDefFoundError e) { Log.ignore(e); } catch(ClassNotFoundException e) { Log.ignore(e); } catch(Exception e) { Log.warn(e); } // If no start.config properties, use clean slate if (properties==null) properties = new Properties(); // 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(); //对每个配置文件进行解析,反射得到对应的反射出来的类,主要是Server这个类 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() ) //对每个反射出来的类,如果是实现LifeCycle,则启动对应的类, lc.start(); } } } catch (AccessControlException ace) { ace.printStackTrace(System.err); } catch ( Exception e ) { Log.warn( Log.EXCEPTION, e ); } return null; } } ); } LifeCycle接口:jetty的各个组件的生命周期管理的接口, jetty服务器的配置文件配置的都是"org.eclipse.jetty.server.Server"这个类,这个类包装了连接器,处理器,context上下文等信息,是jetty的主入口 在jetty.xml等配置文件能够配置的标签为: <Configure id="Server" class="org.eclipse.jetty.server.Server">下面的子节点标签只能是下面列出来的几个: String tag = node.getTag(); if ("Set".equals(tag)) //通过调用server类的set方法去注入相关参数或者bean set(obj, node); else if ("Put".equals(tag)) //map注入时使用put方法去插入 put(obj, node); else if ("Call".equals(tag)) //调用call的方法 call(obj, node); else if ("Get".equals(tag)) //调用get方法,通过get取出值之后再通过Ref注入到需要的bean中 get(obj, node); else if ("New".equals(tag)) //new一个新的对象 newObj(obj, node); else if ("Array".equals(tag)) //new 一个Array对象 newArray(obj, node); else if ("Ref".equals(tag)) //引用一个bean refObj(obj, node); else if ("Property".equals(tag)) //基本类型注入 propertyObj(obj, node); else throw new IllegalStateException("Unknown tag: " + tag); <Configure id="Server" class="org.eclipse.jetty.server.Server"> <!-- =========================================================== --> <!-- configure rewrite handler --> <!-- =========================================================== --> <Get id="oldhandler" name="handler"/> <Set name="handler"> <New id="Rewrite" class="org.eclipse.jetty.rewrite.handler.RewriteHandler"> <Set name="handler"><Ref id="oldhandler"/></Set> <Ref id="DeploymentManager"> <Call name="addAppProvider"> <Arg> <New class="org.eclipse.jetty.deploy.providers.WebAppProvider"> <Set name="monitoredDir"><Property name="jetty.home" default="." />/webapps</Set> 线程池分析 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> //////////////////////////////////////////////////////// 这里能设置的属性:参见org.eclipse.jetty.util.thread.QueuedThreadPool 线程池实现 private BlockingQueue<Runnable> _jobs; 任务队列 private String _name; 线程池名字 private int _maxIdleTimeMs=60000; 线程最大空闲时间 private int _maxThreads=254; 最大线程数 private int _minThreads=8; 最小线程数 private int _maxQueued=-1; 任务队列数,每个线程队列的线程容量是_minThreads,每次以_minThreads增长 private int _priority=Thread.NORM_PRIORITY; 线程优先级 private boolean _daemon=false; 是否以守护线程启动 private int _maxStopTime=100; 服务停止时 让还在运行的线程还能运行的最长时间,在doStop()中使用 单位毫秒 .... dostart()方法中 if (_jobs==null) { _jobs=_maxQueued>0 ?new ArrayBlockingQueue<Runnable>(_maxQueued) :new BlockingArrayQueue<Runnable>(_minThreads,_minThreads); } .... ThreadPool可以用以下三种实现: http://daizuan.iteye.com/blog/1114372 QueuedThreadPool 使用jdk1.5的并发包得特性(AtomicInteger,ConcurrentLinkedQueue)来实现 ExecutorThreadPool 使用jdk1.5自带的ThreadPoolExecutor OldQueuedThreadPool 功能跟QueuedThreadPool类似,但是实现是用加锁等jdk1.5以前已有的特性去实现,性能应该不如QueuedThreadPool //////////////////////////////////////////////////////// </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="8080"/></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> ////////////////////////////////////////////////////////////////////////////// Connector分析: AbstractConnector.java能够设置的参数: private String _host; private int _port = 0; private String _integralScheme = HttpSchemes.HTTPS; 加密协议 private int _integralPort = 0; 加密端口 private String _confidentialScheme = HttpSchemes.HTTPS; 信任协议 private int _confidentialPort = 0; 信任端口 private int _acceptQueueSize = 0; backlog 请求等待队列长度 private int _acceptors = 1; 等待请求连接的线程数 private int _acceptorPriorityOffset = 0; 连接器线程的优先级 current.setPriority(old_priority - _acceptorPriorityOffset); private boolean _useDNS; 是否使用DNS查找主机名,主机名会带入request中 private boolean _forwarded; //是否把请求头带入request中 private String _hostHeader; //请求头 private String _forwardedHostHeader = "X-Forwarded-Host"; private String _forwardedServerHeader = "X-Forwarded-Server"; private String _forwardedForHeader = "X-Forwarded-For"; private String _forwardedProtoHeader = "X-Forwarded-Proto"; private boolean _reuseAddress = true; protected int _maxIdleTime = 200000; //线程最大空闲时间 protected int _lowResourceMaxIdleTime = -1; 在请求队列大于空闲线程时,线程的空闲时间设置 protected int _soLingerTime = -1; public void setStatsOn(boolean on) 是否开启统计功能 /** connections to server */ private final CounterStatistic _connectionStats = new CounterStatistic(); /** requests per connection */ private final SampleStatistic _requestStats = new SampleStatistic(); /** duration of a connection */ private final SampleStatistic _connectionDurationStats = new SampleStatistic(); SO_LINGER选项 1) 设置该选项:public void setSoLinger(boolean on, int seconds) throws SocketException 2) 读取该选项:public int getSoLinger() throws SocketException 3) SO_LINGER选项用来控制Socket关闭时的行为。 l socket.setSoLinger(true,0):执行Socket的close()方法时,该方法也会立即返回,但底层的Socket也会立即关闭,所有未发送完的剩余数据被丢弃。 l socket.setSoLinger(true,3600):执行Socket的close()方法时,该方法不会立即返回,而进入阻塞状态,同时,底层的Socket会尝试发送剩余的数据。只有满足以下两个条件之一,close()方法才返回: n 底层的Socket已经发送完所有的剩余数据。 n 尽管底层的Socket还没有发送完所有的剩余数据,但已经阻塞了3600秒。close()方法的阻塞时间超过3600秒,也会返回,剩余未发送的数据被丢弃。 以上两种情况内,当close()方法返回后,底层的Socket会被关闭,断开连接。 4) setSoLinger(boolean on ,int second)方法中的seconds参数以秒为单位,而不是以毫秒为单位。 socket用法:http://www.cnblogs.com/jerrychoi/archive/2010/04/15/1712931.html 安全协议在下面两个类会被调用: 在ConstraintSecurityHandler.java会根据不同的安全选项进行不同的处理 UserDataConstraint dataConstraint = roleInfo.getUserDataConstraint(); if (dataConstraint == null || dataConstraint == UserDataConstraint.None) { return true; } HttpConnection connection = HttpConnection.getCurrentConnection(); Connector connector = connection.getConnector(); if (dataConstraint == UserDataConstraint.Integral) { if (connector.isIntegral(request)) return true; if (connector.getConfidentialPort() > 0) { String url = connector.getIntegralScheme() + "://" + request.getServerName() + ":" + connector.getIntegralPort() + request.getRequestURI(); if (request.getQueryString() != null) url += "?" + request.getQueryString(); response.setContentLength(0); response.sendRedirect(url); } else response.sendError(Response.SC_FORBIDDEN,"!Integral"); request.setHandled(true); return false; } else if (dataConstraint == UserDataConstraint.Confidential) { if (connector.isConfidential(request)) return true; if (connector.getConfidentialPort() > 0) { String url = connector.getConfidentialScheme() + "://" + request.getServerName() + ":" + connector.getConfidentialPort() + request.getRequestURI(); if (request.getQueryString() != null) url += "?" + request.getQueryString(); response.setContentLength(0); response.sendRedirect(url); } else response.sendError(Response.SC_FORBIDDEN,"!Confidential"); request.setHandled(true); return false; } Connect的类图结构: AbstractNIOConnector 下面的SelectChannelConnector和BlockingChannelConnector是通过连接字通道来配置NIO。 NIO跟BIO的区别:在socket连接上设置Blocking为true,表示是阻塞同步,如果为false表示异步非阻塞。 BlockingChannelConnector.java SocketChannel channel = _acceptChannel.accept(); channel.configureBlocking(true); Socket socket=channel.socket(); configure(socket); BlockingChannelEndPoint connection=new BlockingChannelEndPoint(channel); connection.dispatch(); SelectChannelConnector.java SocketChannel channel = server.accept(); channel.configureBlocking(false); Socket socket = channel.socket(); configure(socket); _manager.register(channel); SelectChannelConnector连接器接受到请求之后,注册到SelectorManager上,由SelectorManager的doSelect去做异步处理。异步的实现方式是采用jdk1.6的select 机制。 SocketConnector这个是阻塞IO的一种实现。在这个类里有说明:This Connector should only be used if NIO is not available. SocketConnector —— 当连接请求相对较少或者NIO特性不可用时,可以使用这个连接器; BlockingChannelConnector —— 连接请求相对较少时用(要求NIO特性可用) SelectChannelConnector —— 当连接请求量比较大,或者需要异步处理Ajax请求 SslSocketConnector —— 没有NIO特性的SSL连接器 SslSelectChannelConnector —— 具有NIO特性的SSL连接器 AJPConnector —— 提供对apache mod_jk或者mod_proxy_ajp请求的支持,它是针对AJP协议的实现。 Handler分析: ////////////////////////////////////////////////////////////////////////////// <!-- =========================================================== --> <!-- 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> <Call name="addBean"> <Arg> <New id="DeploymentManager" class="org.eclipse.jetty.deploy.DeploymentManager"> <Set name="contexts"> <Ref id="Contexts" /> //上面的contexts在这里引用 </Set> <Call name="setContextAttribute"> <Arg>org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern</Arg> <Arg>.*/jsp-api-[^/]*\.jar$|.*/jsp-[^/]*\.jar$</Arg> </Call> </New> </Arg> </Call> <Ref id="DeploymentManager"> <Call name="addAppProvider"> <Arg> <New class="org.eclipse.jetty.deploy.providers.WebAppProvider">//web应用加载器 <Set name="monitoredDir"><Property name="jetty.home" default="." />/webapps</Set> //web应用加载目录配置 <Set name="defaultsDescriptor"><Property name="jetty.home" default="."/>/etc/webdefault.xml</Set>//web应用默认配置 <Set name="scanInterval">1</Set> //热部署扫描间隔 单位:秒 <Set name="contextXmlDir"><Property name="jetty.home" default="." />/contexts</Set> //context配置文件路径 <Set name="extractWars">false</Set> //是否解压war包 </New> </Arg> </Call> </Ref> </Configure> ///////////////////////////////////////////////////////////////////// 处理器组件主要用于处理已经接收到的请求。它的主要API是一个处理函数: public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException 参数说明: target —— 请求的目标,通常是一个URI,或者一个资源名称。有点类似于Servlet的名字。 baseRequest —— 未被封装的请求对象 request —— 可能是Request对象(Jetty的HttpServletRequest实现),或者是一个封装过的请求。用户可以通过使用HttpConnection.getCurrentConnection()方法来访问Request对象。 response —— Response对象(Jetty的HttpServletResponse实现),或者是一个封装过的响应。用户可以通过使用HttpConnection.getCurrentConnection()方法来访问Request对象。 一个Handler实现可以直接处理请求,或者将请求转发给其它的Handler(如Servlet等),也可以对请求中包含的数据进行修改后再转发。根据处理器的功能,可以分为三类: 1.路由处理器 – 这类处理器的主要作用是将请求进行路由,转发至合适的处理器进行处理,常用的有HandlerCollection, ContextHandlerCollection 2.过滤器 – 对请求的数据进行分析处理,然后再转发给其它处理器。如HandlerWrapper, ContextHandler, SessionHandler 3.处理器 – 这类处理器就是Servlet之类的了,它主要的功能是业务逻辑处理,并返回用户请求的数据。如ResourceHandler, ServletHandler。 Handler的类图结构: Handler主要有介绍下面几个: HandlerCollection.java: Handler集合,按照list的顺序执行。 public class HandlerCollection extends AbstractHandlerContainer { private final boolean _mutableWhenRunning; 默认为false,可以通过构造函数传入。如果为true,则在启动后不能再增加handler。 private volatile Handler[] _handlers; handler集合,通过配置文件注入 private boolean _parallelStart=true; 可以通过配置文件改变,如果为true,等需要等待所有handler启动完成之后才算启动完成。 这里用到java并发包得countdownwatch。 if (_parallelStart) { final CountDownLatch latch = new CountDownLatch(_handlers.length); for (int i=0;i<_handlers.length;i++) { final int h=i; getServer().getThreadPool().dispatch( new Runnable() { public void run() { try{_handlers[h].start();} catch(Throwable e){mex.add(e);} finally{latch.countDown();} } } ); } latch.await(); } else { for (int i=0;i<_handlers.length;i++) { try{_handlers[i].start();} catch(Throwable e){mex.add(e);} } } ContextHandlerCollection.java 由DeployManager.java的org.eclipse.jetty.deploy.providers.WebAppProvide类扫描monitoredDir属性下面的war包后,创建WebAppContext,put到 ContextHandlerCollection的handler集合中,WebAppContext是处理J2ee标准下得war包应用的。 Context是一种特殊的处理器(Handler),它的主要功能是将一组处理同一URI路径上的请求的处理器组织起来。一般的Context实现都会具备以下功能: 1.上下文路径 —— 用于标识哪些请求会被该上下文处理,如”/myapp” 2.静态资源路径 —— 或者说是Web应用的路径 如/www/WebRoot 3.类加载器 —— 用于加载分配至该Context的类,如/www/WebRoot/WEB-INF/classes Jetty中的Context实现有 1.ContextHandler 2.ServletContext 3.WebApplicationContext Web应用的上下文是通过使用web.xml描述文件将安全处理器(SecurityHandler)、会话处理器(SessionHandler)、Servlet等处理器组织在一起的。 DefaultHandler.java:处理没有被上面其他的handler处理的请求,包括favicon.ico,404页面,OPTIONS and TRACE methods boolean _serveIcon=true; 是否显示服务器的icon boolean _showContexts=true; 如果RequestURI不是/,如果这个值为true,则显示应用的context信息, DebugHandler.java 调试模式下打印request和response的信息 <New id="DebugHandler" class="org.eclipse.jetty.server.handler.DebugHandler"> <Set name="handler"><Ref id="oldhandler"/></Set> <Set name="outputStream"> <New class="org.eclipse.jetty.util.RolloverFileOutputStream"> <Arg type="String"><Property name="jetty.logs" default="./logs"/>/yyyy_mm_dd.debug.log</Arg> <Arg type="boolean">true</Arg> <!-- append --> <Arg type="int">90</Arg> <!-- retain days --> </New> </Set> RequestLogHandler.java 打印request路径的handler <Call name="addHandler"> <Arg> <New id="RequestLog" class="org.eclipse.jetty.server.handler.RequestLogHandler"> <Set name="requestLog"> <New id="RequestLogImpl" class="org.eclipse.jetty.server.NCSARequestLog"> <Set name="filename"><Property name="jetty.logs" default="./logs"/>/yyyy_mm_dd.request.log</Set> <Set name="filenameDateFormat">yyyy_MM_dd</Set> <Set name="retainDays">90</Set> <Set name="append">true</Set> <Set name="extended">false</Set> <Set name="logCookies">false</Set> <Set name="LogTimeZone">GMT</Set> </New> </Set> </New> </Arg> </Call> IPAccessHandler.java 配置应用访问的ip白名单和黑名单 public class IPAccessHandler extends HandlerWrapper { IPAddressMap<PathMap> _white = new IPAddressMap<PathMap>(); IPAddressMap<PathMap> _black = new IPAddressMap<PathMap>(); 配置如下: <New id="IPAccessHandler" class="org.eclipse.jetty.server.handler.IPAccessHandler"> <Set name="handler"><Ref id="oldhandler"/></Set> <Set name="white"> <Array type="String"> <Item>127.0.0.1</Item> <Item>127.0.0.2/*.html</Item> </Array> </Set> <Set name="black"> <Array type="String"> <Item>127.0.0.1/blacklisted</Item> <Item>127.0.0.2/black.html</Item> </Array> </Set> RewriteHandler.java 配置rewrite规则的handler,详细见 public class RewriteHandler extends HandlerWrapper { private RuleContainer _rules; 配置如下: <New id="Rewrite" class="org.eclipse.jetty.rewrite.handler.RewriteHandler"> <Set name="handler"><Ref id="oldhandler"/></Set> <Set name="rewriteRequestURI">true</Set> <Set name="rewritePathInfo">false</Set> <Set name="originalPathAttribute">requestedPath</Set> <!-- Add rule to protect against IE ssl bug --> <Call name="addRule"> <Arg> <New class="org.eclipse.jetty.rewrite.handler.MsieSslRule"/> </Arg> </Call> <!-- protect favicon handling --> <Call name="addRule"> <Arg> <New class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule"> <Set name="pattern">/favicon.ico</Set> <Set name="name">Cache-Control</Set> <Set name="value">Max-Age=3600,public</Set> <Set name="terminating">true</Set> </New> </Arg> </Call> <!-- redirect from the welcome page to a specific page --> <Call name="addRule"> <Arg> <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule"> <Set name="pattern">/rewrite/</Set> <Set name="replacement">/rewrite/info.html</Set> </New> </Arg> </Call> <!-- replace the entire request URI --> <Call name="addRule"> <Arg> <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule"> <Set name="pattern">/some/old/context</Set> <Set name="replacement">/rewritten/newcontext</Set> </New> </Arg> </Call> <!-- replace the beginning of the request URI --> <Call name="addRule"> <Arg> <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule"> <Set name="pattern">/rewrite/for/*</Set> <Set name="replacement">/rewritten/</Set> </New> </Arg> </Call> <!-- reverse the order of the path sections --> <Call name="addRule"> <Arg> <New class="org.eclipse.jetty.rewrite.handler.RewriteRegexRule"> <Set name="regex">(.*?)/reverse/([^/]*)/(.*)</Set> <Set name="replacement">$1/reverse/$3/$2</Set> </New> </Arg> </Call> <!-- add a cookie to each path visited --> <Call name="addRule"> <Arg> <New class="org.eclipse.jetty.rewrite.handler.CookiePatternRule"> <Set name="pattern">/*</Set> <Set name="name">visited</Set> <Set name="value">yes</Set> </New> </Arg> </Call> <!-- actual redirect, instead of internal rewrite --> <Call name="addRule"> <Arg> <New class="org.eclipse.jetty.rewrite.handler.RedirectPatternRule"> <Set name="pattern">/redirect/*</Set> <Set name="location">/redirected</Set> </New> </Arg> </Call> <!-- add a response rule --> <Call name="addRule"> <Arg> <New class="org.eclipse.jetty.rewrite.handler.ResponsePatternRule"> <Set name="pattern">/400Error</Set> <Set name="code">400</Set> <Set name="reason">ResponsePatternRule Demo</Set> </New> </Arg> </Call> </New> </Set> SessionHandler.java session管理,在cookie和URI中查找 Look for a requested session ID in cookies and URI parameters,默认sessionid的key为JSESSIONID 有两种实现:HashSessionManager和JDBCSessionManager。前面是存放在内存的session管理,后面是基于数据库的session管理。 StatisticsHandler.java 服务器监控统计的handler ConstraintSecurityHandler.java 安全验证的handler ErrorPageErrorHandler.java 处理出错页面的handler ServletHandler.java This handler does not implement the full J2EE features and is intended to be used when a full web application is not required. Specifically filters and request wrapping are not supported. ResourceHandler.java 资源文件处理handler /////////////////////////////////////////// ///////////////////////// <!-- =========================================================== --> <!-- extra options --> <!-- =========================================================== --> <Set name="stopAtShutdown">true</Set> stop的时候先去把相关的lifecycle接口的实现先stop掉 <Set name="sendServerVersion">true</Set> 是否发送jetty的版本,在HttpConnection类调用 <Set name="sendDateHeader">true</Set> 是否发送头信息,在requestHandler调用 <Set name="gracefulShutdown">1000</Set> 等待connect close和context shutdown之后休眠1000毫秒再stop相关的connector,handler等 </Configure> context: 各个子模块的学习:(待续) Deploy: rewrite: 由RewriteHandler.java处理,在rewrite处理完之后调用上级handler去处理。 <Configure id="Server" class="org.eclipse.jetty.server.Server"> <!-- =========================================================== --> <!-- configure rewrite handler --> <!-- =========================================================== --> <Get id="oldhandler" name="handler"/> <Set name="handler"> <New id="Rewrite" class="org.eclipse.jetty.rewrite.handler.RewriteHandler"> <Set name="handler"><Ref id="oldhandler"/></Set> <Set name="rewriteRequestURI">true</Set> <Set name="rewritePathInfo">false</Set> <Set name="originalPathAttribute">requestedPath</Set> <!-- Add rule to protect against IE ssl bug --> <Call name="addRule"> <Arg> <New class="org.eclipse.jetty.rewrite.handler.MsieSslRule"/> </Arg> </Call> <!-- protect favicon handling --> <Call name="addRule"> <Arg> <New class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule"> <Set name="pattern">/favicon.ico</Set> <Set name="name">Cache-Control</Set> <Set name="value">Max-Age=3600,public</Set> <Set name="terminating">true</Set> </New> </Arg> </Call> ...... rewrite规则包括: MsieSslRule.java修复IE6以下的bug,增加response.setHeader(HttpHeaders.CONNECTION, HttpHeaderValues.CLOSE); HeaderPatternRule.java 在request更新头字段 RewritePatternRule.java 把确定的请求重写为另外一个url RewriteRegexRule.java 用正则表达式去重写url CookiePatternRule.java 在cookie中增加一个参数 RedirectPatternRule.java 重写redirect 的url ResponsePatternRule.java 修改response的输出 ForwardedSchemeHeaderRule.java 重写forward的schema jmx: jndi: 参考资料:http://baike.baidu.com/view/209575.htm http://blog.csdn.net/lovingprince/article/details/6364767 OSGI: security: 对应handler:ConstraintSecurityHandler.java继承自SecurityHandler.java抽象类 JDBCLoginService.java默认实现 验证方式:了解了这几种验证的代码实现 都继承子LoginAuthenticator.java抽象类,这个抽象类实现Authenticator.java这个接口,采用适配器模式 BasicAuthenticator.java ClientCertAuthenticator.java DigestAuthenticator.java FormAuthenticator.java 在SecurityHandler.java的生命周期方法dostart()时,会根据配置从factory中获得对应的验证器 if (_authenticator==null && _authenticatorFactory!=null && _identityService!=null) { _authenticator=_authenticatorFactory.getAuthenticator(getServer(),ContextHandler.getCurrentContext(),this, _identityService, _loginService); if (_authenticator!=null) _authMethod=_authenticator.getAuthMethod(); } 对应的DefaultAuthenticatorFactory.java工厂方法中的处理: public Authenticator getAuthenticator(Server server, ServletContext context, AuthConfiguration configuration, IdentityService identityService, LoginService loginService) { String auth=configuration.getAuthMethod(); Authenticator authenticator=null; if (auth==null || Constraint.__BASIC_AUTH.equalsIgnoreCase(auth)) authenticator=new BasicAuthenticator(); else if (Constraint.__DIGEST_AUTH.equalsIgnoreCase(auth)) authenticator=new DigestAuthenticator(); else if (Constraint.__FORM_AUTH.equalsIgnoreCase(auth)) authenticator=new FormAuthenticator(); if (Constraint.__CERT_AUTH.equalsIgnoreCase(auth)||Constraint.__CERT_AUTH2.equalsIgnoreCase(auth)) authenticator=new ClientCertAuthenticator(); return authenticator; } policy: servlet: servlets: servlet中的Filter部分 CGI.java 处理cgi请求的, 主要处理:通过分析请求得到执行的CGI脚本,使用java的exec执行脚本后把输出copy到serlvetoutputstream,需要准备相关的环境变量 Process p=(dir==null)?Runtime.getRuntime().exec(execCmd,env.getEnvArray()):Runtime.getRuntime().exec(execCmd,env.getEnvArray(),dir); CloseableDoSFilter.java 允许通过jettyAPI去关闭请求连接 DoSFilter.java 防止dos攻击的filter。只要通过限制每秒最大处理请求数,每个请求最长处理时间,以及通过ip和端口去方式dos攻击。 ConcatServlet.java 可以在参数里面设置多个资源合并一次下载。 <script type="text/javascript" src="../concat?/js/behaviour.js&/js/ajax.js&/chat/chat.js"></script> 处理代码: String[] parts = q.split("\\&");...... if (type!=null) resp.setContentType(type); for (int i=0;i<parts.length;i++) { RequestDispatcher dispatcher=_context.getRequestDispatcher(parts[i]); if (dispatcher!=null) dispatcher.include(req,resp); } CrossOriginFilter.java: 跨域请求合法性处理 http://hi.baidu.com/aullik5/blog/item/12f2f8ec552da74878f0553f.html 通过在配置文件中配置允许跨域请求的域allowedOrigins(默认都允许)来验证来自请求头Origin这个字段是否在合法性域中,并且还可 以配置允许请求方法(默认为get,put)和请求的允许头字段allowedHeaders(默认= "X-Requested-With,Content-Type,Accept";)来 进行跨域请求处理。 GzipFilter.java gzip压缩支持 通过对ServletOutputStream进行GZIPOutputStream装饰进行压缩支持。 MultiPartFilter.java 文件上传支持 ProxyServlet.java 代理serlvet支持 通过HttpExchange.java实现,注意代理serlvet有几个头字段: // Proxy headers exchange.setRequestHeader("Via","1.1 (jetty)"); if (!xForwardedFor) { exchange.addRequestHeader("X-Forwarded-For", request.getRemoteAddr()); exchange.addRequestHeader("X-Forwarded-Proto", request.getScheme()); exchange.addRequestHeader("X-Forwarded-Host", request.getServerName()); exchange.addRequestHeader("X-Forwarded-Server", request.getLocalName()); } PutFilter.java A Filter that handles PUT, DELETE and MOVE methods. 实现简单,就是文件操作 QoSFilter.java 限制服务进程处理的同时处理请求的数量,保证正在处理的请求的服务质量,保证服务器负载正常。 通过信号量Semaphore来处理最大的请求数量,新请求进来,如果请求队列中未达到正在处理的最大请求数量,则获得信号量处理当前请求,否则挂 起当前请求,知道能获得信号量。 UserAgentFilter.java UserAgent缓存处理,快速匹配当前的Agent放入到request.setAttribute(_attribute,ua); WelcomeFilter.java 如果没有配置welcome page的话可以使用这个filter。 String path=((HttpServletRequest)request).getServletPath(); if (welcome!=null && path.endsWith("/")) request.getRequestDispatcher(path+welcome).forward(request,response); else chain.doFilter(request, response); webApp: webSocket: 服务器端和客户端保持一个TCP的连接,需要浏览器支持,协议内容不一样,可以实现服务器推的技术。 协议内容可以参考:http://feiyezi.iteye.com/blog/1060481 http://www.w3.org/TR/websockets/ jetty相关资料链接: http://blog.romebuilder.com/2010/12/157/ http://blog.csdn.net/lovingprince/article/details/6202669 http://blog.csdn.net/lovingprince/article/details/6202859 http://blog.csdn.net/lovingprince/article/details/6202859 请求处理过程分析