Jetty概述

前言

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);
               
        }     
连接
  1. 当client与Jetty服务器建立连接时,
  2. 连接会直接打到SelectorManager提供的ServerSocketChannel,accept后
  3. 拿到这个client对应的SocketChannel后,会选择一个ManagedSelector来执行IO事件检测,
  4. 并创建好这条连接对应的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规范》

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值