美文网首页
六 tomcat启动源码分析(三)--http请求nio处理

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

作者: 爱编程的凯哥 | 来源:发表于2019-02-22 06:17 被阅读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处理

    相关文章

      网友评论

          本文标题:六 tomcat启动源码分析(三)--http请求nio处理

          本文链接:https://www.haomeiwen.com/subject/msnieqtx.html