tomcat03-架构与源码分析

tomcat03-架构与源码分析,第1张

核心技术组件
(1)web容器 
(2)servlet容器 

web容器

Connector.initInternal()->
protocolHandler.init()->
AbstractProtocol.init()->
endpoint.init()->
bind()->
Apr,JIo,NIO,NIO2->
JIo即Socket实现方式

servlet容器

web项目—>Context标签—>Context.class—>StandardContext—>loadOnStartup()
证明Wrapper就是Servlet :
加载:ContextConfifig.webConfifig()—>getContextWebXmlSource()—>Constants.ApplicationWebXml
解析:ContextConfifig.webConfifig()—>confifigureContext(webXml)—>context.createWrapper()

架构图

Context表示标签能够表示Web项目,那按照server.xml文件来看的话,不就能够把tomcat架构图画出来
了吗?或者按照之前的简略版推导出来。
Context,Host,Engine,Service等

再次回到conf/web.xml文件,发现有一个Connector标签,而且还可以配置port端口,我们能够联想到监听端口,
按照配置文件到源码类的经验,源码中一定会有这样一个Connector类用于端口的监听。

conclusion :架构图<—>server.xml<—>源码 三者有一一对应的关系

tomcat架构设计 各个技术组件含义

官网给的含义说明
https://tomcat.apache.org/tomcat-9.0-doc/architecture/overview.html

Server
In the Tomcat world, a Server represents the whole container. Tomcat provides a default implementation of the Server interface which is rarely customized by users.
Service
A Service is an intermediate component which lives inside a Server and ties one or more Connectors to exactly one Engine. The Service element is rarely customized by users, as the default implementation is simple and sufficient: Service interface.
Engine
An Engine represents request processing pipeline for a specific Service. As a Service may have multiple Connectors, the Engine receives and processes all requests from these connectors, handing the response back to the appropriate connector for transmission to the client. The Engine interface may be implemented to supply custom Engines, though this is uncommon.
Note that the Engine may be used for Tomcat server clustering via the jvmRoute parameter. Read the Clustering documentation for more information.
Host
A Host is an association of a network name, e.g. www.yourcompany.com, to the Tomcat server. An Engine may contain multiple hosts, and the Host element also supports network aliases such as yourcompany.com and abc.yourcompany.com. Users rarely create custom Hosts because the StandardHost implementation provides significant additional functionality.
Connector
A Connector handles communications with the client. There are multiple connectors available with Tomcat. These include the HTTP connector which is used for most HTTP traffic, especially when running Tomcat as a standalone server, and the AJP connector which implements the AJP protocol used when connecting Tomcat to a web server such as Apache HTTPD server. Creating a customized connector is a significant effort.
Context
A Context represents a web application. A Host may contain multiple contexts, each with a unique path. The Context interface may be implemented to create custom Contexts, but this is rarely the case because the StandardContext provides significant additional functionality.

连个核心组件
• Connector:主要负责处理Socket连接,以及Request与Response的转化 
• Container:包括Engine、Host、Context和Wrapper,主要负责内部的处理以及Servlet的管理 
Connector
设计思想 :高内聚、低耦合 
EndPoint:提供字节流给Processor 
Processor:提供Tomcat Request对象给Adapter 
Adapter:提供ServletRequest给容器

• EndPoint 

监听通信端口,是对传输层的抽象,用来实现 TCP/IP 协议的。
对应的抽象类为AbstractEndPoint,有很多实现类,比如NioEndPoint,JIoEndPoint等。在其中有两个组件,一个 是Acceptor,另外一个是SocketProcessor。
Acceptor用于监听Socket连接请求,SocketProcessor用于处理接收到的Socket请求。

•  Processor 

Processor是用于实现HTTP协议的,也就是说Processor是针对应用层协议的抽象。
Processor接受来自EndPoint的Socket,然后解析成Tomcat Request和Tomcat Response对象,最后通过Adapter 提交给容器。 对应的抽象类为AbstractProcessor,有很多实现类,比如AjpProcessor、Http11Processor等。

• Adpater 

ProtocolHandler接口负责解析请求并生成 Tomcat Request 类。
需要把这个 Request 对象转换成 ServletRequest。
Tomcat 引入CoyoteAdapter,这是适配器模式的经典运用,连接器调用 CoyoteAdapter 的 sevice 方法,传入的是
Tomcat Request 对象,CoyoteAdapter 负责将 Tomcat Request 转成 ServletRequest,再调用容器的 service 方法

•  优化图解 

Endpoint接收Socket连接,生成一个SocketProcessor任务提交到线程池去处理
SocketProcessor的run方法会调用Processor组件去解析应用层协议,Processor通过解析生成 tomcat Request对象后,会调 用Adapter的service方法,生成servlet request传给container处理

• Container 

通过Connector之后,我们已经能够获得对应的Servlet

Request Process Flow

流程:http://www.kaotop.com/file/tupian/20220505/request-process.png

启动流程

Bootstrap.java—>main()
–>bootstrap.init()[initClassLoaders()使用自定义类加载器,实例化Catalina]
–>daemon.load(args) Catalina执行load方法
加载server–>(StandardServer.initInternal())
加载service(StandardService initInternal() )
加载engine(ContrainerBase initInternal())–(层层加载,Host,Context)
加载executor,StandardThreadExecutor线程池
加载监听
加载Connector,中的initInternal(),(加载web容器nio,serversocker)
–>daemon.start();启动各个架构组件,逻辑同initInternal方法
使用工厂、模板、适配器、观察者模式

• Catalina.java
 /**
     * Start a new server instance.
     */
    public void load() {

        if (loaded) {
            return;
        }
        loaded = true;

        long t1 = System.nanoTime();

        initDirs();

        // Before digester - it may be needed
        initNaming();

        // Parse main server.xml  //解析server.xml文件
        parseServerXml(true);
        Server s = getServer(); //获取server  StandardServer.java文件
        if (s == null) {
            return;
        }

        getServer().setCatalina(this);
        getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
        getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());

        // Stream redirection
        initStreams();

        // Start the new server   ----StandardServer
        try {
            getServer().init();
        } catch (LifecycleException e) {
            if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
                throw new java.lang.Error(e);
            } else {
                log.error(sm.getString("catalina.initError"), e);
            }
        }

        if(log.isInfoEnabled()) {
            log.info(sm.getString("catalina.init", Long.toString(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t1))));
        }
    }


• StandardServer.java
 /**
     * Invoke a pre-startup initialization. This is used to allow connectors
     * to bind to restricted ports under Unix operating environments.
     */
    @Override
    protected void initInternal() throws LifecycleException {

        super.initInternal();

        // Initialize utility executor
        reconfigureUtilityExecutor(getUtilityThreadsInternal(utilityThreads));
        register(utilityExecutor, "type=UtilityExecutor");

        // Register global String cache
        // Note although the cache is global, if there are multiple Servers
        // present in the JVM (may happen when embedding) then the same cache
        // will be registered under multiple names
        onameStringCache = register(new StringCache(), "type=StringCache");

        // Register the MBeanFactory
        MBeanFactory factory = new MBeanFactory();
        factory.setContainer(this);
        onameMBeanFactory = register(factory, "type=MBeanFactory");

        // Register the naming resources
        globalNamingResources.init();

        // Populate the extension validator with JARs from common and shared
        // class loaders
        if (getCatalina() != null) {
            ClassLoader cl = getCatalina().getParentClassLoader();
            // Walk the class loader hierarchy. Stop at the system class loader.
            // This will add the shared (if present) and common class loaders
            while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
                if (cl instanceof URLClassLoader) {
                    URL[] urls = ((URLClassLoader) cl).getURLs();
                    for (URL url : urls) {
                        if (url.getProtocol().equals("file")) {
                            try {
                                File f = new File (url.toURI());
                                if (f.isFile() &&
                                        f.getName().endsWith(".jar")) {
                                    ExtensionValidator.addSystemResource(f);
                                }
                            } catch (URISyntaxException | IOException e) {
                                // Ignore
                            }
                        }
                    }
                }
                cl = cl.getParent();
            }
        }
        // Initialize our defined Services  ---初始化service
        for (Service service : services) {
            service.init();
        }
    }

• StandardSevice.java
/**
     * Invoke a pre-startup initialization. This is used to allow connectors
     * to bind to restricted ports under Unix operating environments.
     */
    @Override
    protected void initInternal() throws LifecycleException {

        super.initInternal();
	//初始化Container,先找StandardEngine.java--->initInternal找到父类的ContrainerBase.java
        if (engine != null) {
            engine.init();
        }

        // Initialize any Executors  //线程池初始化
        for (Executor executor : findExecutors()) {
            if (executor instanceof JmxEnabled) {
                ((JmxEnabled) executor).setDomain(getDomain());
            }
            executor.init();
        }

        // Initialize mapper listener 初始化监听
        mapperListener.init();

        // Initialize our defined Connectors 初始话web容器connector
        synchronized (connectorsLock) {
            for (Connector connector : connectors) {
                connector.init();
            }
        }
    }

自定义类加载器

WebAppClassLoader,打破了双亲委派模型:先自己尝试去加载这个类,找不到再委托给父类加载器。 通过复写fifindClass和loadClass实现。

Session管理

Context中的Manager对象,查看Manager中的方法,可以发现有跟Session相关的。
Session 的核心原理是通过 Filter 拦截 Servlet 请求,将标准的 ServletRequest 包装一下,换成 Spring 的 Request 对象,这样当我们调用 Request 对象的 getSession 方法时,Spring 在背后为我们创建和管理 Session。

Tomcat源码解读 BootStrap

BootStrap是Tomcat的入口类
bootstrap.init() 和 创建Catalina

Catalina

解析server.xml文件
创建Server组件,并且调用其init和start方法

Lifecycle

用于管理各个组件的生命周期
init、start、stop、destroy
LifecycleBase实现了Lifecycle,利用的是模板设计模式

Server

管理Service组件,并且调用其init和start方法

Service

管理连接器和Engine

tomcat initInternal流程
![在这里插入图片描述](http://www.kaotop.com/file/tupian/20220505/5471a8954fc54f6e9908a13092d05d3a.png)

tomcat start流程

ContainerBase的类关系图

![在这里插入图片描述](http://www.kaotop.com/file/tupian/20220505/473558530350428fafd3777384f0fcef.png)

关注到上述图解中的ContainerBase.startInternal()方法

// Start our child containers, if any 
Container children[] = findChildren(); 
List> results = new ArrayList<>(); 
for (int i = 0; i < children.length; i++) { 
	// 这句代码就是会调用ContainerBase下的一个个子容器的call方法 
	results.add(startStopExecutor.submit(new StartChild(children[i]))); 
} 

查看new StartChild要执行的call方法

private static class StartChild implements Callable { 
	private Container child; 
	public StartChild(Container child) { 
		this.child = child; 
	}
	@Override 
	public Void call() throws LifecycleException { 
		child.start(); 
	return null; 
	} 
} 

StandardHost将一个个web项目部署起来

// Deploy XML descriptors from configBase 
deployDescriptors(configBase, configBase.list()); 
// Deploy WARs 
deployWARs(appBase, filteredAppPaths); 
// Deploy expanded folders 
deployDirectories(appBase, filteredAppPaths); 

StandardContext.startInternal()解析web.xml和添加wrapper

ContextConfig.webConfig()的step9解析到servlets包装成wrapper对象
StandardContext.startInternal()->最终会调用 if (!loadOnStartup(findChildren()))
https://tomcat.apache.org/tomcat-9.0-doc/architecture/startup/serverStartup.pdf

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/794114.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-06
下一篇 2022-05-06

发表评论

登录后才能评论

评论列表(0条)

保存