<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Tomcat源碼分析--啟動

          共 36217字,需瀏覽 73分鐘

           ·

          2021-03-26 08:37

          點擊上方藍(lán)色字體,選擇“標(biāo)星公眾號”

          優(yōu)質(zhì)文章,第一時間送達(dá)

          首先找到catalina.sh中的啟動腳本:

          eval $_NOHUP "\"$_RUNJAVA\"" "\"$CATALINA_LOGGING_CONFIG\"" $LOGGING_MANAGER "$JAVA_OPTS" "$CATALINA_OPTS" \
                -D$ENDORSED_PROP="\"$JAVA_ENDORSED_DIRS\"" \
                -classpath "\"$CLASSPATH\"" \
                -Dcatalina.base="\"$CATALINA_BASE\"" \
                -Dcatalina.home="\"$CATALINA_HOME\"" \
                -Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \
                org.apache.catalina.startup.Bootstrap "$@" start \
                >> "$CATALINA_OUT" 2>&1 "&"

          啟動類為org.apache.catalina.startup.Bootstrap,在Bootstrap類中找到main方法:

              public static void main(String args[]) {
                  if (daemon == null) {
                      // Don't set daemon until init() has completed
                      Bootstrap bootstrap = new Bootstrap();
                      try {
                          //初始化Bootstrap
                          bootstrap.init();
                      } catch (Throwable t) {
                          handleThrowable(t);
                          t.printStackTrace();
                          return;
                      }
                      daemon = bootstrap;
                  } else {
                      // When running as a service the call to stop will be on a new
                      // thread so make sure the correct class loader is used to prevent
                      // a range of class not found exceptions.
                      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) {
                      // Unwrap the Exception for clearer error reporting
                      if (t instanceof InvocationTargetException &&
                              t.getCause() != null) {
                          t = t.getCause();
                      }
                      handleThrowable(t);
                      t.printStackTrace();
                      System.exit(1);
                  }
           
              }


          初始化Bootstrap

          在main方法中會首先初始化Bootstrap,然后解析command,根據(jù)不同的command執(zhí)行不同的邏輯處理,通過catalina.sh啟動Tomcat傳人的參數(shù)是"start",不過看代碼中command默認(rèn)也是start。我們首先看Bootstrap的初始化方法bootstrap.init():

          public void init() throws Exception {
                  //初始化類加載器
                  initClassLoaders();
                  //設(shè)置TCCL
                  Thread.currentThread().setContextClassLoader(catalinaLoader);
           
                  SecurityClassLoad.securityClassLoad(catalinaLoader);
                  
                  //加載并創(chuàng)建啟動類:Catalina
                  Class<?> startupClass =
                      catalinaLoader.loadClass
                      ("org.apache.catalina.startup.Catalina");
                  Object startupInstance = startupClass.newInstance();
                  //通過反射設(shè)置parentClassLoader屬性
                  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);
                  //賦值啟動類到catalinaDaemon屬性
                  catalinaDaemon = startupInstance;
              }


          初始化動作主要是初始化類加載器,然后加載并實例化啟動類(Catalina)。這里初始化的類加載器是common、shared、catalina三個類加載器,沒有初始化WebApp的類加載器:

          private void initClassLoaders() {
                  try {
                      commonLoader = createClassLoader("common", null);
                      if( commonLoader == null ) {
                          // no config file, default to this loader - we might be in a 'single' env.
                          commonLoader=this.getClass().getClassLoader();
                      }
                      catalinaLoader = createClassLoader("server", commonLoader);
                      sharedLoader = createClassLoader("shared", commonLoader);
                  } catch (Throwable t) {
                      handleThrowable(t);
                      log.error("Class loader creation threw exception", t);
                      System.exit(1);
                  }
              }


          6.x以后的版本合并了share、common等目錄,可以根據(jù)參數(shù)server.loader、share.loader等決定是否啟動老的結(jié)構(gòu),以創(chuàng)建sharedClassLoader為例:

          private ClassLoader createClassLoader(String name, ClassLoader parent)
                  throws Exception {
                  //創(chuàng)建shareClassLoader時,name參數(shù)是share
                  String value = CatalinaProperties.getProperty(name + ".loader");
                  if ((value == null) || (value.equals("")))
                      //如果沒有配置share.loader,那么使用直接返回父類加載器
                      return parent;
                  ......
                  //創(chuàng)建shareClassLoader的邏輯
          }


          再回到bootstrap.init()的邏輯,創(chuàng)建好類加載器之后,catalinaDemon已經(jīng)被賦值為一個Catalina對象。


          解析command參數(shù)

          Bootstrap初始化工作完成后,進(jìn)入command==start的處理邏輯:

          if (command.equals("start")) {
              daemon.setAwait(true);
              daemon.load(args);
              daemon.start();
          }


          一共就三行代碼,首先是setAwait(true),看看Bootstrap.setAwait方法:

          public void setAwait(boolean await)
                  throws Exception {
           
                  Class<?> paramTypes[] = new Class[1];
                  paramTypes[0] = Boolean.TYPE;
                  Object paramValues[] = new Object[1];
                  paramValues[0] = Boolean.valueOf(await);
                  Method method =
                      catalinaDaemon.getClass().getMethod("setAwait", paramTypes);
                  method.invoke(catalinaDaemon, paramValues);
           
              }

          通過反射調(diào)用了catalinaDaemon的setAwait方法,前面提到了,catalinaDaemon在Bootstrap初始化的時候生成,我們這里直接看它的setAwait方法:

          protected boolean await = false;
          public void setAwait(boolean b) {
              await = b;
          }

          方法很簡單,就是設(shè)置await屬性,這里將其設(shè)置為了true。然后再回到Bootstrap處理start指令的流程中:

          daemon.setAwait(true);//將Catalina的await屬性設(shè)置為true
          daemon.load(args);//加載初始化Tomcat的核心組件
          daemon.start();//解析文件,啟動組件

          啟動組件

          daemon.load方法主要加載Tomcat的核心組件,這個部分后面再分析,我們直接看daemon.start()方法是如何啟動組件的:

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

          還是通過反射調(diào)用Catalina的start方法(部分代碼):

              protected boolean useShutdownHook = true;
              public void start() {
                  if (getServer() == null) {
                      //生成server實例
                      load();
                  }
                  if (getServer() == null) {
                      log.fatal("Cannot start server. Server instance is not configured.");
                      return;
                  }
                  // 啟動server
                  getServer().start();
                  //注冊一個shutdownhOOK
                  if (useShutdownHook) {
                      if (shutdownHook == null) {
                          shutdownHook = new CatalinaShutdownHook();
                      }
                      Runtime.getRuntime().addShutdownHook(shutdownHook);
                      LogManager logManager = LogManager.getLogManager();
                      if (logManager instanceof ClassLoaderLogManager) {
                          ((ClassLoaderLogManager) logManager).setUseShutdownHook(
                                  false);
                      }
                  }
                  if (await) {
                      //await
                      await();
                      stop();
                  }
              }

          load()方法會解析server.xml節(jié)點,對應(yīng)初始化很多對象,包括server、engine、host、connector等等。最主要的就是頂層Server節(jié)點,對應(yīng)的StandardServer對象:

          digester.addObjectCreate("Server","org.apache.catalina.core.StandardServer","className");

          這部分流程后面再分析。啟動server后,首先注冊了一個shutdownHook(關(guān)于shutdownHook可以參考JAVA之ShutdownHook源碼分析),主要應(yīng)對不是通過socket正常退出的情況。前面通過反射設(shè)置了await屬性為true,所以進(jìn)入await()方法:

          public void await() {
                  getServer().await();
              }

          阻塞主線程

          Catalina的await方法又調(diào)用了getServer的await方法,server在前面的load階段已經(jīng)創(chuàng)建,指定的是org.apache.catalina.core.StandardServer,進(jìn)入它的await方法:

          public void await() {
                  if( port == -2 ) {
                      //內(nèi)嵌的tomcat,直接返回
                      return;
                  }
                  if( port==-1 ) {
                   //如果==-1,根據(jù)stopAwait參數(shù)間隔10秒死檢查循環(huán)
                      try {
                          awaitThread = Thread.currentThread();
                          while(!stopAwait) {
                              try {
                                  Thread.sleep( 10000 );
                              } catch( InterruptedException ex ) {
                                  // continue and check the flag
                              }
                          }
                      } finally {
                          awaitThread = null;
                      }
                      return;
                  }
           
                  //根據(jù)port創(chuàng)建一個ServerSocket,如果port小于0,會拋出異常
                  try {
                      awaitSocket = new ServerSocket(port, 1,
                              InetAddress.getByName(address));
                  } catch (IOException e) {
                      log.error("StandardServer.await: create[" + address
                                         + ":" + port
                                         + "]: ", e);
                      return;
                  }
           
                  try {
                      awaitThread = Thread.currentThread();
           
                      // Loop waiting for a connection and a valid command
                      while (!stopAwait) {
                       //根據(jù)標(biāo)識死循環(huán)
                          ServerSocket serverSocket = awaitSocket;
                          if (serverSocket == null) {
                              break;
                          }
           
                          // Wait for the next connection
                          Socket socket = null;
                          StringBuilder command = new StringBuilder();
                          try {
                              InputStream stream;
                              long acceptStartTime = System.currentTimeMillis();
                              try {
                               //調(diào)用accept方法阻塞,等待連接
                                  socket = serverSocket.accept();
                                  socket.setSoTimeout(10 * 1000);  // Ten seconds
                                  stream = socket.getInputStream();
                              } catch (SocketTimeoutException ste) {
                                  // This should never happen but bug 56684 suggests that
                                  // it does.
                                  log.warn(sm.getString("standardServer.accept.timeout",
                                          Long.valueOf(System.currentTimeMillis() - acceptStartTime)), ste);
                                  continue;
                              } catch (AccessControlException ace) {
                                  log.warn("StandardServer.accept security exception: "
                                          + ace.getMessage(), ace);
                                  continue;
                              } catch (IOException e) {
                                  if (stopAwait) {
                                      // Wait was aborted with socket.close()
                                      break;
                                  }
                                  log.error("StandardServer.await: accept: ", e);
                                  break;
                              }
           
                              // 從socket中讀取數(shù)據(jù),shutdown是一個字符串可以在server.xml中配置,默認(rèn)為SHUTDOWN
                              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) {
                                      log.warn("StandardServer.await: read: ", e);
                                      ch = -1;
                                  }
                                  // Control character or EOF (-1) terminates loop
                                  if (ch < 32 || ch == 127) {
                                      break;
                                  }
                                  command.append((char) ch);
                                  expected--;
                              }
                          } finally {
                              // Close the socket now that we are done with it
                              try {
                                  if (socket != null) {
                                      socket.close();
                                  }
                              } catch (IOException e) {
                                  // Ignore
                              }
                          }
           
                          boolean match = command.toString().equals(shutdown);
                          if (match) {
                           //如果讀取的數(shù)據(jù)是shutdown對應(yīng)的字符串就跳出死循環(huán)
                              log.info(sm.getString("standardServer.shutdownViaPort"));
                              break;
                          } else
                              log.warn("StandardServer.await: Invalid command '"
                                      + command.toString() + "' received");
                      }
                  } finally {
                      ServerSocket serverSocket = awaitSocket;
                      awaitThread = null;
                      awaitSocket = null;
           
                      // Close the server socket and return
                      if (serverSocket != null) {
                          try {
                           //斷開socket連接
                              serverSocket.close();
                          } catch (IOException e) {
                              // Ignore
                          }
                      }
                  }
              }


          代碼邏輯很長,其實主要就干幾件事:


          如果port==-2,表示是內(nèi)嵌的tomcat,直接返回

          如果port==-1,根據(jù)標(biāo)識死循環(huán)阻塞

          否則根據(jù)port創(chuàng)建一個ServerSocket,并且循環(huán)阻塞等待socket連接,直到接收到shutdown指令

          shutdown指令和對應(yīng)的port可以在server.xml中自行配置:

          <Server port="8005" shutdown="SHUTDOWN">
          ......
          </Server>

          就通過這樣的方式阻塞主線程,直到shutdown。await方法返回后,調(diào)用Catalina.stop()方法停止服務(wù)器。大體流程如下:




          ————————————————

          版權(quán)聲明:本文為CSDN博主「黃智霖-blog」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明。

          原文鏈接:

          https://blog.csdn.net/huangzhilin2015/article/details/115048022




          鋒哥最新SpringCloud分布式電商秒殺課程發(fā)布

          ??????

          ??長按上方微信二維碼 2 秒





          感謝點贊支持下哈 

          瀏覽 53
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  人人爱天天摸 | 爱爱亚洲| 啊v视频 | 日韩毛片免费观看 | 日韩黄色一区 |