天天看點

How tomcat works 讀書筆記十四 伺服器元件和服務元件

之前的項目還是有些問題的,例如

1 隻能有一個連接配接器,隻能處理http請求,無法添加另外一個連接配接器用來處理https。

2 對容器的關閉隻能是粗暴的關閉Bootstrap。

伺服器元件

org.apache.catalina.Server接口的執行個體用來表示Catalina的整個servlet引擎。

我們使用Server就是因為,它用一種優雅的方式來啟動/關閉整個系統。

下面是啟動和停止機制是如何工作的。當伺服器啟動的時候,它啟動它内部的所有元件。然後無限期的等待關閉指令,如果你想要關閉系統,發送一個關閉指令道指定端口即可。當伺服器收到正确的關閉指令後,它停止所有元件的服務。

伺服器還使用了另外一個元件,即服務元件,它用來持有元件,例如容器或者一個多個的連接配接器。服務元件将在本章的 service 小節中介紹。(注意有伺服器元件,也有服務元件,這兩個是不一樣的。)

Server接口中屬性 shutdown 用來持有一個停止服務的指令。屬性 port 則是伺服器等待關閉指令的端口。可以調用伺服器的 addService 方法将服務添加到伺服器。使用removeService 方法将服務删除。findServices 傳回所有伺服器中所有的服務。

StandardServer類

StandardServer類是Server接口的标準實作。在這裡,我們主要介紹它的四個方法。initialize(),start(),stop(),await();

我們一個一個來說。

initialize方法

public void initialize() throws LifecycleException {
        if (initialized)
            throw new LifecycleException (
                sm.getString("standardServer.initialize.initialized"));
        initialized = true;

        // Initialize our defined Services
        for (int i = 0; i < services.length; i++) {
            services[i].initialize();
        }
    }      

我們能看到這裡使用了一個initialized來辨別伺服器是否已經初始化,同時初始化它所包含的服務元件。

另外在stop中,initialized并沒有更改,是以如果伺服器先初始化,再關閉,等再次初始化的時候會抛出異常。

start方法

public void start() throws LifecycleException {

        // Validate and update our current component state
        if (started)
            throw new LifecycleException
                (sm.getString("standardServer.start.started"));
        // Notify our interested LifecycleListeners
    ...
        started = true;

        // Start our defined Services
        synchronized (services) {
            for (int i = 0; i < services.length; i++) {
                if (services[i] instanceof Lifecycle)
                    ((Lifecycle) services[i]).start();
            }
        }

        // Notify our interested LifecycleListeners
       ...
    }      

和初始化幹的事情差不多,循環啟動它所關聯的服務元件。另外在stop方法中,started會被置為false。

stop方法

public void stop() throws LifecycleException {
        // Validate and update our current component state
        if (!started)
            throw new LifecycleException
                (sm.getString("standardServer.stop.notStarted"));

        // Notify our interested LifecycleListeners
    ...
        started = false;

        // Stop our defined Services
        for (int i = 0; i < services.length; i++) {
            if (services[i] instanceof Lifecycle)
                ((Lifecycle) services[i]).stop();
        }

        // Notify our interested LifecycleListeners
       ...
    }      

關閉所有的服務元件。

await方法

這裡不再貼出代碼,await做的事就是監聽8005端口,如果使用者給8005端口發來"SHUTDOWN"就關閉serverSocket,然後await方法就執行完了。

現在再說說initialize方法,start方法,await方法,stop四者者都是在Bootstrap裡調用的。

當await方法執行完畢後,就會調用伺服器的stop方法。

Service接口

org.apache.catalina.Service 接口用于表示服務。一個服務可以有一個容器和多個連接配接器。你可以添加多個連接配接器 ,并将它們跟容器相關聯。

StandardService類

StandardService是Service接口的标準實作。

容器與連接配接器

一個 StandardService 執行個體包括兩種元件:一個容器和多個連接配接器。多個連接配接器可以使得 Tomcat 能服務于多個協定。一個協定用處處理 HTTP 請求,另一個用于處理 HTTPS 請求。

StandardService 類用 container 變量來持有容器執行個體,用 connectors 數組來持有所有的連接配接器

private Container container = null;
private Connector connectors[] = new Connector[0];      

要将一個容器跟一個服務相關聯,可以使用它的 setContainer 方法,

在setContainer中,會把container傳遞給每一個connector。

synchronized (connectors) {
            for (int i = 0; i < connectors.length; i++)
                connectors[i].setContainer(this.container);
        }      

要給一個服務添加連接配接器,可以使用 addConnector 方法。在添加連接配接器的同時也會初始化他們。

要删除一個連接配接器,可以使用 removeConnector 方法。

與生命周期相關的方法

這裡主要包含三個方法,initialize,start,stop。

initialize功能就是調用是以連接配接器的initialize方法。

start功能就是啟動容器和若幹個連接配接器。

stop功能就是關閉容器和若幹個連接配接器。

服務元件的這三個方法都會在伺服器元件裡相應的方法裡被調用,而伺服器元件的這三個方法是在Bootstrap裡調用的。

應用程式

這裡主要是兩個類,一個是 Bootstrap 啟動程式,另一個是 Stopper 類用來停止它。

Bootstrap類

public static void main(String args[]){
......

    Service service = new StandardService();
    service.setName("Stand-alone Service");

    Server server = new StandardServer();
    server.addService(service);      //把服務元件添加到伺服器元件裡
    service.addConnector(connector); //把連接配接器關聯到服務元件裡

    //StandardService class's setContainer will call all its connector's setContainer method
    service.setContainer(engine);    //容器并沒有直接和連接配接器關聯,而是和服務元件關聯

    // Start the new server
    if (server instanceof Lifecycle) {
      try {
        server.initialize();         //調用initialize,start,await方法
        ((Lifecycle) server).start();//這裡會調用服務元件的start,來調用連接配接器與容器的start
                                 //連接配接器會在8080上等待消息....
        server.await();              //除非在8005端口收到了SHUTDOWN資訊 這裡會一直循環
        // the program waits until the await method returns,
        // i.e. until a shutdown command is received.
      }
      catch (LifecycleException e) {
        e.printStackTrace(System.out);
      }
    }

    // Shut down the server
    if (server instanceof Lifecycle) {
      try {
        ((Lifecycle) server).stop();  //收到了SHUTDOWM資訊,關閉伺服器元件
      }
      catch (LifecycleException e) {
        e.printStackTrace(System.out);
      }
    }
  }      

Stopper類

public class Stopper {

  public static void main(String[] args) {
    // the following code is taken from the Stop method of
    // the org.apache.catalina.startup.Catalina class
    int port = 8005;
    try {
      Socket socket = new Socket("127.0.0.1", port);
      OutputStream stream = socket.getOutputStream();
      String shutdown = "SHUTDOWN";
      for (int i = 0; i < shutdown.length(); i++)
        stream.write(shutdown.charAt(i));
      stream.flush();
      stream.close();
      socket.close();
      System.out.println("The server was successfully shut down.");
    }
    catch (IOException e) {
      System.out.println("Error. The server has not been started.");
    }
  }
}