博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
tomcat源码分析(第四篇 tomcat请求处理原理解析--Container源码分析)
阅读量:5908 次
发布时间:2019-06-19

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

Container容器是所用servlet容器的父接口,也就是说作为一个servlet容器,首先必须要实现Container接口,每个tomcat服务器只能有唯一的根Container,Connector组件通过setContainer方法将Container容器和Connector关联起来。共有四种类型Container容器,分别对应不同概念的层次,每一层之间是父子的关系。

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

2、Host:表示包含一个或多个Context容器的虚拟主机,标准实现为StandardHost。

3、Context:表示一个web应用程序,一个Context可以有多个Wrapper,标准实现为StandardContext。

4、Wrapper:包装一个独立的Servlet容器,标准实现为StandardWrapper。

在第二节的分析中我们知道,server.xml文件中配置了Engine和Host。

复制代码

那么这四种容器之间是如何协同工作的呢?Connector将一个连接请求交给Container之后,这四类容器之间如何分工合作,怎么将请求交给特定的子容器进行处理,即一个请求是如何从Engine最终映射到一个具体的servlet的?先介绍一下整体运作流程,如下面的时序图所示:

从上图可以看出,每个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 {        // 选出和该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());        }        // host.getPipeline()得到Host对应的管道Pipeline,将request和response对象交给Host的阀去处理        host.getPipeline().getFirst().invoke(request, response);复制代码

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

//connector.getService().getContainer()得到Connector关联的Container,然后将request和response对象交给Engine的管道Pineline中的阀去处理。        if (!request.isAsyncDispatching() && request.isAsync() &&                response.isErrorReportRequired()) {            connector.getService().getContainer().getPipeline().getFirst().invoke(                    request, response);        }        if (request.isAsyncDispatching()) {            connector.getService().getContainer().getPipeline().getFirst().invoke(                    request, response);            Throwable t = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);            if (t != null) {                asyncConImpl.setErrorState(t, true);            }        }复制代码

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

public StandardHost() {        super();        pipeline.setBasic(new StandardHostValve());    }复制代码

同样跟踪StandardHostValve的invoke方法

public final void invoke(Request request, Response response)        throws IOException, ServletException {        // 该request容器关联的Context,保存在MappingData中        Context context = request.getContext();        if (context == null) {            return;        }        //是否支持异步        if (request.isAsyncSupported()) {            request.setAsyncSupported(context.getPipeline().isAsyncSupported());        }        boolean asyncAtStart = request.isAsync();        try {            //设置StandardHostValve的类加载器            context.bind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER);            if (!asyncAtStart && !context.fireRequestInitEvent(request.getRequest())) {                return;            }            // 将request传递给Context的阀去处理,有错误的页面必须在此处处理,不会继续向下传递到Context容器中            try {                if (!response.isErrorReportRequired()) {                    context.getPipeline().getFirst().invoke(request, response);                }            } 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;            }            //设置错误页面            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完全一样,不再继续分析。最后主要解析StandardHostValve的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();        ...        // 分配一个servlet实例处理该request        try {            //servlet可用时,分配servlet,接下来会跟踪allocate()方法            if (!unavailable) {                servlet = wrapper.allocate();            }        } catch (UnavailableException e) {            //分别设置了503错误和404 not found            ...            }        } catch (ServletException e) {            ...        } catch (Throwable e) {            ...        }        // 为该request设置过滤器        ApplicationFilterChain filterChain =                ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);        // 过滤器作用于该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) {            ...        }        // 释放该request的过滤链        if (filterChain != null) {            filterChain.release();        }        // 回收servlet容器实例        try {            if (servlet != null) {                wrapper.deallocate(servlet);            }        } catch (Throwable e) {           ...        }        ...    }复制代码

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

public Servlet allocate() throws ServletException {        // 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) {            // servlet没有加载时要先载入该servlet            if (instance == null || !instanceInitialized) {                synchronized (this) {                    if (instance == null) {                        try {                            ...                            //加载servlet,接下来继续分析loadServlet()方法                            instance = loadServlet();                            newInstance = true;                            //类加载之前并不知道该servlet是否为singleThreadModel,在loadServlet()中会改变singleThreadModel的值,所以此处要再判断一次                            if (!singleThreadModel) {                                countAllocated.incrementAndGet();                            }                        } catch (ServletException e) {                            throw e;                        } catch (Throwable e) {                            ...                        }                    }                    if (!instanceInitialized) {                        //初始化servlet                        initServlet(instance);                    }                }            }            //新加载的servlet实现singleThreadModel时将instance加入到instancePool中,否则直接返回instance            if (singleThreadModel) {                if (newInstance) {                    synchronized (instancePool) {                        instancePool.push(instance);                        nInstances++;                    }                }            } else {                if (log.isTraceEnabled()) {                    log.trace("Returning non-STM instance");                }                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) {                       ...                    }                } else {                    try {                        instancePool.wait();                    } catch (InterruptedException e) {                        // Ignore                    }                }            }            if (log.isTraceEnabled()) {                log.trace("  Returning allocated STM instance");            }            countAllocated.incrementAndGet();            return instancePool.pop();        }    }复制代码

接下来看一下servlet的load过程

public synchronized Servlet loadServlet() throws ServletException {        // 如果不是SingleThreadModel类型的servlet,并且已经存在一个instance实例时,不需要加载。        if (!singleThreadModel && (instance != null))            return instance;        ...        Servlet servlet;        try {            ...            //Context容器中的instanceManager,是一个类加载器,其newInstance方法根据class路径加载servlet            InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();            try {                servlet = (Servlet) instanceManager.newInstance(servletClass);            } catch (ClassCastException e) {                ...            } catch (Throwable e) {                ...            }            if (multipartConfigElement == null) {                MultipartConfig annotation =                        servlet.getClass().getAnnotation(MultipartConfig.class);                if (annotation != null) {                    multipartConfigElement =                            new MultipartConfigElement(annotation);                }            }            // Special handling for ContainerServlet instances            // Note: The InstanceManager checks if the application is permitted            //       to load ContainerServlets            if (servlet instanceof ContainerServlet) {                ((ContainerServlet) servlet).setWrapper(this);            }            classLoadTime=(int) (System.currentTimeMillis() -t1);            if (servlet instanceof SingleThreadModel) {                if (instancePool == null) {                    instancePool = new Stack<>();                }                singleThreadModel = true;    //此处修改了singleThreadModel值,所以allocate方法中新加载servlet类后要重新判断这个值            }            initServlet(servlet);  //初始化刚加载的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对象的传递。

转载地址:http://jmvpx.baihongyu.com/

你可能感兴趣的文章
287. Find the Duplicate Number hard
查看>>
day1作业二:多级菜单
查看>>
Excel常用函数
查看>>
基本类型String的原生方法详解
查看>>
sql server中区分大小写全半角
查看>>
信息安全大赛出的题目
查看>>
ios滑动手势全屏(这段代码实现了下一级控制器滑到上一级控制器)
查看>>
第三方分享功能
查看>>
设计接口使用Post还是Get
查看>>
Python学习之路41-元类
查看>>
我是如何成为Apache Kudu committer & PMC的?
查看>>
activiti总结(三)监听器
查看>>
Redis安装
查看>>
oracle和mysql的分页查询语句区别
查看>>
JavaScript函数之参数解析
查看>>
VC防止程序被多次运行 互斥体方法
查看>>
C#调用API(User32.dll),mouse_event函数详解。
查看>>
Installing/Configuring PuTTy and Xming
查看>>
操作符---part2
查看>>
对线程池简单理解
查看>>