博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
tomcat源码 Container
阅读量:4957 次
发布时间:2019-06-12

本文共 16099 字,大约阅读时间需要 53 分钟。

1.Container的有四个子容器,分别是Engine,Host,Context,Wrapper,如下:

1、Engine:整个Catalina servlet引擎,标准实现为StandardEngine。

2、Host:表示包含一个或多个Context容器的虚拟主机,标准实现为StandardHost。
3、Context:表示一个web应用程序,对应着平时开发对应的一套程序,或者一个WEB-INF目录以及下面的web.xml文件一个Context可以有多个Wrapper,标准实现为StandardContext。
4、Wrapper:包装一个独立的Servlet容器,每个Wrapper封装一个Servlet,标准实现为StandardWrapper。

2.Container一个也有四个子接口Engine,Host,Context,Wrapper和一个默认实现类ContainerBase,另外这四个子容器都是对应一个StandardXXX实现类,都继承ContainerBase,并且Container还继承LifeCycle接口,所以这四个容器也是符合Tomcat的生命周期模式,结构图如下:

 

 

每个Container容器都有对应的阀Valve,多个Valve组成了Pipeline,这就是Container的具体实现过程,也可以在server.xml文件中配置Pipeline和Valve的集合实现。管道Pipe包含了容器中要执行的任务,而每一个阀Valve表示一个具体的任务,在每个管道中,都会有一个默认的阀,可以添加任意数量的阀,可通过server.xml文件配置。对过滤器熟悉的话就会发现,管道和阀的工作机制和过滤器工作机制相似,Pipeline相当于过滤器链FilterChain,Valve相当于每一个过滤器Filter。阀可以处理传给它的request对象和response对象,处理完一个Valve后接着处理下一个Valve,最后处理的阀是基础阀。下面就追踪每一个容器的管道,解析容器处理请求的流程。

首先是Engine容器,默认实现是StandardEngine,创建StandardEngine时实例化其基础阀,代码如下:

public StandardEngine() {    super();    //设置基础阀StandardEngineValve    pipeline.setBasic(new StandardEngineValve());    /* Set the jmvRoute using the system property jvmRoute */    try {        setJvmRoute(System.getProperty("jvmRoute"));    } catch(Exception ex) {        log.warn(sm.getString("standardEngine.jvmRouteFail"));    }    // By default, the engine will hold the reloading thread    backgroundProcessorDelay = 10;}

继续跟踪StandardEngineValve的invoke()方法,源码为:

public final void invoke(Request request, Response response)    throws IOException, ServletException {    // Select the Host to be used for this Request    // 选出和该request相关的Host,在MappingData中保存了请求和容器(Host,Context,Wrapper)之间的映射    Host host = request.getHost();    if (host == null) {        response.sendError            (HttpServletResponse.SC_BAD_REQUEST,             sm.getString("standardEngine.noHost",                          request.getServerName()));        return;    }    if (request.isAsyncSupported()) {        request.setAsyncSupported(host.getPipeline().isAsyncSupported());    }    // Ask this Host to process this request    // host.getPipeline()得到Host对应的管道Pipeline,将request和response对象交给Host的阀去处理    host.getPipeline().getFirst().invoke(request, response);}

StandardEngineValve的invoke()方法是在CoyoteAdapter类中service方法中调用的,也就是Connector将请求交给Container的过程:

public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)            throws Exception {        Request request = (Request) req.getNote(ADAPTER_NOTES);        Response response = (Response) res.getNote(ADAPTER_NOTES);        ......                    // Parse and set Catalina and configuration specific            // request parameters            //根据request得到对应的MappingData,里面保存了解析地址对应的Engine,Host,Context,Wrapper,在后面的管道中使用            postParseSuccess = postParseRequest(req, request, res, response);            if (postParseSuccess) {                //check valves if we support async                request.setAsyncSupported(                        connector.getService().getContainer().getPipeline().isAsyncSupported());                // Calling the container                //得到Connector关联的Container,然后将request和response对象交给Engine的管道Pineline中的阀去处理。                connector.getService().getContainer().getPipeline().getFirst().invoke(                        request, response);            }                    ......    }

这个service方法就是SocketProcessor#doRun--->Http11Processor#service--->CoyoteAdapter#service

同样Host容器构造器中设置了其基础阀StandardHostValve:

public StandardHost() {    super();    pipeline.setBasic(new StandardHostValve());}

同样跟踪StandardHostValve的invoke方法:

public final void invoke(Request request, Response response)    throws IOException, ServletException {    // Select the Context to be used for this Request    // 该request容器关联的Context,保存在MappingData中    Context context = request.getContext();    if (context == null) {        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,             sm.getString("standardHost.noContext"));        return;    }    //是否支持异步    if (request.isAsyncSupported()) {        request.setAsyncSupported(context.getPipeline().isAsyncSupported());    }    boolean asyncAtStart = request.isAsync();    boolean asyncDispatching = request.isAsyncDispatching();    try {        //设置StandardHostValve的类加载器        context.bind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER);        if (!asyncAtStart && !context.fireRequestInitEvent(request.getRequest())) {            // Don't fire listeners during async processing (the listener            // fired for the request that called startAsync()).            // If a request init listener throws an exception, the request            // is aborted.            return;        }        // Ask this Context to process this request. Requests that are in        // async mode and are not being dispatched to this resource must be        // in error and have been routed here to check for application        // defined error pages.        try {            // 将request传递给Context的阀去处理,有错误的页面必须在此处处理,不会继续向下传递到Context容器中            if (!asyncAtStart || asyncDispatching) {                context.getPipeline().getFirst().invoke(request, response);            } else {                // Make sure this request/response is here because an error                // report is required.                if (!response.isErrorReportRequired()) {                    throw new IllegalStateException(sm.getString("standardHost.asyncStateError"));                }            }        } catch (Throwable t) {            ExceptionUtils.handleThrowable(t);            container.getLogger().error("Exception Processing " + request.getRequestURI(), t);            // If a new error occurred while trying to report a previous            // error allow the original error to be reported.            if (!response.isErrorReportRequired()) {                request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t);                throwable(request, response, t);            }        }        // Now that the request/response pair is back under container        // control lift the suspension so that the error handling can        // complete and/or the container can flush any remaining data        response.setSuspended(false);        Throwable t = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);        // Protect against NPEs if the context was destroyed during a        // long running request.        if (!context.getState().isAvailable()) {            return;        }        // Look for (and render if found) an application level error page         //设置错误页面        if (response.isErrorReportRequired()) {            if (t != null) {                throwable(request, response, t);            } else {                status(request, response);            }        }        if (!request.isAsync() && !asyncAtStart) {            context.fireRequestDestroyEvent(request.getRequest());        }    } finally {        // Access a session (if present) to update last accessed time, based        // on a strict interpretation of the specification        if (ACCESS_SESSION) {            request.getSession(false);        }        context.unbind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER);    }}

Context和Wrapper的管道和阀的实现过程与Engine和Host完全一样,不再继续分析。最后主要解析StandardWrapperValve的invoke()方法,看该方法如何将request交个一个servlet处理。鉴于该方法源码太长,只展示出了部分重要代码。

public final void invoke(Request request, Response response)    throws IOException, ServletException {    ......    //获取关联的StandardWrapper    StandardWrapper wrapper = (StandardWrapper) getContainer();    Servlet servlet = null;    //wrapper的父容器Context    Context context = (Context) wrapper.getParent();    ......    // Allocate a servlet instance to process this request    // 分配一个servlet实例处理该request    try {        if (!unavailable) {            //servlet可用时,分配servlet,接下来会跟踪allocate()方法            servlet = wrapper.allocate();        }    } catch (UnavailableException e) {        //分别设置了503错误和404 not found        ......    } catch (ServletException e) {        ......    } catch (Throwable e) {        ......    }    ......    // Create the filter chain for this request    // 为该request设置过滤器    ApplicationFilterChain filterChain =            ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);    // Call the filter chain for this request    // NOTE: This also calls the servlet's service() method    // 过滤器作用于该request,并且此过程中调用了servlet的service()方法    try {        if ((servlet != null) && (filterChain != null)) {            // Swallow output if needed            if (context.getSwallowOutput()) {                try {                    SystemLogHandler.startCapture();                    if (request.isAsyncDispatching()) {                        request.getAsyncContextInternal().doInternalDispatch();                    } else {                        filterChain.doFilter(request.getRequest(),                                response.getResponse());                    }                } finally {                    String log = SystemLogHandler.stopCapture();                    if (log != null && log.length() > 0) {                        context.getLogger().info(log);                    }                }            } else {                if (request.isAsyncDispatching()) {                    request.getAsyncContextInternal().doInternalDispatch();                } else {                    filterChain.doFilter                        (request.getRequest(), response.getResponse());                }            }        }    } catch (ClientAbortException e) {        ......    } catch (IOException e) {        ......    } catch (UnavailableException e) {        ......    } catch (ServletException e) {        ......    } catch (Throwable e) {        ......    }    // Release the filter chain (if any) for this request    // 释放该request的过滤链    if (filterChain != null) {        filterChain.release();    }    // Deallocate the allocated servlet instance    try {        // 回收servlet容器实例        if (servlet != null) {            wrapper.deallocate(servlet);        }    } catch (Throwable e) {        ......    }    ......}

接着跟踪Wrapper的allocate源码:该方法主要功能是分配一个初始化了的servlet实例,其service方法可以被调用。

public Servlet allocate() throws ServletException {    // If we are currently unloading this servlet, throw an exception    // servlet类没有加载时剖出异常    if (unloading) {        throw new ServletException(sm.getString("standardWrapper.unloading", getName()));    }    boolean newInstance = false;    // If not SingleThreadedModel, return the same instance every time    if (!singleThreadModel) {        // Load and initialize our instance if necessary        // servlet没有加载时要先载入该servlet        if (instance == null || !instanceInitialized) {            synchronized (this) {                if (instance == null) {                    try {                        if (log.isDebugEnabled()) {                            log.debug("Allocating non-STM instance");                        }                        // Note: We don't know if the Servlet implements                        // SingleThreadModel until we have loaded it.                        //加载servlet,接下来继续分析loadServlet()方法                        instance = loadServlet();                        newInstance = true;                        //类加载之前并不知道该servlet是否为singleThreadModel,在loadServlet()中会改变singleThreadModel的值,所以此处要再判断一次                        if (!singleThreadModel) {                            // For non-STM, increment here to prevent a race                            // condition with unload. Bug 43683, test case                            // #3                            countAllocated.incrementAndGet();                        }                    } catch (ServletException e) {                        throw e;                    } catch (Throwable e) {                        ExceptionUtils.handleThrowable(e);                        throw new ServletException(sm.getString("standardWrapper.allocate"), e);                    }                }                if (!instanceInitialized) {                    //初始化servlet                    initServlet(instance);                }            }        }        //新加载的servlet实现singleThreadModel时将instance加入到instancePool中,否则直接返回instance        if (singleThreadModel) {            if (newInstance) {                // Have to do this outside of the sync above to prevent a                // possible deadlock                synchronized (instancePool) {                    instancePool.push(instance);                    nInstances++;                }            }        } else {            if (log.isTraceEnabled()) {                log.trace("  Returning non-STM instance");            }            // For new instances, count will have been incremented at the            // time of creation            if (!newInstance) {                countAllocated.incrementAndGet();            }            return instance;        }    }    //SingleThreadedModel类型的servlet时返回instancePool中的一个instance。    synchronized (instancePool) {        while (countAllocated.get() >= nInstances) {            // Allocate a new instance if possible, or else wait            if (nInstances < maxInstances) {                try {                    instancePool.push(loadServlet());                    nInstances++;                } catch (ServletException e) {                    throw e;                } catch (Throwable e) {                    ExceptionUtils.handleThrowable(e);                    throw new ServletException(sm.getString("standardWrapper.allocate"), e);                }            } else {                try {                    instancePool.wait();                } catch (InterruptedException e) {                    // Ignore                }            }        }        if (log.isTraceEnabled()) {            log.trace("  Returning allocated STM instance");        }        countAllocated.incrementAndGet();        return instancePool.pop();    }}

接下来看一下servlet的load过程loadServlet:

public synchronized Servlet loadServlet() throws ServletException {    // Nothing to do if we already have an instance or an instance pool    // 如果不是SingleThreadModel类型的servlet,并且已经存在一个instance实例时,不需要加载。    if (!singleThreadModel && (instance != null))        return instance;    ......    Servlet servlet;    try {        long t1=System.currentTimeMillis();        // Complain if no servlet class has been specified        if (servletClass == null) {            unavailable(null);            throw new ServletException                (sm.getString("standardWrapper.notClass", getName()));        }        //Context容器中的instanceManager,是一个类加载器,其newInstance方法根据class路径加载servlet        InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();        try {            servlet = (Servlet) instanceManager.newInstance(servletClass);        } catch (ClassCastException e) {            unavailable(null);            // Restore the context ClassLoader            throw new ServletException                (sm.getString("standardWrapper.notServlet", servletClass), e);        } catch (Throwable e) {            ......        }        ......        if (servlet instanceof SingleThreadModel) {            if (instancePool == null) {                instancePool = new Stack<>();            }            //此处修改了singleThreadModel值,所以allocate方法中新加载servlet类后要重新判断这个值            singleThreadModel = true;        }        //初始化刚加载的servlet        initServlet(servlet);        fireContainerEvent("load", this);        loadTime=System.currentTimeMillis() -t1;    } finally {        if (swallowOutput) {            String log = SystemLogHandler.stopCapture();            if (log != null && log.length() > 0) {                if (getServletContext() != null) {                    getServletContext().log(log);                } else {                    out.println(log);                }            }        }    }    return servlet;}

通过以上分析,我们知道了一个request请求是如何从Engine容器一路流动到了具体处理容器Wrapper中的,就是通过管道和阀的工作机制实现的,每一个容器都会对应一个管道,可以向管道中添加任意数量的阀valve,但必须要有一个基础阀,上一层的容器通过调用下一次容器的管道的阀的invoke方法实现request对象的传递。

 

转载于:https://www.cnblogs.com/grasp/p/10100126.html

你可能感兴趣的文章
【转】Simulink模型架构指导
查看>>
MYSQL数据库的导出的几种方法
查看>>
SQL Server-5种常见的约束
查看>>
硬件之美
查看>>
[转载]java开发中的23种设计模式
查看>>
表格的拖拽功能
查看>>
函数的形参和实参
查看>>
文字过长 用 ... 表示 CSS实现单行、多行文本溢出显示省略号
查看>>
1Caesar加密
查看>>
【TP SRM 703 div2 500】 GCDGraph
查看>>
MapReduce 重要组件——Recordreader组件 [转]
查看>>
webdriver api
查看>>
apache 实现图标缓存客户端
查看>>
揭秘:黑客必备的Kali Linux是什么,有哪些弊端?
查看>>
linux系统的远程控制方法——学神IT教育
查看>>
springboot+mybatis报错Invalid bound statement (not found)
查看>>
Linux环境下SolrCloud集群环境搭建关键步骤
查看>>
P3565 [POI2014]HOT-Hotels
查看>>
MongoDB的简单使用
查看>>
hdfs 命令使用
查看>>