六 tomcat启动源码分析(三)--http请求nio处理

2019-02-22  本文已影响53人  爱编程的凯哥

目标

分析tomcat处理一次http请求的nio过程模式,因为bio比较常见,网上好多分析资料可参考。

分析

上一节,我们已经分析了tomcat端启动流程,最后Connector中启动Endpoint进行端口监听,然后在mapperListener中存了相关各容器的映射关系,最后pipeline作为容器的调用管道链。按照这个流程,我们分析调用源码,进行debug,看下最终我的nio调用时序图:


时序图

简单语言描述下:

  1. 请求进入NioEndPoint的内部Acceptor接收请求,根据nio的异步处理机制,socket会作为一个pollerEvent事件存入队列,poller会轮询通过select进行选择可读事件
 protected class Acceptor implements Runnable {
        /**
         * The background thread that listens for incoming TCP/IP connections and
         * hands them off to an appropriate processor.
         */
        @Override
        public void run() {

            int errorDelay = 0;

            // Loop until we receive a shutdown command
            while (running) {
                   。。。。。。。。
                        Thread.sleep(1000);
                。。。。。。。
                        //每次用完一个新建
                        SocketChannel socket = null;
                        socket = serverSock.accept();
                   ..............
                    //此处去注册任务了,然后关闭此socket
                        if (!setSocketOptions(socket)) {
                            try {
                                socket.socket().close();
                                socket.close();
                  。。。。。。。。。。。。。。
        }//run
    }

对应的setSocketOptions方法

   protected boolean setSocketOptions(SocketChannel socket) {
            //获取NioChannel,没有空闲的则新建一个
            NioChannel channel = nioChannels.poll();
            。。。。。。。。。。           
          //注册poller事件任务
            getPoller0().register(channel);
          。。。。。。。
            return true;
    }
  1. selector找到待处理事件后开启异步线程调用SocketProcessor进行处理,SocketProcessor找到其对应的协议处理类封装request,最后通过CoyoteAdapter进行适配处理
    看下Poller中监听run代码
 public void run() {
            // Loop until we receive a shutdown command
            while (running) {
                try {
                    // Loop if endpoint is paused
                    while (paused && (!close) ) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            // Ignore
                        }
                    }
                    //判断是否有待处理任务
                    boolean hasEvents = events();
                    //通过nio的selector选择可读事件,进行处理
                    Iterator<SelectionKey> iterator =
                        keyCount > 0 ? selector.selectedKeys().iterator() : null;
                    // Walk through the collection of ready keys and dispatch
                    // any active event.
                    while (iterator != null && iterator.hasNext()) {
                       
                            //处理此key
                            processKey(sk, attachment);
                   
        }

然后进入processKey处理数据,此方法最终调用processSocket(channel, null, true)方法

    public boolean processSocket(NioChannel socket, SocketStatus status, boolean dispatch) {
        try {
            KeyAttachment attachment = (KeyAttachment)socket.getAttachment(false);
            attachment.setCometNotify(false); //will get reset upon next reg
            SocketProcessor sc = processorCache.poll();
            if ( sc == null ) sc = new SocketProcessor(socket,status);
            else sc.reset(socket,status);
            //获取SocketProcessor处理器,如果配置了getExecutor则在异步线程中进行处理,否则直接处理
            if ( dispatch && getExecutor()!=null ) getExecutor().execute(sc);
            else sc.run();
        } catch (RejectedExecutionException rx) {
            log.warn("Socket processing request was rejected for:"+socket,rx);
            return false;
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(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;
    }

在SocketProcessor的run方法中,核心部分我们看到
state = (status==null)?handler.process(socket):handler.event(socket,status);
这个handler为Http11NioProtocol,根据我们配置的协议形成,看下其process方法,最终找到其对应方法SocketState state = processor.process(socket); ,此时processor为协议对应的处理器Http11NioProcessor,简化此方法

 public SocketState process(NioChannel socket)
        throws IOException {
        RequestInfo rp = request.getRequestProcessor();
                。。。。。。。。               
                  //处理request数据,session,cookie等信息都在此时进行处理
                    prepareRequest();
                    //适配找到对应容器业务
                    adapter.service(request, response);
                。。。。。。
        }

    }
  1. CoyoteAdapter首先会解析request,最后调用pipeline调用链进入container
   public void service(org.apache.coyote.Request req, 
                        org.apache.coyote.Response res)
        throws Exception {

        Request request = (Request) req.getNote(ADAPTER_NOTES);
        Response response = (Response) res.getNote(ADAPTER_NOTES);
        //处理request和response信息
          .............         
      //此处会调用pipeline逐级调用进入engine、host、context、wrapper        connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);

              ...............
              //成功后,返回信息
                response.finishResponse();
                if (postParseSuccess) {
                    // Log only if processing was invoked.
                    // If postParseRequest() failed, it has already logged it.
                    ((Context) request.getMappingData().context).logAccess(
                            request, response,
                            System.currentTimeMillis() - req.getStartTime(),
                            false);
                }
                req.action(ActionCode.POST_REQUEST , null);
            }

     。。。。。。。。。
        }

    }
  1. 在pipeline层层传递下最后进入StandardWrapperValve找到最终的servlet,匹配path对应的filter,包装filer链,调用filter的doFilter方法,最后进入servlet执行业务
    public final void invoke(Request request, Response response)
        throws IOException, ServletException {

       。。。。。
        //找到对应的servlet
        StandardWrapper wrapper = (StandardWrapper) getContainer();
        Servlet servlet = null;
        Context context = (Context) wrapper.getParent();
        
        。。。。。。。。。
        //形成过滤器调用链
        ApplicationFilterFactory factory =
            ApplicationFilterFactory.getInstance();
        ApplicationFilterChain filterChain =
            factory.createFilterChain(request, wrapper, servlet);
        
       。。。。。。。

    }

  1. servlet执行完后,CoyoteAdapter会调用finishResponse方法关闭输出流,返回客户端。

知识点:

  1. tomcat中nio的实现,加深对nio概念理解

  2. 适配器模式进行接口适配

3.pipeline和filter等责任链模式的使用

参考资料:https://blog.csdn.net/sunyunjie361/article/details/60126398

目录: tomcat 源码学习系列
上一篇:   tomcat启动源码分析(二)--入口代码calatina启动介绍
下一篇:  tomcat启动源码分析(三)--http请求nio处理

上一篇 下一篇

猜你喜欢

热点阅读