Tomcat启动和处理流程

一个简单的http server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
public class HttpServer {
  private boolean shutdown = false;

  public static void main(String[] args) {
    HttpServer server = new HttpServer();
    server.await();
  }

  public void await() {
    ServerSocket serverSocket = null;
    int port = 8080;
    serverSocket =  new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));

    while (!shutdown) {
      Socket socket = null;
      InputStream input = null;
      OutputStream output = null;
      socket = serverSocket.accept();
      input = socket.getInputStream();
      output = socket.getOutputStream();

      Request request = new Request(input);
      request.parse();

      Response response = new Response(output);
      response.setRequest(request);

      if (request.getUri().startsWith("/servlet/")) {
        ServletProcessor1 processor = new ServletProcessor1();
        processor.process(request, response);
      } else {
        StaticResourceProcessor processor = new StaticResourceProcessor();
        processor.process(request, response);
      }

      socket.close();

      shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
    }
  }
}

public class Request {
  public void parse() {
    StringBuffer request = new StringBuffer(2048);
    int i;
    byte[] buffer = new byte[2048];
    i = input.read(buffer);
    for (int j=0; j<i; j++) {
      request.append((char) buffer[j]);
    }
    uri = parseUri(request.toString());
  }

  private String parseUri(String requestString) {
    // intput: GET /test HTTP/1.1
    // output: "/test"
  }
}

public class Response {
  public void sendStaticResource() throws IOException {
    byte[] bytes = new byte[BUFFER_SIZE];
    FileInputStream fis = null;
    File file = new File(HttpServer.WEB_ROOT, request.getUri());
    if (file.exists()) {
      fis = new FileInputStream(file);
      int ch = fis.read(bytes, 0, BUFFER_SIZE);
      while (ch!=-1) {
        output.write(bytes, 0, ch);
        ch = fis.read(bytes, 0, BUFFER_SIZE);
      }
    } else {
      String errorMessage = "HTTP/1.1 404 File Not Found\r\n" +
        "Content-Type: text/html\r\n" +
        "Content-Length: 23\r\n" +
        "\r\n" +
        "<h1>File Not Found</h1>";
      output.write(errorMessage.getBytes());
    }
  }
}

public class StaticResourceProcessor {
  public void process(Request request, Response response) {
    response.sendStaticResource();
  }
}

public class ServletProcessor1 {
  public void process(Request request, Response response) {
    String uri = request.getUri();
    String servletName = uri.substring(uri.lastIndexOf("/") + 1);
    URLClassLoader loader = null;

    // Initialize Class Loader, repository is the folder to find servlet classes.
    URL[] urls = new URL[1];
    URLStreamHandler streamHandler = null;
    File classPath = new File(Constants.WEB_ROOT);
    String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString();
    urls[0] = new URL(null, repository, streamHandler);
    loader = new URLClassLoader(urls);

    // Load class
    Class myClass = null;
    myClass = loader.loadClass(servletName);

    Servlet servlet = null;
    servlet = (Servlet) myClass.newInstance();
    servlet.service((ServletRequest) request, (ServletResponse) response);
  }
}

相关概念

Server. Tomcat服务

Service. 一个应用. Service 只是在 Connector 和 Container 外面多包一层,把它们组装在一起,向外面提供服务,一个 Service可以设置多个 Connector,但是只能有一个 Container容器, 例如一个应用可以同时监听80和443。

Connectors. 主要任务是负责接收浏览器的发过来的 tcp 连接请求,创建一个 Request 和 Response 对象分别用于和请求端交换数据。然后分配线程让 Container 来处理这个请求。 * Connector1. 监听80端口 * Connector2. 监听443端口

Container. Container 是容器的父接口,所有子容器都必须实现这个接口,Container容器的设计用的是典型的责任链的设计模式,它有四个子容器组件构成,分别是:Engine、Host、Context、Wrapper,这四个组件是父子关系,Engine 包含 Host,Host 包含 Context,Context 包含 Wrapper。 * Engine。最顶层的Containter,不能setParent。 * Host。默认是一个,例如localhost。也可以有多个Host。 * Context。代表一个应用程序。setPath设置该应用挂载的地址。 * Wrapper。具体的Servlet Processor。最底层的Container,不能addChild。 - /contextPath1/path1 => Wrapper1 - /contextPath1/path2 => Wrapper2

Lifecycle

Container都有生命周期的概念,使用观察者的设计模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface Lifecycle {
    public static final String STARTEVENT = "start";
    public static final String BEFORESTARTEVENT = "beforestart";
    public static final String AFTERSTARTEVENT = "afterstart";
    public static final String STOPEVENT = "stop";
    public static final String BEFORESTOPEVENT = "beforestop";
    public static final String AFTERSTOPEVENT = "afterstop";

    public void addLifecycleListener(LifecycleListener listener);
    public LifecycleListener[] findLifecycleListeners();
    public void removeLifecycleListener(LifecycleListener listener);
    public void start() throws LifecycleException;
    public void stop() throws LifecycleException;
}

StandardPipeline

Engine, Host, Context, Wrapper都有自己的pipeline. 并且每个pipeline的最后一个Value都会调用下一层的pipeline.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
public class StandardPipeline implements Pipeline, Contained, Lifecycle {
    public synchronized void start() throws LifecycleException {
        // Validate and update our current component state
        if (started)
            throw new LifecycleException
                (sm.getString("standardPipeline.alreadyStarted"));

        // Notify our interested LifecycleListeners
        lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);

        started = true;

        // Start the Valves in our pipeline (including the basic), if any
        for (int i = 0; i < valves.length; i++) {
            if (valves[i] instanceof Lifecycle)
                ((Lifecycle) valves[i]).start();
            registerValve(valves[i]);
        }
        if ((basic != null) && (basic instanceof Lifecycle))
            ((Lifecycle) basic).start();

        if( basic!=null )
            registerValve(basic);

        // Notify our interested LifecycleListeners
        lifecycle.fireLifecycleEvent(START_EVENT, null);

        // Notify our interested LifecycleListeners
        lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);

    }

    // Add a new Valve to the end of the pipeline associated with this Container.
    public void addValve(Valve valve) {
        // Validate that we can add this Valve
        if (valve instanceof Contained)
            ((Contained) valve).setContainer(this.container);

        // Start the new component if necessary
        if (started) {
            if (valve instanceof Lifecycle) {
                try {
                    ((Lifecycle) valve).start();
                } catch (LifecycleException e) {
                    log.error("StandardPipeline.addValve: start: ", e);
                }
            }
            // Register the newly added valve
            registerValve(valve);
        }

        // Add this Valve to the set associated with this Pipeline
        synchronized (valves) {
            Valve results[] = new Valve[valves.length +1];
            System.arraycopy(valves, 0, results, 0, valves.length);
            results[valves.length] = valve;
            valves = results;
        }

    }

    public void invoke(Request request, Response response)
        throws IOException, ServletException {

        // Invoke the first Valve in this pipeline for this request
        //(new StandardPipelineValveContext()).invokeNext(request, response);

        StandardValveContext valveContext =
            (StandardValveContext) request.getValveContext();
        if (valveContext == null) {
            valveContext = new StandardValveContext();
            request.setValveContext(valveContext);
        }

        valveContext.set(basic, valves);
        valveContext.invokeNext(request, response);
        valveContext.set(null, null);

    }
}

public final class StandardValveContext implements ValveContext {
    public final void invokeNext(Request request, Response response) throws IOException, ServletException {
        int subscript = stage;
        stage = stage + 1;

        // Invoke the requested Valve for the current request thread
        if (subscript < valves.length) {
            valves[subscript].invoke(request, response, this);
        } else if ((subscript == valves.length) && (basic != null)) {
            basic.invoke(request, response, this);
        } else {
            throw new ServletException
                (sm.getString("standardPipeline.noValve"));
        }
    }
}

StandardPipeline提供value注册机制。StandardValveContext.invokeNext()方法先执行注册的value,最后执行basic value。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public abstract class ContainerBase implements Container, Lifecycle, Pipeline, MBeanRegistration, Serializable {
    protected Pipeline pipeline = new StandardPipeline(this);
}

public class StandardEngine extends ContainerBase implements Engine {
    public StandardEngine() {
        super();
        pipeline.setBasic(new StandardEngineValve());
    }
}

public class StandardHost extends ContainerBase implements Deployer, Host {
    public StandardHost() {
        super();
        pipeline.setBasic(new StandardHostValve());
    }
}

Engine, Host, Context, Wrapper都继承ContainerBase

StandardEngine, StandardHost, StandardContext, StandardWrapper初始化时都会为pipeline加上相应的BasicValue。StandardEngineValue, StandardHostValue, StandardContextValue, StandardWrapperValue

BasicValues.invoke(req, resp)

StandardEngineValue. 检验请求类型,检验Header必须包含host,根据Requst选择对应的Host。最后调用host.invoke(request, response);

StandardHostValve. 检验请求类型,根据Requst选择对应的Context,更新session时间。最后调用context.invoke(request, response);

StandardContextValue. 检验请求类型,根据Requst选择对应的Wrapper。最后调用wrapper.invoke(request, response);

StandardWrapperValue. - Allocate a servlet instance to process this request - Create the filter chain for this request. Filter chain calls servlet’s service() method

启动过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
public class Catalina extends Embedded {
    public static void main(String args[]) {
        (new Catalina()).process(args);
    }

    public void process(String args[]) {
        setAwait(true);
        setCatalinaHome();
        setCatalinaBase();
        try {
            if (arguments(args)) {
                if (starting) {
                    load(args);
                    start();
                } else if (stopping) {
                    stopServer();
                }
            }
        } catch (Exception e) {
            e.printStackTrace(System.out);
        }
    }

  protected boolean arguments(String args[]) {
        boolean isConfig = false;

        if (args.length < 1) {
            usage();
            return (false);
        }

        for (int i = 0; i < args.length; i++) {
            if (isConfig) {
                configFile = args[i];
                isConfig = false;
            } else if (args[i].equals("-config")) {
                isConfig = true;
            } else if (args[i].equals("-debug")) {
                debug = 1;
            } else if (args[i].equals("-nonaming")) {
                setUseNaming( false );
            } else if (args[i].equals("-help")) {
                usage();
                return (false);
            } else if (args[i].equals("start")) {
                starting = true;
                stopping = false;
            } else if (args[i].equals("stop")) {
                starting = false;
                stopping = true;
            } else {
                usage();
                return (false);
            }
        }

        return (true);
    }

    protected Digester createStartDigester() {
        long t1=System.currentTimeMillis();
        // Initialize the digester
        Digester digester = new CatalinaDigester();
        if (debug>0)
            digester.setDebug(debug);
        digester.setValidating(false);
        digester.setClassLoader(StandardServer.class.getClassLoader());

        // Configure the actions we will be using
        digester.addObjectCreate("Server",
                                 "org.apache.catalina.core.StandardServer",
                                 "className");
        digester.addSetProperties("Server");
        digester.addSetNext("Server",
                            "setServer",
                            "org.apache.catalina.Server");

        digester.addObjectCreate("Server/GlobalNamingResources",
                                 "org.apache.catalina.deploy.NamingResources");
        digester.addSetProperties("Server/GlobalNamingResources");
        digester.addSetNext("Server/GlobalNamingResources",
                            "setGlobalNamingResources",
                            "org.apache.catalina.deploy.NamingResources");

        digester.addObjectCreate("Server/Listener",
                                 null, // MUST be specified in the element
                                 "className");
        digester.addSetProperties("Server/Listener");
        digester.addSetNext("Server/Listener",
                            "addLifecycleListener",
                            "org.apache.catalina.LifecycleListener");

        digester.addObjectCreate("Server/Service",
                                 "org.apache.catalina.core.StandardService",
                                 "className");
        digester.addSetProperties("Server/Service");
        digester.addSetNext("Server/Service",
                            "addService",
                            "org.apache.catalina.Service");

        digester.addObjectCreate("Server/Service/Listener",
                                 null, // MUST be specified in the element
                                 "className");
        digester.addSetProperties("Server/Service/Listener");
        digester.addSetNext("Server/Service/Listener",
                            "addLifecycleListener",
                            "org.apache.catalina.LifecycleListener");

        digester.addObjectCreate("Server/Service/Connector",
                                 "org.apache.coyote.tomcat5.CoyoteConnector",
                                 "className");
        digester.addRule("Server/Service/Connector",
                         new SetAllPropertiesRule());
        digester.addSetNext("Server/Service/Connector",
                            "addConnector",
                            "org.apache.catalina.Connector");

        digester.addObjectCreate("Server/Service/Connector/Factory",
                                 "org.apache.coyote.tomcat5.CoyoteServerSocketFactory",
                                 "className");
        digester.addSetProperties("Server/Service/Connector/Factory");
        digester.addSetNext("Server/Service/Connector/Factory",
                            "setFactory",
                            "org.apache.catalina.net.ServerSocketFactory");

        digester.addObjectCreate("Server/Service/Connector/Listener",
                                 null, // MUST be specified in the element
                                 "className");
        digester.addSetProperties("Server/Service/Connector/Listener");
        digester.addSetNext("Server/Service/Connector/Listener",
                            "addLifecycleListener",
                            "org.apache.catalina.LifecycleListener");

        // Add RuleSets for nested elements
        digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));
        digester.addRuleSet(new EngineRuleSet("Server/Service/"));
        digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
        digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Default"));
        digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/DefaultContext/"));
        digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/Default"));
        digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/DefaultContext/"));
        digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));
        digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/"));

        // When the 'engine' is found, set the parentClassLoader.
        digester.addRule("Server/Service/Engine",
                         new SetParentClassLoaderRule(digester,
                                                      parentClassLoader));

        long t2=System.currentTimeMillis();
        log.debug("Digester for server.xml created " + ( t2-t1 ));
        return (digester);
    }
}

设置项目目录,处理启动参数。使用Digester动态配置加载项。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
public final class StandardServer implements Lifecycle, Server, MBeanRegistration {
    public void addService(Service service) {
        service.setServer(this);

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

            if (initialized) {
                try {
                    service.initialize();
                } catch (LifecycleException e) {
                    e.printStackTrace(System.err);
                }
            }

            if (started && (service instanceof Lifecycle)) {
                try {
                    ((Lifecycle) service).start();
                } catch (LifecycleException e) {
                    ;
                }
            }

            // Report this property change to interested listeners
            support.firePropertyChange("service", null, service);
        }
    }

    /**
     * Invoke a pre-startup initialization. This is used to allow connectors
     * to bind to restricted ports under Unix operating environments.
     */
    public void initialize()
        throws LifecycleException
    {
        if (initialized) {
                log.info(sm.getString("standardServer.initialize.initialized"));
            return;
        }
        initialized = true;

        if( oname==null ) {
            try {
                oname=new ObjectName( "Catalina:type=Server");
                Registry.getRegistry().registerComponent(this, oname, null );
            } catch (Exception e) {
                log.error("Error registering ",e);
            }
        }

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

    public void start() throws LifecycleException {

        // Validate and update our current component state
        if (started) {
            log.debug(sm.getString("standardServer.start.started"));
            return;
        }

        // Notify our interested LifecycleListeners
        lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);

        lifecycle.fireLifecycleEvent(START_EVENT, null);
        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
        lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);

    }
}

向Server添加Service,每次复杂原数组,使用synchronized保证线程安全。

Server.initialize()遍历services并调用Service.initialize(). Server.start()遍历services并调用Service.start()

StandardService与Server类似,都有initialize和start方法,初始化并启动connector

HttpConnector的run方法会调用open()方法, start方法创建processors

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
private ServerSocket open() throws IOException {
    // Acquire the server socket factory for this Connector
    ServerSocketFactory factory = getFactory();

    // If no address is specified, open a connection on all addresses
    if (address == null) {
        logger.log(sm.getString("ajp13Connector.allAddresses"));
        try {
            return (factory.createSocket(port, acceptCount));
        } catch(Exception ex ) {
            ex.printStackTrace();
            return null;
        }
    }

    // Open a server socket on the specified address
    try {
        InetAddress is = InetAddress.getByName(address);
        logger.log(sm.getString("ajp13Connector.anAddress", address));
        return (factory.createSocket(port, acceptCount, is));
    } catch (Exception e) {
        try {
            logger.log(sm.getString("ajp13Connector.noAddress", address));
            return (factory.createSocket(port, acceptCount));
        } catch( Exception e1 ) {
            e1.printStackTrace();
            return null;
        }
    }
}

protected int minProcessors = 5;
private int maxProcessors = 20;
public void start() throws LifecycleException {
    // Validate and update our current state
    if (started)
        throw new LifecycleException
            (sm.getString("ajp13Connector.alreadyStarted"));

    if (debug > 0) {
        debugThread = new DebugThread();
        debugThread.setDaemon(true);
        debugThread.start();
    }

    threadName = "Ajp13Connector[" + port + "]";
    threadGroup = new ThreadGroup(threadName);
    threadGroup.setDaemon(true);
    logger.setConnector(this);
    logger.setName(threadName);
    lifecycle.fireLifecycleEvent(START_EVENT, null);
    started = true;

    // Establish a server socket on the specified port
    try {
        serverSocket = open();
    } catch (IOException e) {
        throw new LifecycleException(threadName + ".open", e);
    }

    // Start our background thread
    threadStart();

    // Create the specified minimum number of processors
    while (curProcessors < minProcessors) {
        if ((maxProcessors > 0) && (curProcessors >= maxProcessors))
            break;
        Ajp13Processor processor = newProcessor();
        recycle(processor);
    }
}
start方法会创建5个Processor,并放到processors数组里。

Processor.start()调用threadStart(),启动线程。run()调用await()等待请求,await()调用Object.wait()等待请求。Connector调用assign方法唤醒processor。assign()运行在HttpConnector线程, 两者通过available变量通信。

HttpProcessor.run()当await()获取到锁后,调用process方法,调用container.invoke()方法,调用pipeline的invoke链,最后实例化Servlet处理请求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/**
 * The background thread that listens for incoming TCP/IP connections and
 * hands them off to an appropriate processor.
 */
public void run() {

    // Process requests until we receive a shutdown signal
    while (!stopped.value()) {

        // Wait for the next socket to be assigned
        if (debug > 0) {
            logger.log("waiting for next socket to be assigned...");
        }
        Socket socket = await();
        if (socket == null)
            continue;

        if (debug > 0) {
            logger.log("socket assigned.");
        }

        // Process the request from this socket
        process(socket);

        // Finish up this request
        if (debug > 0) {
            logger.log("recycling myself ...");
        }
        connector.recycle(this);
    }

    // Tell threadStop() we have shut ourselves down successfully
    synchronized (threadSync) {
        threadSync.notifyAll();
    }
}

// Process an incoming TCP/IP connection on the specified socket.
synchronized void assign(Socket socket) {

    // Wait for the Processor to get the previous Socket
    while (available) {
        try {
            wait();
        } catch (InterruptedException e) {
        }
    }

    // Store the newly available Socket and notify our thread
    this.socket = socket;
    available = true;
    notifyAll();

    if ((debug > 0) && (socket != null))
        logger.log(" An incoming request is being assigned");

}

// Await a newly assigned Socket from our Connector
private synchronized Socket await() {
    // Wait for the Connector to provide a new Socket
    while (!available) {
        try {
            wait();
        } catch (InterruptedException e) {
        }
    }

    // Notify the Connector that we have received this Socket
    Socket socket = this.socket;
    available = false;
    notifyAll();

    if ((debug > 0) && (socket != null))
        logger.log("  The incoming request has been awaited");

    return (socket);
}