Review the Tomcat startup steps

  • 1. Install JDK and configure environment variables
  • 2. Download Tomcat and decompress it
  • 3. Run start.sh in the tomcat/bin directory to complete the script execution process

  • 1.Tomcat is essentially a Java program, so the startup.sh script starts a JVM to run Tomcat’s BootStrap class

In fact, There is no essential difference between Tomcat and the code we usually write, but Tomcat is started by script. Our usual SpringBoot or simple Java classes can be launched with Java commands.

  • 2.BootStrap is used to initialize TomcatClass loaderTo createCatalina.
  • 3.Catalina parses server. XML, creates the responding component, and calls server.start
  • 4. The Server manages services and invokes service.start
  • 5.Service manages the top-level container Engine by calling engine.start

After these steps, Tomcat startup is complete.

Tomcat as a company

  • Catalina: Company founder, responsible for the component team, creating the Server and its sub-components
  • Server: CEO of a company who manages multiple business groups, each of which is a Service
  • General manager of business Group, managing two functional departments,External Marketing Department: Connectors.Internal R&D department: container
  • Engine: General manager of r&d, as the top-level container component

Catalina

XML, create components defined by server. XML, and then call the init and start methods of the Server

public void start() {//1. Obtain the Server and create it if it is emptyif(getServer() == null) { load(); } //2. The system displays an error message and exitsif (getServer() == null) {
            log.fatal(sm.getString("catalina.noServer"));
            return; } long t1 = System.nanoTime(); // 3. Start Server try {getServer().start(); } catch (LifecycleException e) { log.fatal(sm.getString("catalina.serverStartFail"), e);
            try {
                getServer().destroy();
            } catch (LifecycleException e1) {
                log.debug("destroy() failed for failed Server ", e1);
            }
            return;
        }

        long t2 = System.nanoTime();
        if(log.isInfoEnabled()) {
            log.info(sm.getString("catalina.startup", Long.valueOf((t2 - t1) / 1000000))); } // Create a hook that Tomcat closesif (useShutdownHook) {
            if (shutdownHook == null) {
                shutdownHook = new CatalinaShutdownHook();
            }
            Runtime.getRuntime().addShutdownHook(shutdownHook);

            // If JULI is being used, disable JULI's shutdown hook since // shutdown hooks run in parallel and log messages may be lost // if JULI's hook completes before the CatalinaShutdownHook()
            LogManager logManager = LogManager.getLogManager();
            if (logManager instanceof ClassLoaderLogManager) {
                ((ClassLoaderLogManager) logManager).setUseShutdownHook(
                        false); }} // Listen for Tomcat stop requestsif(await) { await(); stop(); }}Copy the code

Tomcat’s close Hook is used to clean up after a JVM is shut down, such as flushing cached data to disk or cleaning up temporary files. A Hook is essentially a thread whose run method the JVM tries to execute before stopping.

Catalina’s close hook

protected class CatalinaShutdownHook extends Thread {

        @Override
        public void run(a) {
            try {
                if(getServer() ! =null) {
                // The stop method is called
                    Catalina.this.stop(); }}catch (Throwable ex) {
                ExceptionUtils.handleThrowable(ex);
                log.error(sm.getString("catalina.shutdownHookFail"), ex);
            } finally {
                / / a little...}}}}Copy the code

Catalina only calls the internal stop method in the shutdown Hook, and finally releases and cleans resources through the stop and destory methods of the Server.

Server

The implementation class of the Server component is StandardServer, inherited from LifeCycleBase, and the life cycle is uniformly managed. Its sub-component is Service, so the life cycle of Service needs to be managed.

  • Call the start method of the Service component at startup time
  • To stop is to call the stop method of the Service component

Server Adds a Service component

public void addService(Service service) {

        service.setServer(this);

        synchronized (servicesLock) {
            Service results[] = new Service[services.length + 1];
            System.arraycopy(services, 0, results, 0, services.length);
            results[services.length] = service;
            services = results;

            if (getState().isAvailable()) {
                try {
                    service.start();
                } catch (LifecycleException e) {
                    // Ignore}}// Report this property change to interested listeners
            support.firePropertyChange("service".null, service); }}Copy the code

You can see that the Server holds references to all services in an array with a default length of 0. Each time a new Service component is added, a new array of length +1 is created, and the data from the original array is copied to the new array using system.arrayCopy () Native method to avoid array automatic 1.5 times expansion waste memory space.

In addition to managing Service components, the Server also has an important function, which is to start a Socket to listen for the stop port. This is why the shutdown.

In fact, at the end of Catalina startup, there is an await method. This method calls Server#await. In the Server#await method, a Socket is created to listen for closed data in an infinite loop from port 8005 (closed port mode is 8005). After receiving the SHUTDOWN command, it exits the loop and enters the stop process.

Service

The realization of a Service is StandardService LifeCycleBase StandardService inheritance, and will hold a Server, Connector, Engine, Mapper and other components.

public class StandardService extends LifecycleBase implements Service {

/ / Server instance
private Server server = null;
// Array of connectors
protected Connector connectors[] = new Connector[0];
private final Object connectorsLock = new Object();
// Corresponding Engine container
private Engine engine = null;
// The mapper and its listener
protected final Mapper mapper = new Mapper();
protected final MapperListener mapperListener = new MapperListener(this);
Copy the code

The function of MapperListener is to support dynamic deployment. It listens for container changes and updates information to Mapper.


In the Service startup method, the life cycle of the child components is maintained, and the components have a one-word startup sequence when various components are started

protected void startInternal(a) throws LifecycleException {

        if(log.isInfoEnabled())
            log.info(sm.getString("standardService.start.name".this.name));
        //1. Start the listener
        setState(LifecycleState.STARTING);

        
        //2. Start Engine, which starts its child container
        if(engine ! =null) {
            synchronized(engine) { engine.start(); }}/ / a little...
        // Start the Mapper listener
        mapperListener.start();

        // Finally start the connector
        synchronized (connectorsLock) {
            for (Connector connector: connectors) {
                // If it has already failed, don't try and start it
                if(connector.getState() ! = LifecycleState.FAILED) { connector.start(); }}}}Copy the code

As you can see from the component startup sequence, the Service starts the Engine first, then the Mapper listener, and then the connector.

The external service components can be started only after the internal components are started. In this way, the connection will not cause problems due to the incomplete initialization of the internal components. So the stop sequence is the opposite of the start sequence

Engine

The Engine implementation class StandardEngine is essentially a top-level container, so it inherits from ContainerBase and implements the Engine interface.

But because Engine is a top-level container, much of the functionality is abstracted into ContainerBase. Holds references to all child container hosts through a HashMap.

    protected final HashMap<String, Container> children = new HashMap<>();
Copy the code

When Engine is started, it starts child containers from a dedicated thread pool

The most important function of the Engine container is to forward requests to hosts for processing through pipline-Valve.

The first base Valve for Pipline-Valve is already set up in the Engine constructor

 /** * Create a new StandardEngine component with the default basic Valve. */
    public StandardEngine(a) {

        super(a);// Set the first base valve
        pipeline.setBasic(new StandardEngineValve());
       / /.. slightly
    }
Copy the code

StandardEngineValve

When the Engine is created, a StandardEngineValve is created by default to connect to the Host Pipline, and the request is already routed in the Mapper component The corresponding container is located by URL, and then the container object is stored in the Request object, so StandardEngineValve can start the whole call link.

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) {
            // REQUEST without a host when no default host
            // is defined. This is handled by the CoyoteAdapter.
            return;
        }
        if (request.isAsyncSupported()) {
            request.setAsyncSupported(host.getPipeline().isAsyncSupported());
        }

        // Ask this Host to process this request
        host.getPipeline().getFirst().invoke(request, response);
    }
Copy the code

Server locks both the startup connector and the container

In order to ensure the correctness of multi-threaded operation sharing resources.

The Server uses threadsafe arrays to reference services internally, and the Engine uses a hashMap to hold hosts that are threadsafe. However, because resources are added and deleted dynamically, locks are required.