Toncat(二)基础类介绍

一、Server

  • service组件的维护:Server 组件的具体实现类是 StandardServer。Server在内部维护了若干 Service 组件,它是以数组来保存的。还管理Service的生命周期(启、停方法)
    Server类图
  • 实现Tomcat的关闭:具体实现在StandardServer的await方法中,在await方法中会启动一个Socket来监听8005(停止端口),并在一个死循环里接收Socket上的连接请求,如果有新的连接到来就建立连接,然后从Socket中读取数据,如果读到的数据是停止命令“SHUTDOWN”,就退出循环,进入stop流程。
public void await() {
    if (getPortWithOffset() == -1) {
        try {
            awaitThread = Thread.currentThread();
            while(!stopAwait) {
                try {
                    Thread.sleep( 10000 );
                }
            }
        } finally {
            awaitThread = null;
        }
        return;
    }
    try {
        // 创建socket,默认端口8005
        awaitSocket = new ServerSocket(getPortWithOffset(), 1,                InetAddress.getByName(address));
    }
    try {
        awaitThread = Thread.currentThread();
        // 死循环,等待停止命令
        while (!stopAwait) {
            ServerSocket serverSocket = awaitSocket;
            if (serverSocket == null) {
                break;
            }
            // 等待连接,从连接中获取请求命令
            Socket socket = null;
            StringBuilder command = new StringBuilder();
            try {
                InputStream stream;
                long acceptStartTime = System.currentTimeMillis();
                try {
                    socket = serverSocket.accept();                  socket.setSoTimeout(10 * 1000);  // Ten seconds
                    stream = socket.getInputStream();
                } catch (SocketTimeoutException ste) {
                    // log ……
                    continue;
                } catch (AccessControlException ace) {
                    // log ……
                    continue;
                } catch (IOException e) {
                    if (stopAwait) {
                        // Wait was aborted with socket.close()
                        break;
                    }
                    // log ……
                    break;
                }
                // Read a set of characters from the socket
                int expected = 1024; // Cut off to avoid DoS attack
                while (expected < shutdown.length()) {
                    if (random == null)
                        random = new Random();
                    expected += (random.nextInt() % 1024);
                }
                while (expected > 0) {
                    int ch = -1;
                    try {
                        ch = stream.read();
                    } catch (IOException e) {                        log.warn(sm.getString("standardServer.accept.readError"), e);
                        ch = -1;
                    }
                    // Control character or EOF (-1) terminates loop
                    if (ch < 32 || ch == 127) {
                        break;
                    }                  command.append((char) ch);
                    expected--;
                }
            } finally {
                // Close the socket now that we are done with it
                try {
                    if (socket != null) {                        socket.close();
                    }
                }
                // Ignore
            }
            // 如果连接是SHUTDOWN命令,结束循环
            boolean match = command.toString().equals(shutdown);
    } finally {
        ServerSocket serverSocket = awaitSocket;
        awaitThread = null;
        awaitSocket = null;
        // Close the server socket and return
        if (serverSocket != null) {
            try {               serverSocket.close();
            }
         // Ignore
        }
    }
}

二、Service

2.1 简介

  • Service需要负责管理Connector和Container的生命周期。具体实现StandardService
  • Service内部还有映射器Mapper及其监听器MapperListener:映射器及其监听器用于将用户请求的 URL 定位到一个 Servlet。它的工作原理是: Mapper组件里保存了Web应用的配置信息,其实就是容器组件与访问路径的映射关系,比如Host容器里配置的域名、Context容器里的Web应用路径,以及 Wrapper容器里Servlet映射的路径;MapperListener监听器用于支持热部署,当Web应用的部署发生变化时,Mapper中的映射信息也要跟着变化,MapperListener就是一个监听器,它监听容器的变化,并把信息更新到Mapper 中,这是典型的观察者模式。
    StandardService类图

2.2 Sercice组件的功能

  • Service组件最重要的功能对下层容器的管理,比如启动时,要负责启动Connector和Container组件。Service的启动方法在startIntel中,如下:
protected void startInternal() throws LifecycleException {
    if(log.isInfoEnabled())      log.info(sm.getString("standardService.start.name", this.name));
    // 1. 触发启动监听器
       setState(LifecycleState.STARTING); 
    // 2. 先启动Engine,Engine会启动它子容器
    if (engine != null) {
        synchronized (engine) {
            engine.start();
        }
    }
    synchronized (executors) {
        for (Executor executor: executors) {
            executor.start();
        }
    } 
    // 3. 再启动Mapper监听器
    mapperListener.start();
    // 4. 最后启动连接器,连接器会启动它子组件,比如Endpoint
    synchronized (connectorsLock) {
        for (Connector connector: connectors) {
            // If it has already failed, don't try and start it
            if (connector.getState() != LifecycleState.FAILED) {                connector.start();
            }
        }
    }
}

从启动方法可以看到,Service先启动了Engine组件,再启动Mapper监听器,最后才是启动连接器。这很好理解,因为内层组件启动好了才能对外提供服务,才能启动外层的连接器组件。而 Mapper 也依赖容器组件,容器组件启动好了才能监听它们的变化,因此Mapper和MapperListener在容器组件之后启动。组件停止的顺序跟启动顺序相反。

三、Connector连接器

Connector连接器类图
Connector连接器架构

3.1 简介

  • 处理 Socket 连接:将网络字节流转化为Request和Response对象。也就是说连接器对Servlet容器屏蔽了协议及I/O模型等的区别。
  • Tomcat设计者将上述功能及实现进一步细分为以下三部分:
    1. 网络通信—EndPoint
    2. 应用层协议解析—Processor处理器
    3. Tomcat Request/Response 与 ServletRequest/ServletResponse 的转化—Adaper适配器

3.2 ProtocolHandler

  • 处理网络连接和应用层协议,包含2个重要部件:EndPoint、Processor
  • 初始化在Connector构造函数中完成,默认为Http/1.1NIO类型,即HttpNioProtocol

3.2.1 网络通讯与应用层协议简介

  • 网络通信的I/O模型是变化的,可能是非阻塞 I/O、异步 I/O 或者 APR。
  • 应用层协议也是变化的,可能是 HTTP、HTTPS、AJP。浏览器端发送的请求信息也是变化的。

3.2.2 ProtocolHandler架构

ProtocolHandler

  1. Tomcat使用抽象基类AbstractProtocol实现ProtocolHandler接口,将一些协议通用功能进行抽取。
  2. 每一种应用协议对应自己的协议抽象基类,如AbstractHttp11Protocol
  3. 具体协议的实现扩展协议抽象基类,如Http11NioProtocol

3.2.3 Endpoint容器

Endpoint容器架构图

3.2.3.1 EndPoint简介
  • EndPoint是通信端点:即通信监听的接口,是具体的Socket接收和发送处理器,是对传输层的抽象,因此EndPoint是用来实现TCP/IP协议的。
  • EndPoint抽象实现类是AbstractEndpoint,而 具体子类是NioEndpoint和Nio2Endpoint,有两个重要的子组件:Acceptor和SocketProcessor。
3.2.3.2 EndPoint子组件
  • Acceptor:用于监听Socket连接请求。
  • SocketProcessor:用于处理接收到的 Socket请求,它实现Runnable接口,在run()方法里调用协议处理组件Processor 进行处理。为了提高处理能力,SocketProcessor被提交到线程池来执行。

3.2.4 Processor处理器

  • Processor:用来实现应用层协议的(HTTP协议、AJP协议等),负责接收来自EndPoint的Socket,读取字节流解析成Tomcat Request和Response对象,并通过Adapter将其提交到容器处理。
  • Processor抽象实现类 AbstractProcessor:对一些协议共有的属性进行封装,没有对方法进行实现。具体的实现有AJPProcessor、HTTP11Processor等,这些具体实现类实现了特定协议的解析方法和请求处理方式。

3.2.5 代码处理流程

上图的流程代码逻辑大致如下:

NioEndPoint.startIntel -> 
Poller -> run() 
-> processKey(SelectionKey sk, NioSocketWrapper attachment) 
-> processSocket(attachment, SocketEvent.OPEN_READ, true) 
-> createSocketProcessor(socketWrapper, event) 
-> SocketProcessor run 
-> getHandler().process 
-> processor.process(wrapper, status) 
-> service(socketWrapper) 
-> getAdapter().service(request, response)

3.3 Adapter适配器

Tomcat设计者的解决方案是引入CoyoteAdapter,这是适配器模式的经典运用,连接器调用CoyoteAdapter 的 Sevice方法,传入的是Tomcat Request对象,CoyoteAdapter负责将Tomcat Request转成ServletRequest,再调用Engine容器的pipline方法,实现对servlet的调用。

四 Container容器

  • 定义容器基本功能:子级child、父级Parent容器和Cluster集群、Pipeline管道、Threads线程池、Container容器、ContainerListener容器监听等CRUD功能
  • 抽象基类为ContainerBase
  • Toncat设计了4中容器,分别为Engine、Host、Context、Wrapper

4.1 ContainerBase简介

  • 抽象类中相关功能得到实现。
protected final HashMap<String,Container> children = new HashMap<>();
  • 使用HashMap存储保存子容器。
protected synchronized void startInternal(){
    Container children[]=findChildren();
    List<Future<Void>> results=new ArrayList<>();
    for(Container child:children){
    results.add(startStopExecutor.submit(new StartChild(child))); 
}
}
  • ChntainerBase实现了子容器的CRUD,以及子容器的启动和停止提供了默认实现,使用专门的线程池来启动子容器。

4.2 Engine容器

  • 作用:把请求转发给某一个Host子容器来处理
    继承了ContainerBase基类,具体实现类是StandardEngine。

4.2.1 StandardEngineValue

final class StandardEngineValve extends ValveBase {
    public StandardEngineValve() {
        super(true);
    } 
    @Override
    public final void invoke(Request request, Response response)
        throws IOException, ServletException {
        // 拿到请求中的host容器
        Host host = request.getHost();
        if (host == null) {
            // is defined. This is handled by the CoyoteAdapter.
            return;
        }
        if (request.isAsyncSupported()) {
            request.setAsyncSupported(host.getPipeline().isAsyncSupported());
        }
        // 调用host容器pipline中第一个valve,将请求提交给host处理
                host.getPipeline().getFirst().invoke(request, response);
    }
}

4.4 Host容器

  • Host是跟虚拟域名绑定的,同时负责维护其子容器Context的生命周期。Host容器在Tomcat中的实现类是StandardHost,它也继承了ContainerBase基类同时实现了Host接口。

4.4 Context容器

  • Context对应一个web应用,是用于存储管理Servlet的容器,在Tomcat中的默认实现是StandardContext。
  • StandardContext也继承了ContainerBase类,子容器也是通过ContainerBase类的成员变量children维护的,但是StandardContext中重写了startInternal方法,在该方法中,完成WebAppClassLoader的初始化,子容器Wrapper的初始化及启动,web.xml配置文件解析,Context监听器维护与触发等。

4.4.1 基础属性

比如我们在配置文件中配置的Context的path属性,用于指定Host下(域名下)web应用的访问路径:

<Host appBase="webapps" autoDeploy="true" name="www.lidol.top" unpackWARs="true">
    <!--访问路径:www.lidol.top/demo1-->
    <Context docBase="/Users/zhuoli/Documents/demo/demo1" path="/demo1" reloadable="true"/> 
    <!--访问路径:www.lidol.top/demo2-->
    <Context docBase="/Users/zhuoli/Documents/demo/demo2" path="/demo2" reloadable="true"/> 
</Host>  
  • 在StandardContext中,我们发现存在这样两个成员变量:
/**
 * The document root for this web application.
 */
private String docBase = null;
private String path = null;
  • Context是所有的Servlet的父容器,我们知道Servlet中一个重要的组件就是ServletContext(Servlet上下文),该组件就是在StandardContext中初始化的:
/**
 * The ServletContext implementation associated with this Context.
 */
protected ApplicationContext context = null;
@Override
public ServletContext getServletContext() { 
    if (context == null) {
        context = new ApplicationContext(this);
        if (altDDName != null)            context.setAttribute(Globals.ALT_DD_ATTR,altDDName);
    }
    return (context.getFacade()); 
}

4.4.2 Context和Java Web三大组件

  • Servlet其实就是Context的子容器Wrapper,用继承的children成员变量来维护。
  • Filter是一种Servlet的扩展机制,在web.xml中配置,在Context容器中进行管理的:
private Map<String, FilterDef> filterDefs = new HashMap<>();

需要注意的是StandardContext中只是将我们配置的Filter解析成FilterDef,并不是每个请求都需要将所有的Filter走一遍。在web.xml中,我们通过来配置servlet生效的Filter集合,Tomcat容器会为每个请求生成一个FilterChain,用于表示该请求相关联的Filter,在每个Filter执行结束后,才会调用Servlet的service方法。

  • Listener跟Filter一样,也是一种扩展机制,你可以监听容器内部发生的事件,主要有两类事件:

    • 生命状态的变化,比如Context容器启动和停止、Session的创建和销毁
    • 属性的变化,比如Context容器某个属性值变了、Session的某个属性值变了以及新的请求来了等,在web.xml配置了监听器,在监听器里实现我们的业务逻辑。对于Tomcat来说,它需要读取配置文件,拿到监听器类的名字,实例化这些类,并且在合适的时机调用这些监听器的方法。
  • Context容器将两类事件分开来管理,分别用不同的集合来存放不同类型事件的监听器:

// 监听属性值变化的监听器
private List<Object> applicationEventListenersList = new CopyOnWriteArrayList<>(); 
// 监听生命事件的监听器
private Object applicationLifecycleListenersObjects[] = new Object[0];

剩下的事情就是触发监听器了,比如在Context容器的启动方法里,就触发了所有的ServletContextListener:

for (int i = 0; i < instances.length; i++) {
    if (!(instances[i] instanceof ServletContextListener))
        continue;
    ServletContextListener listener =
        (ServletContextListener) instances[i];
    try {        fireContainerEvent("beforeContextInitialized", listener);
        if (noPluggabilityListeners.contains(listener)) {            listener.contextInitialized(tldEvent);
        } else {           listener.contextInitialized(event);
        }        fireContainerEvent("afterContextInitialized", listener);
    } catch (Throwable t) {        ExceptionUtils.handleThrowable(t);        fireContainerEvent("afterContextInitialized", listener);
        getLogger().error            (sm.getString("standardContext.listenerStart",                          instances[i].getClass().getName()), t);
        ok = false;
    }
}

4.5 Wrapper容器

  • 用来管理Servlet,每一个Servlet都对应一个Wrapper,在Tomcat中的实现是StandardWrapper。所以StandardWrapper中有一个比较重要的成员变量:
protected volatile Servlet instance = null;
  • StandardWrapper:提供了allocate()方法,用于实例化并初始化Servlet。而allocate方法最终调用了StandardWrapper中的loadServlet()方法来实例化并初始化Servlet。
public synchronized Servlet loadServlet() throws ServletException {
    // Nothing to do if we already have an instance or an instance pool
    if (!singleThreadModel && (instance != null))
        return instance;
    // 1. 创建Servlet实例
    servlet = (Servlet) instanceManager.newInstance(servletClass);
    // 2. 调用Servlet的init方法,初始化Servlet
    initServlet(servlet); 
    return servlet; 
}
  • StandardWrapperValve:完成Servlet的初始化和实例化。如下:
public final void invoke(Request request, Response response)
    throws IOException, ServletException {
    // 1. 实例化Servlet
    servlet = wrapper.allocate();
    // 2. 为当前请求创建一个FilterChain
    ApplicationFilterChain filterChain =         ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
    // 3. 调用这个FilterChain的doFilter方法,FilterChain中最后一个Filter执行结束后,会调用Servlet的service方法   filterChain.doFilter(request.getRequest(), response.getResponse());
}

到这里,我们也可以解答一个疑问,为什么Servlet会在第一次被调用时实例化。因为在上述loadServlet方法中,只有Wrapper容器中的Servlet实例为null时(第一次调用时),才会创建Servlet实例。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weixin_42242792

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值