tomcat启动过程分析

tomcat启动过程


       tomcat中最顶层的容器叫server,代表服务器,server中起码有一个service,提供服务。service中包含两部分:connector和Container。connector处理连接相关的功能,包括socket与request、response的转化。Container负责封装管理servlet以及处理request请求。一个tomcat只有一个server,一个server包含多个service,一个service只有一个Container,但是可以有多个connector(使用相同协议或者不同协议提供不同端口的连接),tomcat结构如图所示:


       tomcat的Server由org.apache.catalina.startup.Catalina来管理,Catalina是整个tomcat的管理类,有load、start、stop三个方法来管理整个服务器的生命周期,load方法用于根据conf/server.xml文件创建server并根据init方法进行初始化;start方法启动服务器,stop方法停止服务器;start和stop方法分别调用server的start和stop方法,load调用server的init方法,然后这三个方法在按容器结构逐层调用对应方法。例如server的start方法会调用service方法,service的start方法又会调用connectors和container的start方法,然后整个tomcat服务就启动了。init方法和start方法也类似,这就是tomcat的生命周期。Catalina还有个await方法,Catalina的await方法调用了server的await方法,使的主线程不会退出。

       tomcat的main方法不在Catalina中,而是在org.apache.catalina.startup.Bootstrap中,Bootstrap作用类似于CatalinaAdaptor,具体处理过程使用Catalina完成,这样启动入口和管理类可以分开,可以方便的创造出多种启动方式,每种方式只要设计对应的CatalinaAdaptor即可。

Bootstrap的启动过程


       bootstrap是tomcat的入口,启动tomcat即是调用bootstrap的main方法:

public static void main(String[] args)
  {
    if (daemon == null)
    {
      Bootstrap bootstrap = new Bootstrap();
      try
      {
        bootstrap.init();
      }
      catch (Throwable t)
      {
        handleThrowable(t);
        t.printStackTrace();
        return;
      }
      daemon = bootstrap;
    }
    else
    {
      Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
    }
    try
    {
      String command = "start";
      if (args.length > 0) {
        command = args[(args.length - 1)];
      }
      if (command.equals("startd"))
      {
        args[(args.length - 1)] = "start";
        daemon.load(args);
        daemon.start();
      }
      else if (command.equals("stopd"))
      {
        args[(args.length - 1)] = "stop";
        daemon.stop();
      }
      else if (command.equals("start"))
      {
        daemon.setAwait(true);
        daemon.load(args);
        daemon.start();
      }
      else if (command.equals("stop"))
      {
        daemon.stopServer(args);
      }
      else if (command.equals("configtest"))
      {
        daemon.load(args);
        if (null == daemon.getServer()) {
          System.exit(1);
        }
        System.exit(0);
      }
      else
      {
        log.warn("Bootstrap: command \"" + command + "\" does not exist.");
      }
    }
    catch (Throwable t)
    {
      if (((t instanceof InvocationTargetException)) && (t.getCause() != null)) {
        t = t.getCause();
      }
      handleThrowable(t);
      t.printStackTrace();
      System.exit(1);
    }
  }


       main方法有两部分内容:首先创建Bootstrap,并执行init方法;然后处理main的参数,如果参数为空,默认执行start方法。

       在init中初始化了ClassLoader,并使用Classloader创建了Catalina实例,赋值给catalinaDaemon,后面操作都使用catalinaDaemon来执行。

       对于start命令处理调用了三个方法:setAwait(true)、load(args)和start(),这三个方法内部都调用了Catalina的相应方法,不过是通过反射来调用的。start方法(另外两个也类似):

 public void start() throws Exception {
    if (this.catalinaDaemon == null) {
      init();
    }
    Method method = this.catalinaDaemon.getClass().getMethod("start", (Class[])null);
    method.invoke(this.catalinaDaemon, (Object[])null);
  }

首先判断catalinaDaemon有没有初始化,没有的话进行init,然后通过Method进行反射调用Catalina的start方法。

Catalina的启动过程


       Catalina主要调用setAwait、load和start来完成启动。setAwait用于设置Server启动完成后时候进入等待状态;load用于加载配置文件,并初始化server;start用于启动服务器。

       setAwait方法如下:

 public void setAwait(boolean b)
  {
    this.await = b;
  }

setAwait方法就是设置await的值,start方法启动服务器后会使用它的值来判断是否进入等待状态。

       Catalina的load方法根据conf/server.xml来创建server方法,并进行赋值,然后调用server的init方法:

public void load()
  {
    long t1 = System.nanoTime();

    initDirs();

    initNaming();

    Digester digester = createStartDigester();

    InputSource inputSource = null;
    InputStream inputStream = null;
    File file = null;
    try
    {
      try
      {
        file = configFile();
        inputStream = new FileInputStream(file);
        inputSource = new InputSource(file.toURI().toURL().toString());
      }
      catch (Exception e)
      {
        if (log.isDebugEnabled()) {
          log.debug(sm.getString("catalina.configFail", new Object[] { file }), e);
        }
      }
      if (inputStream == null) {
        try
        {
          inputStream = getClass().getClassLoader().getResourceAsStream(getConfigFile());

          inputSource = new InputSource(getClass().getClassLoader().getResource(getConfigFile()).toString());
        }
        catch (Exception e)
        {
          if (log.isDebugEnabled()) {
            log.debug(sm.getString("catalina.configFail", new Object[] { getConfigFile() }), e);
          }
        }
      }
      if (inputStream == null) {
        try
        {
          inputStream = getClass().getClassLoader().getResourceAsStream("server-embed.xml");

          inputSource = new InputSource(getClass().getClassLoader().getResource("server-embed.xml").toString());
        }
        catch (Exception e)
        {
          if (log.isDebugEnabled()) {
            log.debug(sm.getString("catalina.configFail", new Object[] { "server-embed.xml" }), e);
          }
        }
      }
      if ((inputStream == null) || (inputSource == null))
      {
        if (file == null)
        {
          log.warn(sm.getString("catalina.configFail", new Object[] { getConfigFile() + "] or [server-embed.xml]" }));
        }
        else
        {
          log.warn(sm.getString("catalina.configFail", new Object[] { file.getAbsolutePath() }));
          if ((file.exists()) && (!file.canRead())) {
            log.warn("Permissions incorrect, read permission is not allowed on the file.");
          }
        }
        return;
      }
      try
      {
        inputSource.setByteStream(inputStream);
        digester.push(this);
        digester.parse(inputSource);
      }
      catch (SAXParseException spe)
      {
        log.warn("Catalina.start using " + getConfigFile() + ": " + spe.getMessage()); return;
      }
      catch (Exception e)
      {
        log.warn("Catalina.start using " + getConfigFile() + ": ", e); return;
      }
      if (inputStream != null) {
        try
        {
          inputStream.close();
        }
        catch (IOException localIOException3) {}
      }
      getServer().setCatalina(this);
    }
    finally
    {
      if (inputStream != null) {
        try
        {
          inputStream.close();
        }
        catch (IOException localIOException4) {}
      }
    }
    getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
    getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());

    initStreams();
    try
    {
      getServer().init();
    }
    catch (LifecycleException e)
    {
      if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
        throw new Error(e);
      }
      log.error("Catalina.start", e);
    }
    long t2 = System.nanoTime();
    if (log.isInfoEnabled()) {
      log.info("Initialization processed in " + (t2 - t1) / 1000000L + " ms");
    }
  }


       Catalina的start方法主要调用server的start方法,并根据await来判断是否让程序进入等待状态:

 public void start()
  {
    if (getServer() == null) {
      load();
    }
    if (getServer() == null)
    {
      log.fatal("Cannot start server. Server instance is not configured.");
      return;
    }
    long t1 = System.nanoTime();
    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("Server startup in " + (t2 - t1) / 1000000L + " ms");
    }
    if (this.useShutdownHook)
    {
      if (this.shutdownHook == null) {
        this.shutdownHook = new CatalinaShutdownHook();
      }
      Runtime.getRuntime().addShutdownHook(this.shutdownHook);

      LogManager logManager = LogManager.getLogManager();
      if ((logManager instanceof ClassLoaderLogManager)) {
        ((ClassLoaderLogManager)logManager).setUseShutdownHook(false);
      }
    }
    if (this.await)
    {
      await();
      stop();
    }
  }

先判断Server是否已经存在,如果不存在调用load来初始化Server,然后调用Server的start方法来启动服务器。并且注册关闭钩子并根据await判断程序是否进入等待状态。进入等待状态会调用await和stop两个方法,await调用server的await,server内部的await方法会执行while循环,此时程序就停在了await方法,当await退出时,就会执行stop方法关闭服务器。

Server启动过程


       Server接口中提供addService(Service service)和removeService(Service service)来添加和删除Service,Server的start和init方法分别循环调用所有Service的init和start方法来启动所有的Service。

       Server的默认实现是org.apache.catalina.core.StandardServer,StandardServer继承于LifecycleMBeanBase,lifecycleMbeanBase继承LifecycleBase,init和start方法定义在LifecycleBase中,LifecycleBase里的init和start调用initInternal和startInternal方法,这两个都是模板方法,由具体子类实现,所以StandardServer的init和start方法执行时会调用自己的initInternal和startInternal方法。StandardServer的initInternal和startInternal方法循环调用了每个service的start和init方法:

protected void startInternal() throws LifecycleException {
    fireLifecycleEvent("configure_start", null);
    setState(LifecycleState.STARTING);

    this.globalNamingResources.start();
    synchronized (this.servicesLock)
    {
      for (int i = 0; i < this.services.length; i++) {
        this.services[i].start();
      }
    }
  }

  protected void initInternal() throws LifecycleException {
    super.initInternal();

    this.onameStringCache = register(new StringCache(), "type=StringCache");

    MBeanFactory factory = new MBeanFactory();
    factory.setContainer(this);
    this.onameMBeanFactory = register(factory, "type=MBeanFactory");

    this.globalNamingResources.init();
    if (getCatalina() != null)
    {
      ClassLoader cl = getCatalina().getParentClassLoader();
      while ((cl != null) && (cl != ClassLoader.getSystemClassLoader()))
      {
        if ((cl instanceof URLClassLoader))
        {
          URL[] urls = ((URLClassLoader)cl).getURLs();
          for (URL url : urls) {
            if (url.getProtocol().equals("file")) {
              try
              {
                File f = new File(url.toURI());
                if ((f.isFile()) && (f.getName().endsWith(".jar"))) {
                  ExtensionValidator.addSystemResource(f);
                }
              }
              catch (URISyntaxException localURISyntaxException) {}catch (IOException localIOException) {}
            }
          }
        }
        cl = cl.getParent();
      }
    }
    for (int i = 0; i < this.services.length; i++) {
      this.services[i].init();
    }
  }

StandardServer还实现了await方法,Catalina就是调用它使服务器进入等待状态:

public void await()
  {
    if (this.port == -2) {
      return;
    }
    if (this.port == -1)
    {
      try
      {
        this.awaitThread = Thread.currentThread();
        while (!this.stopAwait) {
          try
          {
            Thread.sleep(10000L);
          }
          catch (InterruptedException localInterruptedException) {}
        }
      }
      finally
      {
        this.awaitThread = null;
      }
      return;
    }
    try
    {
      this.awaitSocket = new ServerSocket(this.port, 1, InetAddress.getByName(this.address));
    }
    catch (IOException e)
    {
      log.error("StandardServer.await: create[" + this.address + ":" + this.port + "]: ", e);

      return;
    }
    try
    {
      this.awaitThread = Thread.currentThread();
      while (!this.stopAwait)
      {
        ServerSocket serverSocket = this.awaitSocket;
        if (serverSocket == null) {
          break;
        }
        Socket socket = null;
        StringBuilder command = new StringBuilder();
        try
        {
          long acceptStartTime = System.currentTimeMillis();
          try
          {
            socket = serverSocket.accept();
            socket.setSoTimeout(10000);
            stream = socket.getInputStream();
          }
          catch (SocketTimeoutException ste)
          {
            InputStream stream;
            log.warn(sm.getString("standardServer.accept.timeout", new Object[] { Long.valueOf(System.currentTimeMillis() - acceptStartTime) }), ste);
            try
            {
              if (socket != null) {
                socket.close();
              }
            }
            catch (IOException localIOException1) {}
            continue;
          }
          catch (AccessControlException ace)
          {
            log.warn("StandardServer.accept security exception: " + ace.getMessage(), ace);
            try
            {
              if (socket != null) {
                socket.close();
              }
            }
            catch (IOException localIOException2) {}
            continue;
          }
          catch (IOException e)
          {
            if (this.stopAwait) {
              try
              {
                if (socket != null) {
                  socket.close();
                }
              }
              catch (IOException localIOException3) {}
            }
            log.error("StandardServer.await: accept: ", e);
          }
          InputStream stream;
          int expected = 1024;
          while (expected < this.shutdown.length())
          {
            if (this.random == null) {
              this.random = new Random();
            }
            expected += this.random.nextInt() % 1024;
          }
          while (expected > 0)
          {
            int ch = -1;
            try
            {
              ch = stream.read();
            }
            catch (IOException e)
            {
              log.warn("StandardServer.await: read: ", e);
              ch = -1;
            }
            if ((ch < 32) || (ch == 127)) {
              break;
            }
            command.append((char)ch);
            expected--;
          }
          try
          {
            if (socket != null) {
              socket.close();
            }
          }
          catch (IOException localIOException5) {}
          match = command.toString().equals(this.shutdown);
        }
        finally
        {
          try
          {
            if (socket != null) {
              socket.close();
            }
          }
          catch (IOException localIOException6) {}
        }
        boolean match;
        if (match)
        {
          log.info(sm.getString("standardServer.shutdownViaPort"));
          break;
        }
        log.warn("StandardServer.await: Invalid command '" + command.toString() + "' received");
      }
    }
    finally
    {
      ServerSocket serverSocket;
      ServerSocket serverSocket = this.awaitSocket;
      this.awaitThread = null;
      this.awaitSocket = null;
      if (serverSocket != null) {
        try
        {
          serverSocket.close();
        }
        catch (IOException localIOException8) {}
      }
    }
  }

await的大致逻辑为先判断端口号port:
- port为-2,直接退出
- port为-1,会进入while(!stopAwait)的循环,并且内部没有break语句,stopAwait只有调用了stop才会变成true,所以此情况下只有外部调用stop才会退出循环
- port为其他值,会进入while(!stopAwait)循环,同时在port所在端口启动serverSocket监听关闭命令,如果接受到了则使用break调出循环。

这里的port为在配置文件中设置的shutdown端口,如果不通过网络命令来关闭tomcat,将port设置为-1即可。await对于接受的参数进行了一定处理,ASCII码小于32及其后面的部分直接丢弃。

Service的启动


       Service的默认实现为org.apache.catalina.core.StandardService,StandardService也继承LifecycleMbeanBase,所以init和start也会调用initInternal和startInternal:

protected void initInternal()
    throws LifecycleException
  {
    super.initInternal();
    if (this.container != null) {
      this.container.init();
    }
    for (Executor executor : findExecutors())
    {
      if ((executor instanceof JmxEnabled)) {
        ((JmxEnabled)executor).setDomain(getDomain());
      }
      executor.init();
    }
    this.mapperListener.init();
    synchronized (this.connectorsLock)
    {
      for (Connector connector : this.connectors) {
        try
        {
          connector.init();
        }
        catch (Exception e)
        {
          String message = sm.getString("standardService.connector.initFailed", new Object[] { connector });

          log.error(message, e);
          if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
            throw new LifecycleException(message);
          }
        }
      }
    }
  }

protected void startInternal()
    throws LifecycleException
  {
    if (log.isInfoEnabled()) {
      log.info(sm.getString("standardService.start.name", new Object[] { this.name }));
    }
    setState(LifecycleState.STARTING);
    if (this.container != null) {
      synchronized (this.container)
      {
        this.container.start();
      }
    }
    synchronized (this.executors)
    {
      for (Executor executor : this.executors) {
        executor.start();
      }
    }
    this.mapperListener.start();
    synchronized (this.connectorsLock)
    {
      for (Connector connector : this.connectors) {
        try
        {
          if (connector.getState() != LifecycleState.FAILED) {
            connector.start();
          }
        }
        catch (Exception e)
        {
          log.error(sm.getString("standardService.connector.startFailed", new Object[] { connector }), e);
        }
      }
    }
  }

StandardService中的initInternal和startInternal方法主要调用container、executors、mapperListener、connectors的init和start方法。MapperListener是Mapper的监听器,用来监控container的变化,executor是用来在connector管理线程的线程池,在server.xml中有参考的用法,只不过是被注释的:

<!--
    <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
        maxThreads="150" minSpareThreads="4"/>
    -->

这样整个tomcat就完成了启动,tomcat的整个启动流程如下图:

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值