Jetty的工作原理解析

目前最活跃也是很有前景的一个Servlet引擎,架构比较简单,可拓展性强且非常灵活的应用服务器。其有一个基本的数据模型:Handler,所有可以被扩展的组件都可以作为一个Handler添加到Server中,Jetty来做管理

基本架构

核心由Server和Connector两个组件构成。整个Server组件是基于Handler容器工作的,类似Tomcat的Container容器。Connector组件负责接受客户端的连接请求,并将请求分配给一个处理队列去执行

除了上述两个组件外,还有一些可有可无的,可以在此之上做拓展。例如JMX,可以定义一些Mbean加到Server中,当Server启动时,这些Bean会一起工作

整个Jetty的所有组件的生命周期管理都是基于观察者模板设计的,和Tomcat类似,每个组件都有一个观察者集合,当触发生命周期中的事件时会触发调用

Handler的体系结构

其中主要有两种Handler类型,一种时HandlerWrapper,可以将一个Handler委托给另一个类去执行,例如将一个Handler加到Jetty中,那么就必须将这个Handler委托给Server去调用,配合ScopeHandler类可以拦截Handler的执行,再调用Handler之前或者之后来做另外的事情。

另一个种是HandlerCollection,可以将多个Handler组装在一起,构成一个Handler链

启动过程

Jetty的入口是Server类,其启动成功了就表示Jetty能提供服务了。Jetty所提供的服务由Server类启动时都调用了那些组件的start方法来决定,配置Jetty的过程就是将那些类配置到Server的过程

Server启动其他组件的顺序:首先启动设置到Server的Handler,通常会有多个子Handler构成一个链,依次启动这个链上所有的Handler。然后会启动注册再Server上JMX的Mbean。最后启动Connector

接受请求

其基于两种协议工作,一种是HTTP,一种是AJP。如果将Jetty集成到JBoss或者Apache,那么就可以让Jetty基于AJP模式工作

基于HTTP工作

如果在前端没有其他Web服务器,那么Jetty应该基于HTTP工作

Jetty创建接收连接的环境需要以下三个步骤:

  1. 创建一个队列线程池,用于处理每个建立连接的任务,这个线程池可以由用户来指定,这个和Tomcat是类似的
  2. 创建ServerSocket,用于准备接受客户端的Socket请求,以及客户端用来包装这个Socket的一些辅助类
  3. 创建一个或者多个监听线程,用来监听访问端口是否有连接进来

其中存在一个Acceptor线程,当其接收到Socket连接后,将为这个请求创建ConnectorEndPoint(以BIO的方式来处理连接请求),HttpConnector用来表示这个连接是一个HTTP连接,它会创建HttpParse来解析HTTP,并且创建符合HTTP的Request和Response对象。然后这个线程将会被交给队列线程去执行

基于AJP工作

通常一个Web服务站点的后端服务器不是将Java的应用服务器直接暴露给服务访问者,而是在应用服务器(如Jboss)的前面再加一个Web服务器(如Apache或者Nginx),做负载均衡、权限控制等

这样常见的Web服务端架构下的Servlet引擎不需要解析和封装返回的HTTP,因为其的解析工作已经在Apache或者Nginx服务器上完成了,JBoss只需要基于更简单的AJP工作就行

相比较HTTP,两者的逻辑几乎相同,不同的地方在:

  1. 服务到Socket数据包时,如何让来转换这个数据包,按照HTTP的包格式来解析就是HttpParser,按照AJP类解析就是Ajp13Parserer,封装返回的数据也是如此
  2. 让Jetty工作在AJP下,需要配置connector的实现类为Ajp13SocketConnector

基于NIO方式工作

Jetty建立客户端连接到处理客户端连接都是基于BIO的方式,除此之外,它也支持另一种NIO的处理方式,Jetty的默认connector就是NIO方式

NIO工作的原型中存在一个Selector,当创建时,相当于一个观察者打开了一个Server端通道,把这个Server通道注册到观察者上并且指定监听事件,然后遍历这个观察者观察到的事件,取出感兴趣的事件处理

在这里,不需要为每个被观察者创建一个线程来监控它随时发生的事件,而是将这些被观察者都注册到一个地方统一管理,再由它把出发的事件统一发送给感兴趣的程序模块

其实,Jetty用一个线程来监听客户端的连接请求,当接收到请求后,把这个请求注册到Selector上,然后才以非阻塞的方式执行

同样,真正共享一个线程的处理只是在监听不同连接的数据传送事件上,如有多个连接已经建立,传统方式时当没有数据传输时,线程是阻塞的,也就是一直在等待下一个数据的到来,而Niode处理方式是只有一个线程在等待所有连接的数据的到来,而当某个连接数据到来时,Jetty会将其分配给这个连接对应的处理线程去处理

处理请求

当Jetty接收到一个请求后,就会将这个请求交给Server中注册的代理Handler去执行,至于如何执行注册的Handler有使用者进行规定,Jetty要做的就是调用你注册的第一个Handler的handle

与JBoss集成

Jetty作为一个Servlet引擎都是基于AJP工作的,所以它前面必然有一个服务器,在通常情况下与JBoss集成的可能性很大

JBoss基于JMX的架构,所以只要是符合JMX规范的系统或者是框架都可以作为一个组件加到JBoss中作功能拓展

Jetty集成到JBoss中需要集成JBoss的AbstractWebContainer类,这个类是现实的是模板模式,其中的抽象方法getDeployer需要子类去实现

与Tomcat的比较

Tomcat经过长时间的发展,已经广泛被市场接受和认可,相对于Jetty,其更加稳定成熟,不过Jetty也有自己的优点

  1. 架构更为简单
  2. Tomcat在处理少数非常繁忙的连接上更有优势,也就是说连接的生命周期如果短,Tomcat的总体性能更高。而Jetty可以同时处理大量连接而且可以长时间保持这些连接,例如一些Web聊天应用非常适合用Jetty作为服务器