Tomcat-Connector源码分析

《看透SpringMVC源码分析与实践》
引用 https://yq.aliyun.com/articles/20177

Connector用于接收请求并将请求封装成Request和Response来具体处理,最底层是使用Socket协议来进行连接的,在封装完成之后交给Container进行处理(即Servlet容器),Container处理完毕之后返回给Connector,最终Connector使用Socket将处理结果返回给客户端。这样整个请求就处理完成了。

Connector中具体是用ProtocolHandler来处理请求的,不同的ProtocolHandler代表不同的连接类型:
三种HTTP协议,客户端与Tomcat服务器直接连接。

  • Http11Protocol : BIO,支持http1.1协议
  • Http11NioProtocol : NIO,支持http1.1协议
  • Http11AprProtocol : ARP(Apache portable runtime),支持http1.1协议 Tomcat 7.0.30版本开始,默认使用此方式。

以及三种带AJP13是定向包协议.

  • AjpProtocol : BIO,支持AJP协议
  • AjpNioProtocol,NIO,支持AJP协议
  • AjpAprProtocol,Apr ,支持AJP协议

什么是AJP
AJPv13协议是面向数据包的。WEB服务器和Servlet容器通过TCP连接来交互;为了节省SOCKET创建的昂贵代价,WEB服务器会尝试维护一个永久TCP连接(长连接)到servlet容器,并且在多个请求和响应周期过程会重用连接。

  • Apache是世界排名第一的Web服务器,它支持处理静态页面,Apache可以支持PHP。
  • Tomcat支持动态页面(servlet, JSP),对静态页面的处理不太理想。
  • Tomcat能够承担性能要求较低的WEB应用。
  • 通常会使用Apache + Tomcat的配置下,Apache处理静态页面,对于JSP则转发交给Tomcat来处理。



    AJP就是Apache和Tomcat之间的通讯基本有三种方式之一,另外常见的还有Mod_Jk、HTTP_Proxy协议.
    这里写图片描述
    引用:http://blog.chinaunix.net/uid-20662363-id-3012760.html

ProtocolHandler有三个非常重要的组件:Endpoint,Processor和Adapter.

  • Endpoint: 用于处理底层Socket的网络协议。 Endpoint的抽象实现类AbstractEndpoint中定义了Acceptor,AsyncTimeout两个内部类和一个接口Handler
    • Acceptor: 用于监听请求。
    • AsyncTimeout: 用于异步检查request超时。
    • Handler: 用于处理接收到的Socket,在内部调用了Processor
  • Processor: 用于将Endpoint接收到的Socket封装成Request
  • Adapter: 用于将封装好的Request交给Containner进行具体处理。

Connector处理请求过程如下图:
这里写图片描述

源码分析

实例化

Connector实例化
Connector是在Catalina的load方法中,执行createStartDigester()解析server.xml配置文件的配置信息所创建的。
server.xml-part

<Connector port="8080" protocol="HTTP/1.1" 
connectionTimeout="20000" redirectPort="8443" />

<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

java创建-流程

//org.apache.catalina.startup.Catalina.createStartDigester()
digester.addRule("Server/Service/Connector", new ConnectorCreateRule());

//org.apache.catalina.startup.ConnectorCreateRule.begin()                         
Connector con = new Connector(attributes.getValue("protocol"));

可见最终Connector的创建流程,执行到了Connector的构造函数中。

//org.apache.catalina.Connector
public Connector(String protocol) {
      // 将server.xml中配置的protocol “HTTP/1.1”,“AJP/1.3” 转换为上文中的6种protocol;  
      setProtocol(protocol);
      Class<?> clazz = Class.forName(protocolHandlerClassName);

      //调用具体protocol的无参构造函数,实例化。
      this.protocolHandler = (ProtocolHandler) clazz.newInstance();
}


public void setProtocol(String protocol) {
    //若支持apr默认为:xxxAprProtocol
     if (AprLifecycleListener.isAprAvailable()) {
         if ("HTTP/1.1".equals(protocol)) {
             setProtocolHandlerClassName
                 ("org.apache.coyote.http11.Http11AprProtocol");
         } else if ("AJP/1.3".equals(protocol)) {
             setProtocolHandlerClassName
                 ("org.apache.coyote.ajp.AjpAprProtocol");
         } else if (protocol != null) {
             setProtocolHandlerClassName(protocol);
         } else {
             setProtocolHandlerClassName
                 ("org.apache.coyote.http11.Http11AprProtocol");
         }
     } else {
      //否则默认为:Http11Protocol 或者 AjpProtocol
         if ("HTTP/1.1".equals(protocol)) {
             setProtocolHandlerClassName
                 ("org.apache.coyote.http11.Http11Protocol");
         } else if ("AJP/1.3".equals(protocol)) {
             setProtocolHandlerClassName
                 ("org.apache.coyote.ajp.AjpProtocol");
         } else if (protocol != null) {
             setProtocolHandlerClassName(protocol);
         }
     }

 }

在构造函数中,最主要的就是指定ProtocolHandler的类型,并通过执行newInstance方法,创建具体的ProtocolHandler实例。

ProtocolHandler实例化
ProtocolHandler类图,如下:
这里写图片描述
从上图可以看到,ProtocolHandler有一个抽象实现类AbstractProtocol,AbstractProtocol又有AbstractAjpProtocolAbstractHttp11Protocol抽象子类,以及本文开局提到的最终的六种ProtocolHandler子类。
本文以Http11NioProtocol为例,分析ProtocolHandler。

//org.apache.coyote.http11.Http11NioProtocol
public Http11NioProtocol() {
    // 创建NioEndpoint
     endpoint=new NioEndpoint();
     //创建Http11ConnectionHandler
     cHandler = new Http11ConnectionHandler(this);
    //指定NioEndpoint的handler,
     ((NioEndpoint) endpoint).setHandler(cHandler);
     setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
     setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
     setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
 }

构造函数中,最主要是NioEndpoint类型的Endpoint,Http11ConnectionHandler类型的Handler并将其设置到Endpoint中。

Endpoint
Endpoint用于处理具体的连接和传输数据,AbstractEndpoint是所有Endpoint的父类,类图如下:
这里写图片描述

其中NioEndpoint中处理请求的具体流程如下,
这里写图片描述
Poller和SocketProcessor是NioEndpoint的内部类,后续会讲到。


初始化

整个Connector的初始化过程流程见下图:

Connector初始化
根据之前的经验,Connector的生命周期方法是在Service中调用的,首先调用生命周期LifecycleBase.init(),然后再调用Connector.initInternal()。
这里写图片描述

//org.apache.catalina.Connector
protected void initInternal() throws LifecycleException {
     super.initInternal();

     //创建新的Adapter,并设置到protocolHandler,
     //Adapter的主要作用是通过自身的service方法调用Container管道中的invoke方法来处理强求
     adapter = new CoyoteAdapter(this);

    //将Adapter设置到protocolHandler中
     protocolHandler.setAdapter(adapter);

     //省略部分代码....

    //初始化protocolHandler
    protocolHandler.init();

     //初始化mapperListener
     mapperListener.init();
 }

ProtocolHandler初始化
本文的ProtocolHandler默认使用Http11NioProtocol,Http11NioProtocol的初始化逻辑是调用AbstractProtocol.init() --> Endpoint.init():

Endpoint初始化
Http11NioProtocol中的Endpoint类型为:NioEndpoint,在ProtocolHandler的初始化过程中会调用NioEndpoint初始化方法, 而NioEndpoint的初始化逻辑是调用AbstractEndpoint.init() -->NioEndpoint.bind():

//org.apache.tomcat.util.net.NioEndpoint
public void bind() throws Exception {
    //初始化ServerSocketChannel
     serverSock = ServerSocketChannel.open();
     socketProperties.setProperties(serverSock.socket());
     InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));
     serverSock.socket().bind(addr,getBacklog());
     serverSock.configureBlocking(true); //mimic APR behavior
     serverSock.socket().setSoTimeout(getSocketProperties().getSoTimeout());

    //需要启动acceptor线程的个数,如果是0,则设置为1
    if (acceptorThreadCount == 0) {
       acceptorThreadCount = 1;
     }

    //需要设置poller线程个数
     if (pollerThreadCount <= 0) {
         pollerThreadCount = 1;
     }
    //省略ssl....

   if (oomParachute>0) reclaimParachute(true);

    //创建一个NioBlockingSelector
    selectorPool.open();
}

启动

启动流程如下图:
这里写图片描述

Endpoint启动

//org.apache.tomcat.util.net.NioEndpoint
 public void startInternal() throws Exception {

        if (!running) {
            running = true;
            paused = false;

            // 创建工作线程池
            if ( getExecutor() == null ) {
                createExecutor();
            }

            initializeConnectionLatch();

            // 启动Poller线程,用来处理Acceptor传递来的socket请求。
            pollers = new Poller[getPollerThreadCount()];
            for (int i=0; i<pollers.length; i++) {
                pollers[i] = new Poller();
                Thread pollerThread = new Thread(pollers[i], getName() + "-ClientPoller-"+i);
                pollerThread.setPriority(threadPriority);
                pollerThread.setDaemon(true);
                pollerThread.start();
            }

            //启动Acceptor线程,用来监听接收请求;
            startAcceptorThreads();
        }
    }

这里首先初始化了一些属性,然后启动了Poller和Acceptor线程,通过这些线程或线程组用来监听请求并处理请求。

MapperListener启动

//org.apache.catalina.connector.MapperListener
public void startInternal() throws LifecycleException {
    Engine engine = (Engine) connector.getService().getContainer();
    addListeners(engine);

    Container[] conHosts = engine.findChildren();
    for (Container conHost : conHosts) {
        Host host = (Host) conHost;
        if (!LifecycleState.NEW.equals(host.getState())) {
            //注册host至mapper,同时递归的将所有的context,wrapper注册
            registerHost(host);
        }
    }
}

MapperListener启动,将所有的Context,以及Wrapper都注册值mapper属性中,供后续的请求uri匹配。
这里写图片描述


HTTP请求处理

1.Acceptor
org.apache.tomcat.util.net.NioEndpoint.Acceptor是NioEndpoint的一个内部类,它用来接收请求:

//org.apache.tomcat.util.net.NioEndpoint.Acceptor 
 protected class Acceptor extends AbstractEndpoint.Acceptor {
      @Override
      public void run() {
        int errorDelay = 0;
        while (running) {
            //省略其他判断.....
            SocketChannel socket =  serverSock.accept();

            //将socket注册到Poller中的selector中。
             if (!setSocketOptions(socket)) {
               countDownConnection();
               closeSocket(socket);
             }
        }
      }
 }

setSocketOptions()

// org.apache.tomcat.util.net.NioEndpoint
protected boolean setSocketOptions(SocketChannel socket) {
    //设置socket非阻塞。
    socket.configureBlocking(false);
    Socket sock = socket.socket();

    // 设置socket熟悉,如keepAlive,timeout 等信息
    socketProperties.setProperties(sock);

    // channel 包含
    NioChannel channel = nioChannels.poll();
    NioBufferHandler bufhandler = new NioBufferHandler(
        socketProperties.getAppReadBufSize(),
        socketProperties.getAppWriteBufSize(),
        socketProperties.getDirectBuffer());
    channel = new NioChannel(socket, bufhandler);
    channel.setIOChannel(socket);
    channel.reset();

    // 将chanel注册到Poller线程
    getPoller0().register(channel);

}

2.Poller
org.apache.tomcat.util.net.NioEndpoint.Poller也是NioEndpoint的一个内部类:

//org.apache.tomcat.util.net.NioEndpoint.Poller
public class Poller implements Runnable {

   public Poller() throws IOException {
     synchronized (Selector.class) {
            //获取当前线程的selector,不是bind方法中selectorPool.open();的selector
        this.selector = Selector.open();
     }
   }

    public void register(final NioChannel socket) {
        socket.setPoller(this);
        KeyAttachment key = keyCache.poll();

        /*
         * 创建一个新的KeyAttachment,入参是socket
         * KeyAttachment的类结构继承SocketWrapper<NioChannel>
         */
        final KeyAttachment ka = key!=null?key:new KeyAttachment(socket);
        ka.reset(this,socket,getSocketProperties().getSoTimeout());

        //关注read事件
        ka.interestOps(SelectionKey.OP_READ);

        PollerEvent r = eventCache.poll();
        if ( r==null) {
            //PollerEvent作用:将SocketChannel注册到selector中
            r = new PollerEvent(socket,ka,OP_REGISTER);
        } else {
            r.reset(socket,ka,OP_REGISTER);
        }
        //添加event
        addEvent(r);
    }

   @Override
   public void run() {
     //省略其他.....

     keyCount = selector.selectNow();
     if ( keyCount == 0 ) hasEvents = (hasEvents | events());

     //轮询准备就绪的Key
     Iterator<SelectionKey> iterator = keyCount > 0 ? selector.selectedKeys().iterator() : null;

     while (iterator != null && iterator.hasNext()) {
      SelectionKey sk = iterator.next(); //阻塞...
      KeyAttachment attachment = (KeyAttachment)sk.attachment();
      if (attachment == null) {
          iterator.remove();
      } else {
          attachment.access();
          iterator.remove();

          //调用processKey 处理socket
          processKey(sk, attachment);
     }
    }
  }

   //处理SelectionKey,使用read,write方法等。
   protected boolean processKey(SelectionKey sk, KeyAttachment attachment) {
      unreg(sk, attachment, sk.readyOps());
        boolean closeSocket = false;
        if (sk.isReadable()) {
            //调用processSocket处理socket
            if (!processSocket(channel, SocketStatus.OPEN_READ, true)) {
                closeSocket = true;
            }
        }
        if (!closeSocket && sk.isWritable()) {
            if (!processSocket(channel, SocketStatus.OPEN_WRITE, true)) {
                closeSocket = true;
            }
        }
        if (closeSocket) {
            cancelledKey(sk,SocketStatus.DISCONNECT,false);
        }
  }
}

,上述代码的作用

  • 处理Acceptor接收到NioChannel并注册到selector
  • 然后调用后续的processKey()方法,该方法用来处理NioChannel的read,write方法等。
  • 上述方法内部又调用了Endpoint.processSocket()来处理,在本方法内部主要是将请求转交给SocketProcessor
//org.apache.tomcat.util.net.NioEndpoint
 public boolean processSocket(NioChannel socket, SocketStatus status, boolean dispatch) {
    KeyAttachment attachment = (KeyAttachment)socket.getAttachment(false);
    SocketProcessor sc = processorCache.poll();
    //构造SocketProcessor
    if ( sc == null ) {
        sc = new SocketProcessor(socket,status);
    } else {
        sc.reset(socket,status);
    }

    if ( dispatch && getExecutor()!=null ) {
        //通过线程池处理SocketProcessor
        getExecutor().execute(sc);
    }else {
        //调用SocketProcessor.run()处理请求
        sc.run();
    }
 }

4.SocketProcessor

protected class SocketProcessor implements Runnable {

    protected NioChannel socket = null;
    protected SocketStatus status = null;

    public SocketProcessor(NioChannel socket, SocketStatus status) {
        reset(socket,status);
    }

    public void reset(NioChannel socket, SocketStatus status) {
        this.socket = socket;
        this.status = status;
    }

   public void run() {
        SelectionKey key = socket.getIOChannel().keyFor(
                socket.getPoller().getSelector());
        KeyAttachment ka = (KeyAttachment)key.attachment();
        //省略其他....

        doRun(key, ka);
   }

   private void doRun(SelectionKey key, KeyAttachment ka) {
    SocketState state = SocketState.OPEN;
     /*
      * 调用handler处理请求, 
      * 此时的handler为在Http11NioProtocol构造函数中设置的Http11ConnectionHandler(this)
      */
     state = handler.process(ka, SocketStatus.OPEN_READ); 
   }

}

SocketProcessor处理请求,将请求交给Http11ConnectionHandler处理。

5.Handler
Hander的实际类型为Http11ConnectionHandler,它的类图如下:
这里写图片描述
process()方法最终在AbstractProtocol.AbstractConnectionHandler中实现。

//org.apache.coyote.AbstractProtocol.AbstractConnectionHandler
public SocketState process(SocketWrapper<S> wrapper,
                SocketStatus status) {
    S socket = wrapper.getSocket();   
    Processor<S> processor = connections.get(socket);
    if (processor == null) {
        //获取processor实例
        processor = createProcessor();
    }         
    //调用processor.process()
    SocketState state = processor.process(wrapper);

    //省略其他...
    return SocketState.CLOSED;
}

,方法的主要逻辑是

  • 获取创建Processor,此时示例类型是Http11NioProcessor
  • 然后调用Processor处理请求

6.Processor
通过Http11NioProtocol.createProcessor()获取的Processor,代码如下:

//org.apache.coyote.http11.Http11NioProtocol
public Http11NioProcessor createProcessor() {
    Http11NioProcessor processor = new Http11NioProcessor(
            proto.getMaxHttpHeaderSize(), (NioEndpoint)proto.endpoint,
            proto.getMaxTrailerSize(), proto.getMaxExtensionSize());
    processor.setAdapter(proto.adapter);
    processor.setMaxKeepAliveRequests(proto.getMaxKeepAliveRequests());
    processor.setKeepAliveTimeout(proto.getKeepAliveTimeout());
    //省略其他一些属性配置......
    register(processor);
    return processor;
}

,由上述代码可知Processor的具体类型是Http11NioProcessorprocessor.process()方法处理请求逻辑来自它的父类AbstractHttp11Proccessor

//org.apache.coyote.http11.AbstractHttp11Processor
public SocketState process(SocketWrapper<S> socketWrapper)
       throws IOException {
   RequestInfo rp = request.getRequestProcessor();
   rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);

   // Setting up the I/O
   setSocketWrapper(socketWrapper);
   getInputBuffer().init(socketWrapper, endpoint);
   getOutputBuffer().init(socketWrapper, endpoint);

    if (!error) {
      rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
      // 预处理,设置解析请求头,URI,METHOD等等....
      prepareRequest();
   }      
   if (!error) {
     rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
     //调用Adapter执行请求
     adapter.service(request, response);
   }
}

在这个方法里面,最终请求交给Adapter处理,注意可以看到此时请求的参数是org.apache.coyote.Request,org.apache.coyote.Response

7.Adapter
Tomcat7中Adapter只有一个实现类CoyoteAdapter,处理逻辑如下:

//org.apache.catalina.connector.CoyoteAdapter
public void service(org.apache.coyote.Request req,
                    org.apache.coyote.Response res){

    /*
     * 将org.apache.coyote.Request , Response
     * 转换为org.apache.catalina.connector.Request , Response
     */
    Request request = (Request) req.getNote(ADAPTER_NOTES);
    Response response = (Response) res.getNote(ADAPTER_NOTES);

    // postParseRequest预处理
    boolean postParseSuccess = postParseRequest(req, request, res, response);
    if (postParseSuccess) {
        //交给container处理请求       connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
    }
}

service方法的作用,主要是完成如下2个任务:

  • 预处理请求,通过request URI的信息找到属于自己的Context和Wrapper
  • 调用container处理请求。

1).postParseRequest()

protected boolean postParseRequest(org.apache.coyote.Request req,
                                   Request request,
                                   org.apache.coyote.Response res,
                                   Response response)
        throws Exception {
        //定位host,context,wrapper
        connector.getMapper().map(serverName, decodedURI, version,
                                              request.getMappingData());
        request.setContext((Context) request.getMappingData().context);
        request.setWrapper((Wrapper) request.getMappingData().wrapper);

        //设置session....
        sessionID = request.getPathParameter(
                                SessionConfig.getSessionUriParamName(
                                        request.getContext()));
        request.setRequestedSessionId(sessionID);

        //省略....
}

2).Container处理请求
Containner处理请求的流程如下图:
这里写图片描述

  • StandardEngineValve.invoke():寻找host,并交由host.getPipeline()处理请求
  • StandardHostValve.invoke():设置context,并设置context.getLoader().getClassLoader(),最终交由context.getPipeline()处理请求
  • `StandardContextValve.invoke()“:寻找wrapper,并交由wrapper.getPipeline()处理请求
  • StandardWrapperValve.invoke():通过wrapper定位初&始化、javax.servlet.Servlet,并执行FilterChain执行过程中调用servlet.service()方法
//org.apache.catalina.core.StandardWrapperValve
 public final void invoke(Request request, Response response)
        throws IOException, ServletException {
        StandardWrapper wrapper = (StandardWrapper) getContainer();
        Servlet servlet = null;
        Context context = (Context) wrapper.getParent();

        //定位Servlet,并执行servlet.init()
        servlet = wrapper.allocate();

        //创建本次请求的filterChain
        ApplicationFilterFactory factory = ApplicationFilterFactory.getInstance(); 
        ApplicationFilterChain filterChain = factory.createFilterChain(request, wrapper, servlet);

        //省略其他....

        //调用filterChain,并在此方法内部执行service.service()
        filterChain.doFilter(request.getRequest(), response.getResponse());
} 
 //org.apache.catalina.core.ApplicationFilterChain
 private void internalDoFilter(ServletRequest request, 
                                  ServletResponse response)
        throws IOException, ServletException {
        ApplicationFilterConfig filterConfig = filters[pos++];
        Filter filter = null;
        filter = filterConfig.getFilter();

        // 省略其他.....
        servlet.service(request, response);
}

猜你喜欢

转载自blog.csdn.net/it_freshman/article/details/81710793