前言
web容器应该是最熟悉而陌生的技术之一吧。他封装了http,我们可以方便的使用它。Jetty作为现阶段流行的web容器技术,我们还是有必要熟悉的。
核心流程图
搞清楚Jetty主要是搞清楚它怎么接收,解析,使用,返回的过程。
上图中,绿色部分是Jetty开放给开发者使用的;浅橙色表示JDK或Servlet规范定义的(或开发者实现的),不属于Jetty自身实现;蓝色部分表示Jetty内部实现的一些组件,不对外暴露
启动
我们首先看下Jetty官方给我们的例子
public class Test
{
public static void main( String[] args ) throws Exception
{
Server server = new Server(8080);
ServletHandler servletHandler = new ServletHandler();
server.setHandler(servletHandler);
servletHandler.addServletWithMapping(HelloServlet.class, "/hello");
server.start();
server.join();
}
@SuppressWarnings("serial")
public static class HelloServlet extends HttpServlet
{
@Override
protected void doGet( HttpServletRequest request,
HttpServletResponse response ) throws ServletException,
IOException
{
response.setContentType("text/html");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println("<h1>Hello from HelloServlet</h1>");
}
}
}
开发者首先创建一个Server,
再创建ServletHandler作为Handler来处理业务,可以看到这里加了HelloServlet,
最后调用Server.start启动服务器
组件管理
Jetty中有请多的组件,为了方便管理它们引入了生命周期和容器的概念。
- LifeCycle生命周期
public interface LifeCycle
{
//启动
void start() throws Exception;
//销毁
void stop() throws Exception;
//监听
void addLifeCycleListener(LifeCycle.Listener listener);
}
- Container容器
public interface Container
{
//添加
boolean addBean(Object o);
//获取
<T> T getBean(Class<T> clazz);
//移除
boolean removeBean(Object o);
//监听
void addEventListener(Listener listener);
//有生命周期对象卸载(stop)
void unmanage(Object bean);
//有生命周期对象装载(start)
void manage(Object bean);
}
- ContainerLifeCycle实现
//会在调用server.start()时间触发
protected void doStart() throws Exception
{
try
{
//对容器中的所有bean遍历
for (Bean b : _beans)
{
//不是普通bean有生命周期的
if (b._bean instanceof LifeCycle)
{
LifeCycle l = (LifeCycle)b._bean;
switch (b._managed)
{
//如果是要装过的,则启动它
case MANAGED:
if (l.isStopped() || l.isFailed())
start(l);
break;
//自动选择
case AUTO:
if (l.isStopped())
{//当前为停止的就标记装载,然后启动
manage(b);
start(l);
}
else
{
//当前为启动的则标记为不是
//不是容器帮忙启动
unmanage(b);
}
break;
default:
break;
}
}
}
super.doStart();
}
catch (Throwable t)
{
// on failure, stop any managed components that have been started
//..
throw t;
}
}
Connector组件
Connector组件是Jetty整个通信层的实现,通过利用多组件间的协作完成连接的接受、读、写的操作。整个底层实现依赖于Java NIO配合回调函数,打造了一套Jetty特色的通信框架。
SelectorManager
SelectorManager是选择器管理器。
ManagedSelector
ManagedSelector是一个托管的Selector,它包装了NIO中的Selector,并在内部完成select事件监听处理
SelectChannelEndPoint
EndPoint表示一个逻辑上的传输端点,数据的读取和写入都是从端点开始,配合回调机制(当读操作发生时可以通知到回调函数,而写操作也提供回调机制,当完成写入后,调用写回调函数),可以完成异步处理的。端点的实现通常具有超时的特性,在端点未产生任何读写的情况下,超时机制可以保证EndPoint关闭(包括关联的Channel或其他资源),避免一些资源的浪费。
服务启动
//server创建
public Server(@Name("port") int port)
{
this((ThreadPool)null);
ServerConnector connector = new ServerConnector(this);
connector.setPort(port);
//把connector放到容器中
//通过sever start()启动
setConnectors(new Connector[]{connector});
}
//ServerConnector组件启动
protected void doStart() throws Exception
{
//将jdk nio ServerSocketChannel使用非阻塞模式
_acceptChannel.configureBlocking(false);
//ServerSocketChannel 向SelectorManager注册
_acceptor.set(_manager.acceptor(_acceptChannel));
}
//SelectorManager注册
public Closeable acceptor(SelectableChannel server)
{
//选择一个ManagedSelector
final ManagedSelector selector = chooseSelector();
//创建一接收者对象
ManagedSelector.Acceptor acceptor = selector.new Acceptor(server);
//把acceptor加入updates队列
//ManagedSelector会使用ExecuteProduceConsume(EPC)策略不断的监听事件
//当没有事件的时候会去调用updates的对象的update方法
selector.submit(acceptor);
return acceptor;
}
//Acceptor
//selector是ManagedSelector 代理的 jdk nio selector
public void update(Selector selector)
{
//注册监听事件
_key = _channel.register(selector, SelectionKey.OP_ACCEPT, this);
}
连接
- 当client与Jetty服务器建立连接时,
- 连接会直接打到SelectorManager提供的ServerSocketChannel,accept后
- 拿到这个client对应的SocketChannel后,会选择一个ManagedSelector来执行IO事件检测,
- 并创建好这条连接对应的EndPoint以及Connection
//SelectorProducer,EPC策略中获取任务
public Runnable produce()
{
while (true)
{
//获取事件任务
Runnable task = processSelected();
if (task != null)
return task;
//没用使用看看updates队列有没值
processUpdates();
//刷新key
updateKeys();
//阻塞等待事件
if (!select())
return null;
}
}
//根据事件创建任务
private Runnable processSelected(){
while (_cursor.hasNext())
{
SelectionKey key = _cursor.next();
Object attachment = key.attachment();
//是否断定端点EndPoint
if (attachment instanceof Selectable)
{
//当读事件发生的时候,调用Connection,ReadCallback
//当写事件发生的时候,回调SendCallback,大概是数据发不完,进行的回调
Runnable task = ((Selectable)attachment).onSelected();
if (task != null) {return task;}
else if (key.isConnectable())
{
//有连接进来
processConnect(key, (Connect)attachment);
} else {
// exception
}
return null;
}
private void processConnect(SelectionKey key, final Connect connect)
{
//监听读事件
key.interestOps(0);
//创建好这条连接对应的EndPoint
execute(new CreateEndPoint(connect, key));
}
//创建对应的EndPoint
private void createEndPoint(SelectableChannel channel, SelectionKey selectionKey) throws IOException
{
//用channel创建EndPoint
EndPoint endPoint = _selectorManager.newEndPoint(channel, this, selectionKey);
//创建Connection组件,并创建了回调函数ReadCallback
Connection connection = _selectorManager.newConnection(channel, endPoint, selectionKey.attachment());
//绑定Connection,回调的时候用
endPoint.setConnection(connection);
//绑定key和endPoint,监听读写事件的时候用
selectionKey.attach(endPoint);
endPoint.onOpen();
endPointOpened(endPoint);
//SelectorManager.connectionOpened这个方法会调用HttpConnection.onOpen,最后会触发到SelectChannelEndPoint.fillInterested,注册ReadCallback事件
_selectorManager.connectionOpened(connection);
}
Connection组件
Connection是一条逻辑连接,主要完成Http请求的解析以及通知后续处理。
HttpChannel
HttpChannel主要协助HttpConnection触发Server或Handler的处理操作,它内部会包装本次请求对应的Request、Response,并控制它们的生命周期。
HttpChannel主要就是串联Request、Response,而HttpChannelOverHttp在HttpChannel之上进一步提供增加Request需要的MetaData,HttpParser解析完成请求行会将数据放到MetaData.Request里面,而请求头解析完成后,会放到HttpFields,而这个HttpFields还会和MetaData.Request建立关联,这样MetaData就包含了一次请求需要的基础数据,Response也类似
HttpParser
HttpParser是专门用于解析HTTP协议的类,它能够完成HTTP/0.9,HTTP/1.0,HTTP/1.1的解析.
- quickStart主要是快速检测当前的HTTP报文请求行部分是否有效,如果是脏数据直接快速失败
- parseLine则解析请求行(当然也能处理响应头,不过通常都是解析请求)
- parseHeaders解析请求头,parseContent解析请求体
HttpGenerator
HTTP协议报文生成器,用于HTTP响应报文的生成,和HttpParser相对应.
请求过程
当client向刚才建立的连接中写入请求数据时,ManagedSelector对应的事件检测循环(感兴趣的读者可以跳到后续Connector组件阅读)会探测到读事件,会触发SelectChannelEndPoint触发读ReadCallback回调
从而调用到HttpConnection.onFillable,onFillable里面借助于ByteBufferPool、HttpParser完成数据读取及解析,将解析后的元数据放入Request,直接触发HttpChannel.handle
//HttpConnection
//通过ReadCallback 最终会调用到
public void onFillable()
{
//把Connection对象放到上下文中
HttpConnection last = setCurrentConnection(this);
try
{
while (getEndPoint().isOpen())
{
// 读取数据
int filled = fillRequestBuffer();
// 解析 http
// HttpParser解析完成请求行会将数据放到MetaData.Request里面
boolean handle = parseRequestBuffer();
//转占hadler
boolean suspended = !_channel.handle();
}
}
finally
{
setCurrentConnection(last);
}
}
//HttpChannel
//通过调用server的handle实现
public boolean handle(){
//Server触发Handler调用完成整个链路(Handler -> Filter -> Servlet)
getServer().handle(HttpChannel.this);
}
响应过程
当应用层完成业务操作后,调用Response.getWriter或Response.getOutputStream,最终会调用到HttpOutput.write方法,触发数据回写,这时调用到HttpChannel.sendResponse,
这里会触发HttpConnection.send,而HttpConnection会利用HttpGenerator生成响应报文,然后触发SelectChannelEndPoint向SocketChannel刷数据
//HttpChannel
public boolean sendResponse(MetaData.Response info, ByteBuffer content, boolean complete, final Callback callback){
//创建SendCallback回调函数
final Callback committed = (status < HttpStatus.OK_200 && status >= HttpStatus.CONTINUE_100)
? new Send100Callback(callback)
: new SendCallback(callback, content, true, complete);
//_transport 指 HttpConnection
_transport.send(info, _request.isHead(), content, complete, committed);
}
//HttpConnection.send-_sendCallback.iterate-processing
public Action process() throws Exception{
//利用HttpGenerator完成响应行、响应头、响应体的生成
HttpGenerator.Result result = _generator.generateResponse(_info, _head, _header, chunk, _content, _lastContent);
//这些数据会被积攒起来,形成一个大缓冲
//通过EndPoint将数据写入chanel 并注册 sendCallback
//如果一次性发不完,通过nio事件继续回调sendCallback,继续发
getEndPoint().write(this, _header, chunk, _content);
}
Handler
Handler组件是Jetty实际处理Http请求业务逻辑的执行器,一个实际的Jetty服务应该最少存在一个Handler。Handler有很多种形态,例如单纯处理Servlet请求的或者是一个包含多个Handler的容器。
- ScopedHandler其实就是责任链的变种,常规的责任链声明一个共享方法,自己完成后调用下一个链
- ContextHandler主要是完成上下文路径及Servlet路径的处理
- SessionHandler 作为一个现代的Web应用,Web容器必须能支持Session管理
- ServletHandler作为Servlet容器必不可少的重要成员之一,是整个Servlet容器实现的最复杂的一个处理器。它实现Servlet规范中Filter、Servlet的基本处理逻辑.(jetty-servlet.jar)
- WebAppContext 在Serlet容器上构建Web应用程序。完成web.xml的解析及加载。(jetty-webapp.jar)
Servlet处理过程
通过请求过程最终调用到server.handle方法
public void handle(HttpChannel channel) throws IOException, ServletException
{
final String target = channel.getRequest().getPathInfo();
//从httpchanel获取构造好的请求
final Request request = channel.getRequest();
//从httpchanel获取构造好的响应体
final Response response = channel.getResponse();
//开始触发handler, 根据demo中也就是指ServletHandler
handle(target, request, request, response);
}
//因为ServletHandler实现了ScopedHandler其调用顺序参考
//ScopedHandler
protected void doStart() throws Exception
{
//获取其容器ScopedHandler做为下一个执行点
_nextScope = getChildHandlerByClass(ScopedHandler.class);
}
//ServletHandler
public void doScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException{
//找到对应链接sevlet
MappedResource<ServletHolder> mapping = getMappedServlet(target);
servletHolder = mapping.getResource();
//设置对应的到请求中
baseRequest.setUserIdentityScope(servletHolder);
//顺着链路往下走
nextScope(target, baseRequest, request, response);
}
//因为没有_nextScope则直接搞用自己
//ServletHandler
public void doHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response){
//将语法二次包装
ServletRequest req = request;
if (req instanceof ServletRequestHttpWrapper)
req = ((ServletRequestHttpWrapper)req).getRequest();
servletResponse res = response;
if (res instanceof ServletResponseHttpWrapper)
res = ((ServletResponseHttpWrapper)res).getResponse();
//初始化sevlet
servletHolder.prepare(baseRequest, req, res);
//过虑器不为空走过滤器先
if (chain != null)
chain.doFilter(req, res);
else //sevlet调用
servletHolder.handle(baseRequest, req, res);
}
//servletHolder
//调用HelloServlet
public void handle(Request baseRequest,
ServletRequest request,
ServletResponse response){
servlet.service(request, response);
}
//httpSevlet
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException
{
HttpServletRequest request;
HttpServletResponse response;
//包装请求和响应
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
//分请求类型分别调用doGet,doPost,doHead...
service(request, response);
}
WebAppContext的使用
把Servlet容器转向我们熟知的web应用,demo如下
public class WebAppTest {
public static void main(String[] args) throws Exception {
Server server = new Server(8080);
// 通过doStart方法触发Configuration接中的方式,解析配置文件
// @see WebXmlConfiguration
WebAppContext webAppContext = new WebAppContext();
webAppContext.setResourceBase("web");
server.setHandler(webAppContext);
server.start();
server.join();
}
}
<!-- 路径为:web/WEB-INF/web.xml -->
<web-app>
<display-name>db</display-name>
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
总结
jetty的大体执行过程就差不多这样子。如果有需要可以细致的去讲源码
主要参考
《Jetty9源码剖析》
《官方文档》
《servlet3.1规范》