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规范》

Jetty 欢迎访问Jetty文档 Wiki. Jetty是一个开源项目,提供了http服务器、http客户端和java servlet容器。 这个wiki提供jetty的入门教程、基础配置、功能特性、优化、安全、JavaEE、监控、常见问题、故障排除帮助等等。它包含教程、使用手册、视频、特征描述、参考资料以及常见问题。 Jetty文档 ---------------- 入门: 下载Download, 安装, 配置, 运行 Jetty入门(视频) 下载和安装Jetty 如何安装一个Jetty包 如何配置Jetty – 主要文档 如何运行Jetty 用JConsole监控Jetty 如何使用Jetty开发 Jetty HelloWorld教程 Jetty和Maven HelloWorld教程 Jetty(6)入门 (www.itjungle.com) Jetty Start.jar 配置Jetty 如何设置上下文(Context Path) 如何知道使用了那些jar包 如何配置SSL 如何使用非root用户监听80端口 如何配置连接器(Connectors) 如何配置虚拟主机(Virtual Hosts) 如何配置会话ID(Session IDs) 如何序列化会话(Session) 如何重定向或移动应用(Context) 如何让一个应用响应一个特定端口 使用JNDI 使用JNDI 在JNDI中配置数据源(DataSource) 内嵌Jetty服务器 内嵌Jetty教程 内嵌Jetty的HelloWorld教程 内嵌Jetty视频 优化Jetty 如何配置垃圾收集 如何配置以支持高负载 在Jetty中部署应用 部署管理器 部署绑定 热部署 Context提供者 如何部署web应用 webApp提供者 如何部署第三方产品 部署展开形式的web应用 使用Jetty进行开发 如何使用Jetty进行开发 如何编写Jetty中的Handlers 使用构建工具 如何在Maven中使用Jetty 如何在Ant中使用Jetty Maven和Ant的更多支持 Jetty Maven插件(Plugin) Jetty Jspc Maven插件(Plugin) Maven web应用工程原型 Ant Jetty插件(Plugin) 使用集成开发环境(IDEs) 在Eclipse中使用Jetty 在IntelliJ中使用Jetty 在Eclipse中工作 在Eclipse中开发Jetty Jetty WTP插件(Plugin) JettyOSGi SDK for Eclipse-PDE EclipseRT Jetty StarterKit SDK OSGi Jetty on OSGi, RFC66 基于Jetty OSGi的产品 OSGi贴士 Equinox中使用Jetty实现HTTP Service Felix中使用Jetty实现HTTP Service PAX中使用Jetty实现HTTP Srevice ProSyst mBedded Server Equinox Edition Spring Dynamic Modules里的Jetty JOnAS5里的Jetty 配置Ajax、Comet和异步Servlets 持续和异步Servlets 100 Continue和102 Processing WebSocket Servlet 异步的REST Stress Testing CometD 使用Servlets和Filters Jetty中绑定的Servlets Quality of Service Filter Cross Origin Filter 配置安全策略(Security Policies) 安全领域(Security Realms) 安全域配置教程 Java Authentication and Authorization Service (JAAS) JAAS配置教程 JASPI 安全模型(Secure Mode) 存储在文件中的安全密码以及编程教程 如何开启或禁止Jetty中的SSL功能 如何在Jetty中安全存储密码 如何安全终止Jetty 如何配置Spnego Application Server Integrations(集成) Apache Geronimo JEE 配置Apache httpd和Jetty教程 配置Apache mod_proxy和Jetty 配置Jetty中的AJP13 在JBoss中配置Jetty Remote Glassfish EJBs from Jetty Jetty and Spring EJB3 (Pitchfork) JBoss EJB3 ObjectWeb EasyBeans
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值