`

tomcat 源码研究之servlet单例多线程模式

阅读更多

一, tomcat 处理请求的组件 Engine Host Context 和 Wrapper 这四个 分别对应的实现类StnadardEngine,StandardHost,StandardContext和StandardWrapper
Engine:代表tomcat的引擎,可以有多个引擎,他与Connector连接器并列 起到请求与虚拟机主机下对应应用程序的定位。
Host:代表一个虚拟主机,各Host的name不能相同,appBase代表各虚拟主机的应用发布位置;
Context:代表一个应用,Context可以根据应用的/WEB-INF/web.xml文件中定义的servlet来处理请求。一个Host下可以有多个Context;
Wrapper: 代表一个Servlet或者jsp,它负责管理一个 Servlet,包括的 Servlet 的装载、初始化、执行以及资源回收。
二,请求在这些容器之间的流转又是通过管道Pipeline 管道是由他们父类容器ContainerBase来控制的, 而管道中流转的是Value这样的东西,而每个容器的都实现了自己的值分别是StandardEngineValue,StandardHostValue,
     StandardContextValue和StandardWrapperValue


下面请看从配适器CoyoteAdapter中已经 初步封装的请求 ,如图Service方法

      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);
        if (request == null) {
            // Create objects
            request = connector.createRequest();
            request.setCoyoteRequest(req);
            response = connector.createResponse();
            response.setCoyoteResponse(res);
            // Link objects
            request.setResponse(response);
            response.setRequest(request);
            // Set as notes
            req.setNote(ADAPTER_NOTES, request);
            res.setNote(ADAPTER_NOTES, response);
            // Set query string encoding
            req.getParameters().setQueryStringEncoding
                (connector.getURIEncoding());
        }

        if (connector.getXpoweredBy()) {
            response.addHeader("X-Powered-By", POWERED_BY);
        }
        boolean comet = false;
        boolean async = false;
        try {

            // Parse and set Catalina and configuration specific
            // request parameters           req.getRequestProcessor().setWorkerThreadName(Thread.currentThread().getName());
            boolean postParseSuccess = postParseRequest(req, request, res, response);
            if (postParseSuccess) {
                //check valves if we support async
                request.setAsyncSupported(connector.getService().getContainer().getPipeline().isAsyncSupported());
                // Calling the container的first其实就是StandardEngineValve这么一个类
                connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
//代码省略

重点看这句话代码
 connector.getService().getContainer().getPipeline().getFirst().invoke(xxx,xxx)

由于connector找到父类Service容器 然后找到处理请求的一组容器的顶级容器 即Engine 这个容器管道 此管道中流转的就是StandardEngineValue这样的即getFirst()方法调用得到
交给此Value值处理(invoke)

下面是 StandardEngineValue方法中invoke

      @Override
    public final void invoke(Request request, Response response)
        throws IOException, ServletException {

        // Select the Host to be used for this Request
        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().getFirst().invoke(request, response);

    }

也是用了同样的代码
  host.getPipeline().getFirst().invoke(request, response);

继续StandardHostValue 一直到 StandardWrapperValue的invoke方法
  @Override
    public final void invoke(Request request, Response response)
        throws IOException, ServletException {

        // Initialize local variables we may need
        boolean unavailable = false;
        Throwable throwable = null;
        // This should be a Request attribute...
        long t1=System.currentTimeMillis();
        requestCount++;
        StandardWrapper wrapper = (StandardWrapper) getContainer();
        Servlet servlet = null;
        Context context = (Context) wrapper.getParent();
        
        // Check for the application being marked unavailable
        if (!context.getState().isAvailable()) {
            response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
                           sm.getString("standardContext.isUnavailable"));
            unavailable = true;
        }

        // Check for the servlet being marked unavailable
        if (!unavailable && wrapper.isUnavailable()) {
            container.getLogger().info(sm.getString("standardWrapper.isUnavailable",
                    wrapper.getName()));
            long available = wrapper.getAvailable();
            if ((available > 0L) && (available < Long.MAX_VALUE)) {
                response.setDateHeader("Retry-After", available);
                response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
                        sm.getString("standardWrapper.isUnavailable",
                                wrapper.getName()));
            } else if (available == Long.MAX_VALUE) {
                response.sendError(HttpServletResponse.SC_NOT_FOUND,
                        sm.getString("standardWrapper.notFound",
                                wrapper.getName()));
            }
            unavailable = true;
        }

        // Allocate a servlet instance to process this request
        try {
            if (!unavailable) {
            	//加载实例化Servlet实例 (init(ServletConfig config)方法也被初始化了)
            	//可以看出Servlet是单例的
                servlet = wrapper.allocate();
            }
// 省略代码 ...

接着StandardWrapper的allocate()的方法 查看servlet的创建过程
代码如下
  public Servlet allocate() throws ServletException {

        // If we are currently unloading this servlet, throw an exception
        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
            if (instance == null) {
                synchronized (this) {
                    if (instance == null) {
                        try {
                            if (log.isDebugEnabled())
                                log.debug("Allocating non-STM instance");

                            instance = loadServlet();
                            if (!singleThreadModel) {
                                // For non-STM, increment here to prevent a race
                                // condition with unload. Bug 43683, test case
                                // #3
                                newInstance = true;
                                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);
            }

            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);
            }
        }

        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();

        }

    }

上述代码可知
1,对于没有实现SingleThreadModel 会确保只有一个instance实例 也就是单个servlet实例
即每次请求都访问同一servlet
2, 如果servlet实现了该接口,会确保不会有两个线程同时执行servlet的service方法。 servlet容器通过同步化访问servlet的单实例来保证,也可以通过维持servlet的实例池,对于新的请求会分配给一个空闲的servlet。
3,注意:SingleThreadModel不会解决所有的线程安全隐患。 例如,会话属性和静态变量仍然可以被多线程的多请求同时访问,即便使用了SingleThreadModel servlet。建议开发人员应当采取其他手段来解决这些问题,而不是实现该接口,比如 避免实例变量的使用或者在访问资源时同步代码块。

分享到:
评论

相关推荐

    tomcat源码+文档pdf+源码解析

    源码解析部分则是对Tomcat源码的深度剖析,涵盖了关键类和方法的作用、设计模式的运用以及性能优化技巧。这有助于开发者理解Tomcat内部的工作流程,例如,如何处理HTTP请求的生命周期,以及线程池是如何调度和管理的...

    tomcat源码学习之环境搭建

    在深入探讨Tomcat源码学习之前,我们首先要理解Tomcat是什么。Tomcat是一款开源的、免费的Web服务器和Servlet容器,由Apache软件基金会维护。它实现了Java Servlet和JavaServer Pages(JSP)规范,是Java EE应用...

    tomcat源码

    《深入理解Tomcat:工作原理与源码剖析》 Tomcat作为一款开源的Java Servlet容器,是Apache软件基金会Jakarta项目的重要组成部分,广泛应用于各种Java Web应用的部署。本篇文章将深入探讨Tomcat的工作原理,并结合...

    java和tomcat源码

    深入学习Java源码有助于理解其内部工作原理,包括类加载、内存管理、垃圾回收、多线程、异常处理等机制。你可以通过阅读和分析Java的OpenJDK源码来探索这些概念,OpenJDK是Java Development Kit(JDK)的一个开源...

    tomcat源码资源包

    【标题】"Tomcat源码资源包"是一个包含Apache Tomcat服务器源代码的压缩文件,旨在帮助开发者深入了解Tomcat的...因此,深入研究像"Tomcat源码资源包"这样的资料,对于Java Web开发者来说是一项非常有价值的学习任务。

    tomcat7.0.42源码,eclipse直接使用

    - 学习和研究Tomcat如何实现Servlet容器的多线程、线程池、连接管理和内存管理等高级特性。 - 针对特定需求,开发自定义的Valve(管道组件),Connector,Realm(认证模块)等。 总的来说,Tomcat7.0.42的源码为...

    tomcat6的源码

    2. **线程模型**:Tomcat使用了多线程模型来处理并发请求。源码中的`Catalina`和` Coyote`组件涉及到了这部分内容,包括Acceptor线程和Worker线程的管理。 3. **生命周期管理**:每个Tomcat组件都有自己的生命周期...

    tomcat7源码下载

    《深入剖析Tomcat7源码》 Tomcat7是一款广泛使用的开源Java Servlet容器,它实现了Java EE中的Web应用服务器标准,尤其是Servlet和JSP规范。...下载并研究Tomcat7源码,对于提升Java Web开发技能大有裨益。

    tomcat源码研读笔记中的tomcat源码

    通过对Tomcat源码的深入研究,我们可以更好地理解和优化Java Web应用的性能,解决运行时问题,并为自定义扩展打下基础。这份研读笔记将是你理解Tomcat工作机制的宝贵参考资料。在阅读源码的过程中,应结合实际应用...

    简单的Tomcat源码实现

    【标题】"简单的Tomcat源码实现"涵盖了Tomcat服务器的基础构建和运行原理,这是一个针对初学者或希望深入了解Tomcat内部工作...对于想要从底层了解Web服务器运作的人来说,研究Tomcat源码无疑是一次宝贵的实践经历。

    tomcat源码及依赖包(8)

    通过研究Tomcat源码,开发者可以学习到Web服务器的设计原则,理解Servlet容器的工作机制,掌握JSP和EL的编译过程,以及网络通信和多线程编程的相关知识。此外,对于那些需要对Tomcat进行性能优化、安全性增强或功能...

    tomcat源码基于6.0

    《深入剖析Tomcat源码:基于6.0版本》 Tomcat是一款开源的、轻量级的Java Servlet容器,它负责解析HTTP请求并调用Java应用程序。Tomcat 6.0是其历史上的一个重要版本,提供了许多关键特性和改进。通过深入研究其...

    Tomcat源码所需要jar包

    在分析源码的过程中,还需要掌握Java多线程、网络编程、I/O流、XML解析等相关知识。使用给定的jar包,你可以使用Ant构建系统编译源码,使用Eclipse JDT进行代码分析,同时借助JAX-RPC和WSDL4J理解Tomcat与Web服务的...

    TOMCAT源码分析(启动框架)

    【TOMCAT源码分析(启动框架)】 Tomcat是一款广泛应用的开源Java Servlet容器,它实现了Java Servlet和JavaServer Pages(JSP)规范,为Web应用程序提供了运行环境。本篇文章将深入探讨Tomcat的系统框架及其启动流程...

    tomcat源码 tomcat本来就是开源的

    1. **学习价值**:通过阅读Tomcat源码,可以系统地学习到Java语言在实际项目中的应用技巧,特别是对于多线程、网络编程等高级特性有深入的理解。 2. **扩展功能**:开发者可以根据自己的需求对Tomcat进行定制化开发...

    Tomcat源码研究

    总的来说,Tomcat源码研究涉及了网络编程、多线程、XML解析、安全策略等多个领域,深入学习可以帮助我们理解Web服务器的工作机制,提升编程技巧,更好地解决实际开发中的问题。同时,源码研究也能为我们提供定制化...

    tomcat6源码分析

    Tomcat6采用多线程模型处理请求,每个请求都会分配一个工作线程进行处理。这既保证了请求处理的并发性,也避免了线程池过度消耗资源的问题。 六、安全性 Tomcat6支持多种安全认证方式,如Basic认证、Form认证等,并...

    tomcat源码包

    【压缩包子文件的文件名称列表】:虽然只有一个名为“tomcat源码包.txt”的文件,通常情况下,一个完整的Tomcat源码包会包含多个目录和数千个源代码文件,如: 1. `catalina`:这是Tomcat的主要组件,包括服务器...

    tomcat8.0源码

    通过深入研究Apache Tomcat 8.0的源码,开发者可以了解Web服务器的工作原理,定制自己的容器,优化性能,甚至修复bug或开发新的功能。对于Java Web开发人员来说,这是一次宝贵的学习机会,有助于提升专业技能。

Global site tag (gtag.js) - Google Analytics