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 请求处理过程分析