servlet singleton multithreading mode for tomcat source research


1. The four corresponding implementation classes, Engine Host Context and Wrapper, which are used by tomcat to process requests, respectively correspond to the implementation classes StnadardEngine, StandardHost, StandardContext and StandardWrapper
Engine: representing the engine of tomcat, there can be multiple engines. The location of the corresponding application under the virtual machine host.
Host: represents a virtual host, the name of each host cannot be the same, appBase represents the application publishing location of each virtual host;
Context: represents an application, Context can be processed according to the servlet defined in the /WEB-INF/web.xml file of the application ask. There can be multiple Contexts under a Host;
Wrapper: Represents a servlet or jsp, which is responsible for managing a servlet, including servlet loading, initialization, execution, and resource recycling.
Second, the flow of requests between these containers is through the pipeline Pipeline pipeline is controlled by their parent container ContainerBase, and the flow in the pipeline is something like Value, and each container implements its own value. It is StandardEngineValue, StandardHostValue,
     StandardContextValue and StandardWrapperValue .
Please see the request that has been initially encapsulated from the adapter CoyoteAdapter, as shown in the Service method



      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());
                // The first of Calling the container is actually a class like StandardEngineValve
                connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
// code omitted

Focus on this sentence code
connector.getService().getContainer().getPipeline().getFirst().invoke(xxx,xxx)

Since the connector finds the parent class Service container and then finds the top-level container of a group of containers that processes the request, that is, the Engine, the container pipeline, is the StandardEngineValue that flows in this pipeline, that is, the getFirst() method is called and
handed over to this Value value for processing (invoke)

The following is invoke in the StandardEngineValue method

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

    }

also used the same code
  host.getPipeline().getFirst().invoke(request, response);

Continue StandardHostValue until the invoke method of StandardWrapperValue
  @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) {
            	//Load the instantiated Servlet instance (the init(ServletConfig config) method is also initialized)
            	//It can be seen that the servlet is a singleton
                servlet = wrapper.allocate();
            }
// omit code...

Then the allocate() method of StandardWrapper checks the creation process of the servlet. The
code is as follows
  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) {
            	//Instantiate 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();

        }

    }

The above code shows that
1. If SingleThreadModel is not implemented, it will ensure that there is only one instance instance, that is, a single servlet instance
, that is, each request accesses the same servlet
. 2. If the servlet implements this interface, it will ensure that there will be no two threads executing the service of the servlet at the same time. method. The servlet container does this by synchronizing access to a single instance of the servlet, or by maintaining a pool of servlet instances that allocates new requests to an idle servlet.
3. Note: SingleThreadModel will not solve all thread safety hazards. For example, session properties and static variables can still be accessed concurrently by multiple requests from multiple threads, even if the SingleThreadModel servlet is used. It is recommended that developers should take other measures to solve these problems than implementing this interface, such as avoiding the use of instance variables or synchronizing code blocks when accessing resources.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326442665&siteId=291194637