Tomcat源码走读 02-tomcat的启动和关闭流程

本文详细解析了Tomcat的启动和关闭流程,包括通过startup.bat脚本执行Bootstrap类,初始化类加载器,解析server.xml配置文件,创建容器对象,以及监听8005端口等待shutdown命令。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

02-tomcat的启动和关闭流程

本套代码走读基于tomcat7.0的源码,分析之前,先下载tomcat源码,导入ide。可以参考
https://blog.youkuaiyun.com/z583773315/article/details/56353311

我们平时使用tomcat启动都是使用startup.bat或startup.sh脚本,所以要分析启动流程可以先看下这个脚本。以start.bat为类:
set “CURRENT_DIR=%cd%”
if not “%CATALINA_HOME%” == “” goto gotHome
:gotHome
if exist “%CATALINA_HOME%\bin\catalina.bat” goto okHome
set “EXECUTABLE=%CATALINA_HOME%\bin\catalina.bat”
call “%EXECUTABLE%” start %CMD_LINE_ARGS%

只看上面关键几行,即使不懂bat脚本语言,也大致可以看出来这是要执行catalina.bat脚本,所以继续看catalina.bat:
set MAINCLASS=org.apache.catalina.startup.Bootstrap
set ACTION=start
以及最后一堆类似如下的语句
%_EXECJAVA% %JAVA_OPTS% … %MAINCLASS% %CMD_LINE_ARGS% %ACTION%

可以看到这是要启动Bootstrap类,同时传入参数start

一丶tomcat的启动流程

所以可以知道tomcat的入口在Bootstap的main方法,大致流程图如下:
在这里插入图片描述

1.Bootstrap.init()

public void init()
        throws Exception
    {
        setCatalinaHome(); //设置catalina.home路径
        setCatalinaBase(); //设置catalina.base路径
        initClassLoaders(); //初始化classloader
        Thread.currentThread().setContextClassLoader(catalinaLoader); //设置当前线程的classloader为catalinaLoader。
        SecurityClassLoad.securityClassLoad(catalinaLoader); //从catalinaLoader中加载必要的class
        Class<?> startupClass =
            catalinaLoader.loadClass
            ("org.apache.catalina.startup.Catalina");
        Object startupInstance = startupClass.newInstance();
        String methodName = "setParentClassLoader";
        Class<?> paramTypes[] = new Class[1];
        paramTypes[0] = Class.forName("java.lang.ClassLoader");
        Object paramValues[] = new Object[1];
        paramValues[0] = sharedLoader;
        Method method =
            startupInstance.getClass().getMethod(methodName, paramTypes);
        method.invoke(startupInstance, paramValues); //设置catalina的parentClassLoader为sharedLoader
        catalinaDaemon = startupInstance;  //把catalina对象赋值给成员catalinaDaemon。
    }

init方法主要是做一些初始化工作,确保catalina.home,catalina.base目录存在,初始化自定义类加载器,加载必要的class文件等等。具体类加载器初始化过程查看03-tomcat的类加载器

2.Bootstrap.load() ==> Catalina.load()。删除了部分不重要的代码。

public void load() {
        initDirs(); //还是在确保catalina.home和base的路径
        initNaming();
        Digester digester = createStartDigester(); //初始化digester对象用于解析server.xml
        InputSource inputSource = null;
        InputStream inputStream = null;
        File file = null;
        try {
            file = configFile(); //创建server.xml的文件对象,默认在catalina.base下的conf/server.xml路径
            inputStream = new FileInputStream(file);
            inputSource = new InputSource(file.toURI().toURL().toString());
        } catch (Exception e) {
        }
        try {
            inputSource.setByteStream(inputStream);
            digester.push(this); //把catalina对象压到digester的栈对象stack里。
            digester.parse(inputSource); //解析server.xml,并创建xml中的server。service等对象
        } catch (SAXParseException spe) {
            return;
        } catch (Exception e) {
            return;
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
            }
        }
        getServer().setCatalina(this);//getServer获取的就是digester创建的StandardServer对象
        initStreams();
        try {
            getServer().init();//初始化StandardServer对象以及各子容器
        } catch (LifecycleException e) {
        }
    }

load方法重点就是解析server.xml和初始化容器对象。具体类加载器初始化过程查看04-tomcat的server.xml解析05-tomcat的容器初始化和启动过程

3.Bootstrap.start() ==> Catalina.start()。删除了不重要的部分。

public void start() {
        if (getServer() == null) {
            load(); //如果server为空,再次调用上面上面的load()方法。
        }
        try {
            getServer().start(); //启动各个容器
        } catch (LifecycleException e) {
            return;
        }
        if (useShutdownHook) {
            if (shutdownHook == null) {
                shutdownHook = new CatalinaShutdownHook(); 
            }
            Runtime.getRuntime().addShutdownHook(shutdownHook);//设置钩子,在tomcat停止的时候做一些清理工作
        }
        if (await) {
            await(); //创建serversocket监听server.xml里面server的8005端口接受tomcat的shutdown请求
            stop(); //正常停止,所以移除钩子,然后做一些清理工作
        }
    }

4.Catalina.await() ==> StandardServer.await()。删除了不重要的部分

public void await() {
        try {
            awaitSocket = new ServerSocket(port, 1,
                    InetAddress.getByName(address));  //端口号对应<Server port="8005" shutdown="SHUTDOWN">,创建serversocket阻塞线程,直到监听到8005端口的shutdown请求。
        } catch (IOException e) {
            return;
        }
        try {
            awaitThread = Thread.currentThread();
            while (!stopAwait) {
                ServerSocket serverSocket = awaitSocket;
                if (serverSocket == null) {
                    break;
                }
                Socket socket = null;
                StringBuilder command = new StringBuilder();
                try {
                    InputStream stream;
                    try {
                        socket = serverSocket.accept();
                        socket.setSoTimeout(10 * 1000);  // Ten seconds
                        stream = socket.getInputStream();
                    } catch (AccessControlException ace) {
                        continue;
                    } catch (IOException e) {
                        break;
                    }
                    int expected = 1024; // Cut off to avoid DoS attack
                    while (expected < shutdown.length()) {
                        if (random == null)
                            random = new Random();
                        expected += (random.nextInt() % 1024);
                    }
                    while (expected > 0) {
                        int ch = -1;
                        try {
                            ch = stream.read();
                        } catch (IOException e) {
                        }
                        if (ch < 32)  // Control character or EOF terminates loop
                            break;
                        command.append((char) ch);
                        expected--;
                    }
                } finally {
                    try {
                        if (socket != null) {
                            socket.close();
                        }
                    } catch (IOException e) { }
                }
                // Match against our command string
                boolean match = command.toString().equals(shutdown);
                if (match) {
                    break; //接到shutdown请求,退出循环,不再监听端口阻塞线程。从而继续调用上一步里面后续的catalina.stop()停止tomcat。
                }
            }
        } finally {}
    }

二丶tomcat的关闭流程

关闭过程和启动类似,查看shutdown.bat里面,可以知道其实是调用Bootstap的main()方法,同时入参为stop。

1.Bootstap.stop() ==> Catalina.stopServer()

public void stopServer(String[] arguments) {
        Server s = getServer();
        if( s == null ) {
            // Create and execute our Digester
            Digester digester = createStopDigester();
            File file = configFile();
            FileInputStream fis = null;
            try {
                InputSource is =
                    new InputSource(file.toURI().toURL().toString());
                fis = new FileInputStream(file);
                is.setByteStream(fis);
                digester.push(this);
                digester.parse(is); //和启动一样,先解析server.xml
            } catch (Exception e) {
            } finally {
            }
        } else {
            try {
                s.stop(); //关闭容器
            } catch (LifecycleException e) {
            }
            return;
        }
        // Stop the existing server
        s = getServer();
        if (s.getPort()>0) {
            Socket socket = null;
            OutputStream stream = null;
            try {
                socket = new Socket(s.getAddress(), s.getPort()); //创建8005socket客户端
                stream = socket.getOutputStream();
                String shutdown = s.getShutdown();
                for (int i = 0; i < shutdown.length(); i++) {
                    stream.write(shutdown.charAt(i)); //发送shutdown命令到上面启动过程中的serversocket
                }
                stream.flush();
            } catch (ConnectException ce) {
                System.exit(1);
            } catch (IOException e) {
                System.exit(1);
            } finally {
            }
        } else {
            System.exit(1);
        }
    }

ServerSocket接收到shutdown命令后推出循环,停止tomcat。

附tomcat启动参数说明:
在这里插入图片描述
这些其实对应的就是catalina.sh里面的那些参数。
我们可以使用这些让web应用独立于tomcat目录之外。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值