tomcat源码分析一

tomcat总体结构

Server:接受请求并解析,完成相关任务,返回处理结果。通常情况下使用Socket监听服务器指定端口来实现该功能,一个最简单的服务设计如下:
Start():启动服务器,打开socket连接,监听服务端口,接受客户端请求、处理、返回响应
Stop():关闭服务器,释放资源
缺点:请求监听和请求处理放一起扩展性很差(协议的切换 tomcat独立部署使用HTTP协议,与Apache集成时使用AJP协议
改进一:网络协议与请求处理分离:一个Server包含多个Connector(链接器)和Container(容器)
Connector:开启Socket并监听客户端请求,返回响应数据;
Container:负责具体的请求处理
缺点:Connector接受的请求由那个Container处理,需要建立映射规则
改进二:一个Server可以包含多个Service,每一个Service都是独立的,他们共享一个JVM以及系统类库。一个Service负责维护多个Connector和一个Container,这样来自Connector的请求只能有它所属的Service维护的Container处理。
在这里Container是一个通用的概念,为了明确功能,并与Tomcat中的组件名称相同,可以将Container命名为Engineer
在Engine容器中需要支持管理WEB应用,当接收到Connector的处理请求时,Engine容器能够找到一个合适的Web应用来处理,因此在上面设计的基础上增加Context来表示一个WEB应用,并且一个Engine可以包含多个Context。
缺点:应用服务器需要将每个域名抽象为一个虚拟主机
在一个web应用中,可以包含多个Servlet实例来处理来自不同的链接请求,因此我们还需要一个组件概念来表示Servlet定义,即Wrapper。在前面的多次Container容器中,有Engine、Host、Context、Wrapper等,可以理解为Container的子类.

tomcat源码生命周期分析:从官网上下载tomcat源码包,此次分析使用的版本tomcat-8.5.54

一般分为两类:链接器的实现(LifecycleBase),容器的实现(container)。及相关抽象类的实现。

BootStrap(引导程序)是Tomcat的入口。找到java目录下org.apache.catalina.startup包下的Bootstrap类。
Main方法和static语句块:
Static语句块的作用(看代码):在静态代码块中设置catalinaHome和catalinaBase两个路径

static {...
System.setProperty(Globals.CATALINA_HOME_PROP, catalinaHomeFile.getPath());//Globals.CATALINA_HOME_PROP="catalina.home",我们可以通过设置vm参数的形式传入:editConfig:-Dcatalina.home=catalina-home -Dcatalina.base=catalina-home
// Then base
String base = System.getProperty(Globals.CATALINA_BASE_PROP);...
}

mian方法的作用:实例化BootStrap 初始化BootStrap daemon.load(args) daemon.start();

main{...
    try {
        bootstrap.init();
    }...
    try {
            String command = "start";
            if (command.equals("startd")) {...
            } else if (command.equals("start")) {
                daemon.setAwait(true);
                daemon.load(args);
                daemon.start();
                if (null == daemon.getServer()) {
                    System.exit(1);
                }
            } else if (command.equals("stop")) {...
    }...
}

我们先分析init方法: 初始化commonLoader、catalinaLoader、sharedLoader,设置catalinaDaemon为Catalina实例

init(){...
    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);
        ...
        };// 初始化commonLoader、catalinaLoader、sharedLoader 
   
        Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
        Object startupInstance = startupClass.getConstructor().newInstance();
        // Set the shared extensions class loader
        if (log.isDebugEnabled())
            log.debug("Setting startup class properties");
        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);//此处实际是调用Catalina相关方法
        catalinaDaemon = startupInstance;
}

load、start实际上是由Bootstrap反射调用Catalina的load、start,这一部分代码将在下面的Catalina部分进行分析。load阶段主要是通过读取conf/server.xml或者server-embed.xml,实例化Server、Service、Connector、Engine、Host等组件,并调用Lifecycle#init()完成初始化动作,以及发出INITIALIZING、INITIALIZED事件

load() {...
initDirs();//初始化jmx的环境变量
initNaming();// Before digester - it may be needed 初始化命名空间
Digester digester = createStartDigester();//Create and execute our Digester 定义解析server.xml的配置,告诉Digester哪个xml标签应该解析成什么类
...
 // 如果还是加载不到xml,则直接return,省略部分代码......
      try {
          inputSource.setByteStream(inputStream);
          // 把Catalina作为一个顶级实例
          digester.push(this);
          // 解析过程会实例化各个组件,比如Server、Container、Connector等
          digester.parse(inputSource);
      } catch (SAXParseException spe) {
          // 处理异常......
      }
    } finally {
        // 关闭IO流......
    }
    // 给Server设置catalina信息
    getServer().setCatalina(this);
    getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
    getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
    // Stream redirection
    initStreams();
    // 调用Lifecycle的init阶段
    try {
        getServer().init();//初始化Server,找到具体实现类方法
    } catch (LifecycleException e) {
        // ......
    }
    // ......
}
StandardServer.initInternal(){...//组件都继承了LifeCycleBase,该类中的init会调用initInternal()由子类具体去实现
// Initialize our defined Services
        for (int i = 0; i < services.length; i++) {
            services[i].init();//初始化service
        }...
}
StandardService.initInternal(){...
engine.init();//需要注意的是此处engine初始化后并没有接着初始化子类host、context、wapper这几个是在star生命周期初始启动的
executor.init();
mapperListener.init();
connector.init();...//依次类推查看其他实现类的初始化
}

在connector->ProtocolHandler->AbstractProtocol调用init初始化的时候,还会去执行AbstractEndpoint的init方法,完成请求端口绑定、初始化NIO等操作

AbstractProtocol.init(){...
String endpointName = getName();
        endpoint.setName(endpointName.substring(1, endpointName.length()-1));
        endpoint.setDomain(domain);
        endpoint.init();...
}
AbstractEndpoint.init() throws Exception {
        if (bindOnInit) {
            bind(){
}
NioEndpoint.bind(){
        if (!getUseInheritedChannel()) {
            serverSock = ServerSocketChannel.open();
            socketProperties.setProperties(serverSock.socket());
            InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));
            serverSock.socket().bind(addr,getAcceptCount());

Engine初始化StandardEngine在init阶段,需要获取Realm(域)是用于对单个用户进行身份验证的底层安全领域的只读外观,并标识与这些用户相关联的安全角色。域可以在任何容器级别上附加,但是通常只附加到Context,或者更高级别的容器。

原文地址:https://www.cnblogs.com/leifonlyone/p/12746385.html