【tomcat】相关阶段流程以及机制

本文是在阅读博客以后整理的一些知识笔记,方便自己再次回顾,也提供给需要的伙伴。
 

  • tomcat 架构图

东方鲤鱼
东方鲤鱼-架构图

      LiftCycle接口是一个公用的顶层接口,定义了组件生命周期的一些方法,用于启动、停止Catalina组件。组件的生命周期包括:init、start、stop、destory,以及定义了各种事件的常量,操作LifecycleListener的API,是一个典型的观察者模式。各大组件均实现了Lifecycle接口:

LifecycleBase:它实现了Lifecycle的init、start、stop等主要逻辑,向注册在LifecycleBase内部的LifecycleListener发出对应的事件,并预留了initInternal、startInternal、stopInternal等模板方法,便于子类完成自己的逻辑
- MBeanRegistration:JmxEnabled的父类,jmx框架提供的注册MBean的接口,引入此接口是为了便于使用JMX提供的管理
- LifecycleMbeanBase:JmxEnable的子类,通过重写initInternal、destroyInternal方法,统一向jmx中注册、取消注册当前实例,方便利用jmx对实例对象进行管理,代码上特别强调要求子类先行调用super.initInternal
- ContainerBase、standardServer、StandardService、WebappLoader、Connector、StandardContext、StandardEngine、StandardHost、StandardWrapper等容器都继承了LifecycleMBeanBase,因此这些容器都具有了同样的生命周期并可以通过JMX进行管理。

依赖关系图

这里写图片描述

具体生命周期的处理如下:

  •  初始化流程

东方鲤鱼-初始化图

  • 启动流程

启动流程

  •  处理请求流程

    http 请求是基于socket 技术实现,在tomcat中请求由connetor容器处理,其中最核心的逻辑在Jioendpoint类中; 在启动流程阶段已经完成对连接处理器的初始化工作,此处详细描述一下

public void init() throws Exception {
    ............
// Initialize thread count defaults for acceptor
        if (acceptorThreadCount == 0) {
            acceptorThreadCount = 1;
        }
    ..............
}
 public void start() throws Exception {
    .............
       // Start acceptor threads
            for (int i = 0; i < acceptorThreadCount; i++) {
                Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);
                acceptorThread.setPriority(threadPriority);
                acceptorThread.setDaemon(daemon);
                acceptorThread.start();
            }
    ..............
}

适配层类实现了runable 接口

/**
    * Server socket acceptor thread.
    */
   protected class Acceptor implements Runnable {
       /**
        * 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
           while (running) {
               // Loop if endpoint is paused
               while (paused) {
                   try {
                       Thread.sleep(1000);
                   } catch (InterruptedException e) {
                       // Ignore
                   }
               }
               // Accept the next incoming connection from the server socket
               try {
                   Socket socket = serverSocketFactory.acceptSocket(serverSocket);
                   serverSocketFactory.initSocket(socket);
                   // Hand this socket off to an appropriate processor
                   if (!processSocket(socket)) {
                       // Close socket right away
                       try {
                           socket.close();
                       } catch (IOException e) {
                           // Ignore
                       }
                   }
               }catch ( IOException x ) {
                   if ( running ) log.error(sm.getString("endpoint.accept.fail"), x);
               } catch (Throwable t) {
                   log.error(sm.getString("endpoint.accept.fail"), t);
               }
               // The processor will recycle itself when it finishes
           }
       }
   }

   大体流程是serverSocketFactory.acceptSocket 方法中通过工厂类得到socket 对象,然后把socket对象交给processSocket 方法处理,每当接收线程接收到客户端链接时,就从线程池中取出一个工作线程(Work)来处理socket通信;

    /**
     * processSocket 
     */
    protected boolean processSocket(Socket socket) {
        try {
            if (executor == null) {
                getWorkerThread().assign(socket);
            } else {
                executor.execute(new SocketProcessor(socket));
            }
        } catch (Throwable t) {
            // This means we got an OOM or similar creating a thread, or that
            // the pool and its queue are full
            log.error(sm.getString("endpoint.process.fail"), t);
            return false;
        }
        return true;
    }
/**
     * 当没有空闲的工作线程时,当前线程阻塞,直到线程池//workers.size()>0,
     */
    protected Worker getWorkerThread() {
        // Allocate a new worker thread
        synchronized (workers) {
            Worker workerThread;
            while ((workerThread = createWorkerThread()) == null) {
                try {
                    workers.wait();
                } catch (InterruptedException e) {
                    // Ignore
                }
            }
            return workerThread;
        }
    }
 protected Worker createWorkerThread() {
        synchronized (workers) {
            if (workers.size() > 0) {
                curThreadsBusy++;
                return workers.pop();
            }
            if ((maxThreads > 0) && (curThreads < maxThreads)) {
                curThreadsBusy++;
                if (curThreadsBusy == maxThreads) {
                    log.info(sm.getString("endpoint.info.maxThreads",
                            Integer.toString(maxThreads), address,
                            Integer.toString(port)));
                }
                return (newWorkerThread());
            } else {
            //从此处代码可以看出,如何我们修改server.xml文件connector的maxThreads属性为负数,则线程数可能会一直增加。
                if (maxThreads < 0) {
                    curThreadsBusy++;
                    return (newWorkerThread());
                } else {
                    return (null);
                }
            }
        }
    }


这段代码中涉及worksocket 的创建,以及http协议的解析,最终会把资源封装到httpsevletRequest 对象中,主要是通过CoyoteAdapter 类来实现的把资源请求交给containor容器处理的,CoyoteAdapter类的service方法

/**
     * Service method.
     */
    public void service(org.apache.coyote.Request req,
    	                org.apache.coyote.Response res)
        throws Exception {
 
        Request request = (Request) req.getNote(ADAPTER_NOTES);//判断是否绑定了HttpServletRequest
        Response response = (Response) res.getNote(ADAPTER_NOTES);//判断是否绑定了HttpServletResponse
 
        if (request == null) {
 
            // Create objects
            request = connector.createRequest();//连接器创建一个http请求对象request
            request.setCoyoteRequest(req);//设置该http请求对象的真正req对象
            response = connector.createResponse();
            response.setCoyoteResponse(res);
 
            // Link objects
            request.setResponse(response);//将请求对象与响应对象绑定相关联
            response.setRequest(request);
 
            // Set as notes
            req.setNote(ADAPTER_NOTES, request);
            res.setNote(ADAPTER_NOTES, response);
 
            // Set query string encoding
            req.getParameters().setQueryStringEncoding(connector.getURIEncoding());
 
        }
 
        if (connector.getXpoweredBy()) {
            response.addHeader("X-Powered-By", POWERED_BY);
        }
 
        boolean comet = false;
        boolean async = false;
        
        try {
 
            // Parse and set Catalina and configuration specific 
            // request parameters
            req.getRequestProcessor().setWorkerThreadName(Thread.currentThread().getName());
            if (postParseRequest(req, request, res, response)) {//将当前http请求对象request与相关的host,Context、Wrapper对象引用相绑定。
                //check valves if we support async
                request.setAsyncSupported(connector.getService().getContainer().getPipeline().isAsyncSupported());
                // Calling the container
                connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);//将真正的请求处理交给Engine的Pipeline去处理
 
                if (request.isComet()) {
                    if (!response.isClosed() && !response.isError()) {
                        if (request.getAvailable() || (request.getContentLength() > 0 && (!request.isParametersParsed()))) {
                            // Invoke a read event right away if there are available bytes
                            if (event(req, res, SocketStatus.OPEN)) {
                                comet = true;
                                res.action(ActionCode.ACTION_COMET_BEGIN, null);
                            }
                        } else {
                            comet = true;
                            res.action(ActionCode.ACTION_COMET_BEGIN, null);
                        }
                    } else {
                        // Clear the filter chain, as otherwise it will not be reset elsewhere
                        // since this is a Comet request
                        request.setFilterChain(null);
                    }
                }
 
            }
            AsyncContextImpl asyncConImpl = (AsyncContextImpl)request.getAsyncContext();
            if (asyncConImpl!=null && asyncConImpl.getState()==AsyncContextImpl.AsyncState.STARTED) {
                res.action(ActionCode.ACTION_ASYNC_START, request.getAsyncContext());
                async = true;
            } else if (request.isAsyncDispatching()) {
                asyncDispatch(req, res, SocketStatus.OPEN);
                if (request.isAsyncStarted()) {
                    async = true;
                    res.action(ActionCode.ACTION_ASYNC_START, request.getAsyncContext());
                }
            } else if (!comet) {
                response.finishResponse();
                req.action(ActionCode.ACTION_POST_REQUEST , null);
            }
 
        } catch (IOException e) {
            // Ignore
        } catch (Throwable t) {
            log.error(sm.getString("coyoteAdapter.service"), t);
        } finally {
            req.getRequestProcessor().setWorkerThreadName(null);
            // Recycle the wrapper request and response
            if (!comet && !async) {
                request.recycle();
                response.recycle();
            } else {
                // Clear converters so that the minimum amount of memory 
                // is used by this processor
                request.clearEncoders();
                response.clearEncoders();
            }
        }
 
    }
  1. if (postParseRequest(req, request, res, response)) {//将当前http请求对象request与相关的host,Context、Wrapper对象引用相绑定。

  2. 2.connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);//将真正的请求处理交给Engine的Pipeline去处理:     

 connector.getService() :方法拿到的是connector相关联的service容器对象默认的都是StandardService,

getContainer():方法拿到的是StandardEngine对象。这些对象是在初始化阶段完成容器之间的关系绑定。StandardEngine对象调用getPipeline()方法,因为StandardEngine类继承了ContainerBase类所以getPipeline()方法执行的是父类的方法。         

小结:tomcat 中的请求经过连接器的处理转换以后最终是要通过通道的方式经过container host  context wapper 链式处理,返回也是同样

管道与阀门

Pipeline是管道组件,用于封装一组有序Valve,便于Valve顺序地传递或者处理请求;

Valve是阀门组件,穿插在Container容器中,可以把它理解成请求拦截器,在tomcat接收网络请求与触发Servlet之间执行;

ContainerBase中包含了一个Pipeline实例,默认实现StandardPipeline; 查看父类的代码org.apache.catalina.core.ContainerBase的getPipeline方法如下:

 getPipeline()返回的StandardPipeline对象。从这里可以看出StandardEngine,StandardHost,StandardContext,StandardWrapper标准容器类都继承了Containerbase类,每个容器对象都有一个管道对象,每个管道对象必然会有一个或多个的阀门。也就是说容器包含管道,管道包含阀门。

tomcat为我们提供了一系列的Valve阀门:
- AccessLogValve,记录请求日志,默认会开启
- RemoteAddrValve,可以做访问控制,比如限制IP黑白名单
- RemoteIpValve,主要用于处理 X-Forwarded-For 请求头,用来识别通过HTTP代理或负载均衡方式连接到Web服务器的客户端最原始的IP地址的HTTP请求头字段

前面我们分析了 StandardEngine 的启动逻辑,它会启动其子容器 StandardHost,接下来我们看下 StandardHost 的 start 逻辑。其实, StandardHost 重写的 startInternal 方法主要是为了查找报告错误的 Valve 阀门
 

  • 停止流程

org.apache.catalina.startup.Catalina类中stop方法

 public void stop() {

        try {
            // Remove the ShutdownHook first so that server.stop()
            // doesn't get invoked twice
            if (useShutdownHook) {
                Runtime.getRuntime().removeShutdownHook(shutdownHook);

                // If JULI is being used, re-enable JULI's shutdown to ensure
                // log messages are not lost
                LogManager logManager = LogManager.getLogManager();
                if (logManager instanceof ClassLoaderLogManager) {
                    ((ClassLoaderLogManager) logManager).setUseShutdownHook(
                            true);
                }
            }
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            // This will fail on JDK 1.2. Ignoring, as Tomcat can run
            // fine without the shutdown hook.
        }

        // Shut down the server
        try {
            Server s = getServer();
            LifecycleState state = s.getState();
            if (LifecycleState.STOPPING_PREP.compareTo(state) <= 0
                    && LifecycleState.DESTROYED.compareTo(state) >= 0) {
                // Nothing to do. stop() was already called
            } else {
                s.stop();
                s.destroy();
            }
        } catch (LifecycleException e) {
            log.error("Catalina.stop", e);
        }

    }

移除完关闭钩子后,守护线程
1.调用stop()方法,和启动过程类似,会先后调用StandardServer和StandardService类的stopInternal()方法、加锁暂停HTTP Connector和AJP Connector的连接、加锁停止StandardEngine、再加锁停止HTTP Connector和AJP Connector的连接、调用NamingResources的stopInternal()方法等。
2.调用destroy()方法,会先后调用StandardServer和StandardService类的destroyInternal()方法、加锁销毁Connector、加锁销毁StandardEngine、销毁NamingResources等。
 

  • 销毁流程

各组件的destroy()方法执行完后要调用父类org.apache.catalina.util.LifecycleMBeanBase中的destroyInternal(),这意味着生命周期的结束

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

东方鲤鱼

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

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

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

打赏作者

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

抵扣说明:

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

余额充值