tomcat源码1

看了几天<How Tomcat work>一书,感觉这种书很好,所以就记录一下看了这本书的心德,后面还有几章没完,但是前面说的太精彩了,所以就想把前面几张整理一下,把书中的思路给串起来。
首先看看javax.servlet.Servlet接口
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
package javax.servlet;

import java.io.IOException;


/**
 * Defines methods that all servlets must implement.
 *
 * <p>A servlet is a small Java program that runs within a Web server.
 * Servlets receive and respond to requests from Web clients,
 * usually across HTTP, the HyperText Transfer Protocol.
 *
 * <p>To implement this interface, you can write a generic servlet
 * that extends
 * <code>javax.servlet.GenericServlet</code> or an HTTP servlet that
 * extends <code>javax.servlet.http.HttpServlet</code>.
 *
 * <p>This interface defines methods to initialize a servlet,
 * to service requests, and to remove a servlet from the server.
 * These are known as life-cycle methods and are called in the
 * following sequence:
 * <ol>
 * <li>The servlet is constructed, then initialized with the <code>init</code> method.
 * <li>Any calls from clients to the <code>service</code> method are handled.
 * <li>The servlet is taken out of service, then destroyed with the
 * <code>destroy</code> method, then garbage collected and finalized.
 * </ol>
 *
 * <p>In addition to the life-cycle methods, this interface
 * provides the <code>getServletConfig</code> method, which the servlet
 * can use to get any startup information, and the <code>getServletInfo</code>
 * method, which allows the servlet to return basic information about itself,
 * such as author, version, and copyright.
 *
 * @author     Various
 * @version     $Version$
 *
 * @see     GenericServlet
 * @see     javax.servlet.http.HttpServlet
 *
 */


public interface Servlet {

    /**
     * Called by the servlet container to indicate to a servlet that the
     * servlet is being placed into service.
     *
     * <p>The servlet container calls the <code>init</code>
     * method exactly once after instantiating the servlet.
     * The <code>init</code> method must complete successfully
     * before the servlet can receive any requests.
     *
     * <p>The servlet container cannot place the servlet into service
     * if the <code>init</code> method
     * <ol>
     * <li>Throws a <code>ServletException</code>
     * <li>Does not return within a time period defined by the Web server
     * </ol>
     *
     *
     * @param config            a <code>ServletConfig</code> object
     *                    containing the servlet's
     *                     configuration and initialization parameters
     *
     * @exception ServletException     if an exception has occurred that
     *                    interferes with the servlet's normal
     *                    operation
     *
     * @see                 UnavailableException
     * @see                 #getServletConfig
     *
     */

    public void init(ServletConfig config) throws ServletException;
    
    

    /**
     *
     * Returns a {@link ServletConfig} object, which contains
     * initialization and startup parameters for this servlet.
     * The <code>ServletConfig</code> object returned is the one
     * passed to the <code>init</code> method.
     *
     * <p>Implementations of this interface are responsible for storing the
     * <code>ServletConfig</code> object so that this
     * method can return it. The {@link GenericServlet}
     * class, which implements this interface, already does this.
     *
     * @return        the <code>ServletConfig</code> object
     *            that initializes this servlet
     *
     * @see         #init
     *
     */

    public ServletConfig getServletConfig();
    
    

    /**
     * Called by the servlet container to allow the servlet to respond to
     * a request.
     *
     * <p>This method is only called after the servlet's <code>init()</code>
     * method has completed successfully.
     *
     * <p>  The status code of the response always should be set for a servlet
     * that throws or sends an error.
     *
     *
     * <p>Servlets typically run inside multithreaded servlet containers
     * that can handle multiple requests concurrently. Developers must
     * be aware to synchronize access to any shared resources such as files,
     * network connections, and as well as the servlet's class and instance
     * variables.
     * More information on multithreaded programming in Java is available in
     * <a href="http://java.sun.com/Series/Tutorial/java/threads/multithreaded.html">
     * the Java tutorial on multi-threaded programming</a>.
     *
     *
     * @param req     the <code>ServletRequest</code> object that contains
     *            the client's request
     *
     * @param res     the <code>ServletResponse</code> object that contains
     *            the servlet's response
     *
     * @exception ServletException     if an exception occurs that interferes
     *                    with the servlet's normal operation
     *
     * @exception IOException         if an input or output exception occurs
     *
     */

    public void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException;
    
    

    /**
     * Returns information about the servlet, such
     * as author, version, and copyright.
     *
     * <p>The string that this method returns should
     * be plain text and not markup of any kind (such as HTML, XML,
     * etc.).
     *
     * @return         a <code>String</code> containing servlet information
     *
     */

    public String getServletInfo();
    
    

    /**
     *
     * Called by the servlet container to indicate to a servlet that the
     * servlet is being taken out of service.  This method is
     * only called once all threads within the servlet's
     * <code>service</code> method have exited or after a timeout
     * period has passed. After the servlet container calls this
     * method, it will not call the <code>service</code> method again
     * on this servlet.
     *
     * <p>This method gives the servlet an opportunity
     * to clean up any resources that are being held (for example, memory,
     * file handles, threads) and make sure that any persistent state is
     * synchronized with the servlet's current state in memory.
     *
     */

    public void destroy();
}
在Servlet接口中声明了5个方法里,init(),service(),destory()方法是与servlet的生命周期相关的方法,当实例化某个servlet类后,Servlet容器会调用其init()方法进行初始化,至于Servlet容器是如何调用,后面会分析。后面会被类与类之间的衔接来做一定的分析。在servlet接收任何请求之前,必须是经过正确初始化的。一般情况下init()方法可以留空。
当servlet的一个客户端请求到达后,servlet容器就调用相应的servlet的service()方法,并将javax.serlvet.servletRequest对象和javax.servlet.servletResponse对象作为参数传入,ServletResquest对象包含客户端的HTTP请求的信息,ServletResponse对象则封装servlet的请求信息。这两个类的设计十分有趣,在后面就具体分析这两个类。在servlet对象的整个生命周期内,service()方法会被多次调用。
在将servlet实例从服务中移除前,servlet容器会调用servlet实例的destory()方法,一般当Servlet容器关闭或servlet容器要释放内存时,才会将servlet实例移除,而且只有当servlet实例的service()方法中的线程都退出或执行超时后,才会调用destory()方法,当servlet容器调用了某个servlet实例的destory()方法后,它就不再调用该servlet实例的service()方法了,调用destory()方法让servlet对象有机会去清理自身持有的资源,如内存,文件句柄和线程等,确保有所的持久化与内存中该servlet对象的当前状态同步。
下面<how tomcat work>中的一个servlet类:
import javax.servlet.*;
import java.io.IOException;
import java.io.PrintWriter;
public class PrimitiveServlet implements Servlet {
public void init(ServletConfig config) throws ServletException {
System.out.println("init");
}
public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException {
System.out.println("from service");
PrintWriter out = response.getWriter();
out.println("Hello. Roses are red.");
out.print("Violets are blue.");
}
public void destroy() {
System.out.println("destroy");
}
public String getServletInfo() {
return null;
}
public ServletConfig getServletConfig() {
return null;
}
}
一个功能齐全的servlet容器有以下几件事要做:
1,当第一次调用某个servlet,要载入该servlet类,并调用其init()方法(仅此一次);
2,针对每个request请求,创建一个javax.servlet.ServletRequest实例和一个javax.servlet.ServletResponse实例;
3,调用该servlet的service()方法,将servletRequest对象和servletResponse对象作为参数传入;
4,当关闭该servlet类时,调用其destory()方法,并卸载该servlet类。

那么从客户连接到tomcat上来,最重要要做的事就是如何处理客户的请求并且把请求传递给Servlet容器,虽然看起来很简单,但是这整个过程确是如此的繁杂,包括字符串的解析,以及cookies,session,参数请求等等的工作。那么我们首先的工作就是分析客户连接到tomcat上来这一块,也就是tomcat的默认连接器。

Tomcat的连接器必须实现org.apache.catalina.Connecor接口,在接口中声明了很多方法,其中最重要的是getContainer(),setContainer(),createRequest(),createResponse()方法。为什么这么说呢?看看以下分析:
setContainer()方法用于将连接器和某个servlet容器相关联。如果不跟特定的servlet容器关联起来,我靠,你准备要把包装好的HttpRequest和HttpResponse对象传给谁阿??
getContainer()方法返回与当前连接器相关联的servlet容器。createRequest()方法会引入的HTTP请求创建request对象,相应的,createResponse()方法会创建一个response对象。
org.apache.catalina.connector.http.HttpConnector类实现了Connector接口,首先大概说以下HttpConnector类做的一些工作:
1,首先它实现了org.apache.catalina.Connector接口(使其可以成为Catalina中的连接器),又实现了java.lang.Runnble接口(确保它的实例在自己的线程中运行)和实现了org.apache.catalina.Lifecycle接口,Lifecycle接口用于维护每个实现了该接口的每个Catalina组件的生命周期。但是这个接口目前不是重点,在后面会讲到。
2,由于HttpConnector实现了Lifecycle接口,因此当创建一个HttpConnector实例后,就应该调用其initialize()方法和start()方法,这两个方法如何调用在后面会详细讲到,现在就记住一点:这两个方法只应该被调用一次。
3,HttpConnector类还负责创建服务器套接字,这是最重要的一点,没有这个,啥事都不用做了。HttpConnector类的initialize()方法会调用一个私有方法open(),后者返回一个java.net.ServerSocket实例,赋值给成员变量serverSocket。但是,这里没有直接调用ServerSocket类的构造函数,而是通过open()方法从一个服务器套接字工厂得到一个实例。
4,维护HttpProcessor实例:HttpProcessor实例是用来处理HTTP请求的,在Tomcat的默认连接器中,HttpConnector实例有一个HttpProcessor对象池,每个HttpProcessor实例都运行在其自己的线程中。这样HttpConnector实例就可以同时处理多个HTTP请求了。
好了,接下来就是具体看看它们这几个步骤中tomcat源码的实现:
首先我们从连接用户开始入手:
org.apache.catalina.connector.http.HttpConnector
        /**
     * The shutdown signal to our background thread
     */
    private boolean stopped = false;
    
     /**
     * Use TCP no delay ?
     */
    private boolean tcpNoDelay = true;
    
      /**
     * Timeout value on the incoming connection.
     * Note : a value of 0 means no timeout.
     */
    private int connectionTimeout = Constants.DEFAULT_CONNECTION_TIMEOUT;
    
      /**
     * Has this component been started yet?
     */
    private boolean started = false;
    
    
     /**
     * The thread synchronization object.
     */
    private Object threadSync = new Object();
        
        //该方法运行在一个线程中
 // ---------------------------------------------- Background Thread Methods


    /**
     * The background thread that listens for incoming TCP/IP connections and
     * hands them off to an appropriate processor.
     */
    public void run() {

        // Loop until we receive a shutdown command
               //如果tomcat接收到shutdown的命令,那么将会关闭连接器
        while (!stopped) {

            // Accept the next incoming connection from the server socket
            Socket socket = null;
            try {
                //                if (debug >= 3)
                //                    log("run: Waiting on serverSocket.accept()");
                socket = serverSocket.accept();//服务器等待用户的连接
                //                if (debug >= 3)
                //                    log("run: Returned from serverSocket.accept()");
                if (connectionTimeout > 0)
                    socket.setSoTimeout(connectionTimeout);//设置超时时间
                socket.setTcpNoDelay(tcpNoDelay);//启用/禁用 TCP_NODELAY
            } catch (AccessControlException ace) {
                log("socket accept security exception", ace);
                continue;
            } catch (IOException e) {
                //                if (debug >= 3)
                //                    log("run: Accept returned IOException", e);
                try {
                    // If reopening fails, exit
                    synchronized (threadSync) {
                        if (started && !stopped)//如何其他组件还在运行或者socket还没关闭
                            log("accept error: ", e);
                        if (!stopped) {//如果socket还没关闭
                            //                    if (debug >= 3)
                            //                        log("run: Closing server socket");
                            serverSocket.close();//关闭服务器
                            //                        if (debug >= 3)
                            //                            log("run: Reopening server socket");
                            serverSocket = open();//创建新的serverSocket对象
                        }
                    }
                    //                    if (debug >= 3)
                    //                        log("run: IOException processing completed");
                } catch (IOException ioe) {
                    log("socket reopen, io problem: ", ioe);
                    break;
                } catch (KeyStoreException kse) {
                    log("socket reopen, keystore problem: ", kse);
                    break;
                } catch (NoSuchAlgorithmException nsae) {
                    log("socket reopen, keystore algorithm problem: ", nsae);
                    break;
                } catch (CertificateException ce) {
                    log("socket reopen, certificate problem: ", ce);
                    break;
                } catch (UnrecoverableKeyException uke) {
                    log("socket reopen, unrecoverable key: ", uke);
                    break;
                } catch (KeyManagementException kme) {
                    log("socket reopen, key management problem: ", kme);
                    break;
                }

                continue;
            }

            // Hand this socket off to an appropriate processor
            HttpProcessor processor = createProcessor();//从HttpProcessor获得一个HttpProcessor对象或者是当池中没有HttpProcessor对象。
            if (processor == null) {
                try {
                    log(sm.getString("httpConnector.noProcessor"));
                    socket.close();//关闭socket
                } catch (IOException e) {
                    ;
                }
                continue;
            }
            //            if (debug >= 3)
            //                log("run: Assigning socket to processor " + processor);
            processor.assign(socket);//处理HTTP请求

            // The processor will recycle itself when it finishes

        }

        // Notify the threadStop() method that we have shut ourselves down
        //        if (debug >= 3)
        //            log("run: Notifying threadStop() that we have shut down");
        synchronized (threadSync) {
            threadSync.notifyAll();//唤醒有所的线程
        }

    }
    这段代码衍生出来的需要注意的东西有很多,但是先说一点,上面的log()方法是日志记录,这里暂时不说它,后面会详细的说它。好了,现在要从这段代码开始来把握几点需要注意的地方。有些地方需要加上UML来进行解释会比较好!
    
    第一点需要关注的地方:serverSocket = open();我们来看看open()方法做了些什么事情先。首先它是一个私有的方法,也就是用户不能去重写它的方法。
   org.apache.catalina.connector.http.HttpConnector
   
    /**
     * The IP address on which to bind, if any.  If <code>null</code>, all
     * addresses on the server will be bound.
     */
    private String address = null;
    
    
     /**
     * The port number on which we listen for HTTP requests.
     */
    private int port = 8080;
    
    
     /**
     * The accept count for this Connector.
     */
    private int acceptCount = 10;
    
    
    
     private ServerSocket open()
    throws IOException, KeyStoreException, NoSuchAlgorithmException,
           CertificateException, UnrecoverableKeyException,
           KeyManagementException
    {

        // 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) {
            log(sm.getString("httpConnector.allAddresses"));
            try {
                return (factory.createSocket(port, acceptCount));//返回创建好了的套接字
            } catch (BindException be) {
                throw new BindException(be.getMessage() + ":" + port);
            }
        }

        // Open a server socket on the specified address
        try {
            InetAddress is = InetAddress.getByName(address);//在给定主机名的情况下确定主机的IP地址
            log(sm.getString("httpConnector.anAddress", address));
            try {
                return (factory.createSocket(port, acceptCount, is));
            } catch (BindException be) {
                throw new BindException(be.getMessage() + ":" + address +
                                        ":" + port);
            }
        } catch (Exception e) {
            log(sm.getString("httpConnector.noAddress", address));
            try {
                return (factory.createSocket(port, acceptCount));
            } catch (BindException be) {
                throw new BindException(be.getMessage() + ":" + port);
            }
        }

    }
    看看open()方法:很简单,不多说。主要要加索,保证线程安全
      /**
     * Return the server socket factory used by this Container.
     */
    public ServerSocketFactory getFactory() {

        if (this.factory == null) {
            synchronized (this) {
                this.factory = new DefaultServerSocketFactory();
            }
        }
        return (this.factory);

    }
    
    
    那么从上面的代码用到了工厂设计模式,具体继续跟踪源码的实现:
    apache.catalina.net.ServerSocketFactory,该类是一个接口,提供了三个方法:
    其实也就是java.net.ServerSocket的三个方法构造,只不过换了以下名字调用而已,它的子类DefaultServerSocketFactory实现了这个接口,并且这个类是fianl类,不可以被集成。具体看看这个类的实现:
    /*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package org.apache.catalina.net;


import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.KeyManagementException;
import java.security.cert.CertificateException;
import org.apache.catalina.net.ServerSocketFactory;


/**
 * Default server socket factory, which returns unadorned server sockts.
 *
 * @author db@eng.sun.com
 * @author Harish Prabandham
 * @author Craig R. McClanahan
 */

public final class DefaultServerSocketFactory implements ServerSocketFactory {


    // --------------------------------------------------------- Public Methods


    /**
     * Returns a server socket which uses all network interfaces on
     * the host, and is bound to a the specified port.  The socket is
     * configured with the socket options (such as accept timeout)
     * given to this factory.
     *
     * @param port the port to listen to
     *
     * @exception IOException                input/output or network error
     * @exception KeyStoreException          error instantiating the
     *                                       KeyStore from file (SSL only)
     * @exception NoSuchAlgorithmException   KeyStore algorithm unsupported
     *                                       by current provider (SSL only)
     * @exception CertificateException       general certificate error (SSL only)
     * @exception UnrecoverableKeyException  internal KeyStore problem with
     *                                       the certificate (SSL only)
     * @exception KeyManagementException     problem in the key management
     *                                       layer (SSL only)
     */
    public ServerSocket createSocket (int port)
    throws IOException, KeyStoreException, NoSuchAlgorithmException,
           CertificateException, UnrecoverableKeyException,
           KeyManagementException {

        return (new ServerSocket(port));

    }


    /**
     * Returns a server socket which uses all network interfaces on
     * the host, is bound to a the specified port, and uses the
     * specified connection backlog.  The socket is configured with
     * the socket options (such as accept timeout) given to this factory.
     *
     * @param port the port to listen to
     * @param backlog how many connections are queued
     *
     * @exception IOException                input/output or network error
     * @exception KeyStoreException          error instantiating the
     *                                       KeyStore from file (SSL only)
     * @exception NoSuchAlgorithmException   KeyStore algorithm unsupported
     *                                       by current provider (SSL only)
     * @exception CertificateException       general certificate error (SSL only)
     * @exception UnrecoverableKeyException  internal KeyStore problem with
     *                                       the certificate (SSL only)
     * @exception KeyManagementException     problem in the key management
     *                                       layer (SSL only)
     */
    public ServerSocket createSocket (int port, int backlog)
    throws IOException, KeyStoreException, NoSuchAlgorithmException,
           CertificateException, UnrecoverableKeyException,
           KeyManagementException {

        return (new ServerSocket(port, backlog));

    }


    /**
     * Returns a server socket which uses only the specified network
     * interface on the local host, is bound to a the specified port,
     * and uses the specified connection backlog.  The socket is configured
     * with the socket options (such as accept timeout) given to this factory.
     *
     * @param port the port to listen to
     * @param backlog how many connections are queued
     * @param ifAddress the network interface address to use
     *
     * @exception IOException                input/output or network error
     * @exception KeyStoreException          error instantiating the
     *                                       KeyStore from file (SSL only)
     * @exception NoSuchAlgorithmException   KeyStore algorithm unsupported
     *                                       by current provider (SSL only)
     * @exception CertificateException       general certificate error (SSL only)
     * @exception UnrecoverableKeyException  internal KeyStore problem with
     *                                       the certificate (SSL only)
     * @exception KeyManagementException     problem in the key management
     *                                       layer (SSL only)
     */
    public ServerSocket createSocket (int port, int backlog,
                                      InetAddress ifAddress)
    throws IOException, KeyStoreException, NoSuchAlgorithmException,
           CertificateException, UnrecoverableKeyException,
           KeyManagementException {

        return (new ServerSocket(port, backlog, ifAddress));

    }
是吧,是java.nio.ServerSocket类的三个构造方法吧。port端口号,backlog可接收的队列长度,ifAddress要将服务器绑定到的InetAddress
好的第一个问题解决,接下来的几个问题明天接着说!回宿舍睡觉!    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值