Connector组件 - 969251639/study GitHub Wiki

Connector时tomcat用来接受对外请求的一个组件,它可以根据protocol属性创建不同的Connector来处理不同协议的连接

    <!-- 处理http请求的连接 -->
    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
    <!-- 处理ajp请求的连接 -->
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

我们用得最多基本都是http,所以下面就只分析tomcat如何处理http的请求
Connector创建时会先创建一个protocolHandler,而protocolHandler时一个抽象的概念,具体可以有http的protocolHandler,有ajp的protocolHandler等

    public Connector(String protocol) {
        setProtocol(protocol);
        // Instantiate protocol handler
        ProtocolHandler p = null;
        try {
            Class<?> clazz = Class.forName(protocolHandlerClassName);
            p = (ProtocolHandler) clazz.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            log.error(sm.getString(
                    "coyoteConnector.protocolHandlerInstantiationFailed"), e);
        } finally {
            this.protocolHandler = p;
        }

        if (!Globals.STRICT_SERVLET_COMPLIANCE) {
            URIEncoding = "UTF-8";
            URIEncodingLower = URIEncoding.toLowerCase(Locale.ENGLISH);
        }
    }

    public void setProtocol(String protocol) {

        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 {
            if ("HTTP/1.1".equals(protocol)) {
                setProtocolHandlerClassName
                    ("org.apache.coyote.http11.Http11NioProtocol");
            } else if ("AJP/1.3".equals(protocol)) {
                setProtocolHandlerClassName
                    ("org.apache.coyote.ajp.AjpNioProtocol");
            } else if (protocol != null) {
                setProtocolHandlerClassName(protocol);
            }
        }

    }

可以看到默认的http协议用的就是org.apache.coyote.http11.Http11NioProtocol(protocolHandler )

接下来看它的init方法

    @Override
    protected void initInternal() throws LifecycleException {

        super.initInternal();

        // Initialize adapter
        adapter = new CoyoteAdapter(this);
        protocolHandler.setAdapter(adapter);

        // Make sure parseBodyMethodsSet has a default
        if( null == parseBodyMethodsSet ) {
            setParseBodyMethods(getParseBodyMethods());
        }

        if (protocolHandler.isAprRequired() &&
                !AprLifecycleListener.isAprAvailable()) {
            throw new LifecycleException(
                    sm.getString("coyoteConnector.protocolHandlerNoApr",
                            getProtocolHandlerClassName()));
        }

        try {
            protocolHandler.init();
        } catch (Exception e) {
            throw new LifecycleException
                (sm.getString
                 ("coyoteConnector.protocolHandlerInitializationFailed"), e);
        }
    }

主要做两件事

  1. 创建一个适配器,用来适配原生的request和response到HttpServletRequest和HttpServletResponse
  2. 初始化protocolHandler

先看适配器,使用servlet的时候的入口是service方法

public interface Servlet {
    ...
    public void service(ServletRequest req, ServletResponse res)
            throws ServletException, IOException;
    ...
}

所以适配器也肯定会适配该方法,也就是CoyoteAdapter中的service方法

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是Servlet真正需要的HttpServletRequest和HttpServletResponse,需要将
        //org.apache.coyote.Request和org.apache.coyote.Response转成HttpServletRequest和HttpServletResponse
        if (request == null) {//创建HttpServletRequest和创建HttpServletResponse

            // Create objects
            request = connector.createRequest();
            request.setCoyoteRequest(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());//处理请求组编码

        }
        ...
            // Parse and set Catalina and configuration specific
            // request parameters
            req.getRequestProcessor().setWorkerThreadName(THREAD_NAME.get());//设置该请求的线程名
            postParseSuccess = postParseRequest(req, request, res, response);//解析http
            if (postParseSuccess) {
                //check valves if we support async
                request.setAsyncSupported(connector.getService().getContainer().getPipeline().isAsyncSupported());
                // Calling the container
                connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
                ...
            }
        ...
    }

这个方法很长,最主要的是**connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);**这行代码的调用,这是一个tomcat管道的调用,用来组织tomcat内部各个组件的数据输入输出

再看tomcat架构图其实也是可以发现,内部的组件是有箭头串联起来的,而这些箭头就是管道(pipeline)

管道从connector开始流入(connector时连接的起点),最后流入到Wrapper未终点,而Wrapper其实就是我们想要的Servlet
这里只需要先知道数据从这里开始流入即可,先跳过管道的调用,再看回Connector的start方法
https://github.com/969251639/study/wiki/%E7%AE%A1%E9%81%93%E7%BB%84%E4%BB%B6

    @Override
    protected void startInternal() throws LifecycleException {

        // Validate settings before starting
        if (getPort() < 0) {
            throw new LifecycleException(sm.getString(
                    "coyoteConnector.invalidPort", Integer.valueOf(getPort())));
        }

        setState(LifecycleState.STARTING);

        try {
            protocolHandler.start();
        } catch (Exception e) {
            String errPrefix = "";
            if(this.service != null) {
                errPrefix += "service.getName(): \"" + this.service.getName() + "\"; ";
            }

            throw new LifecycleException
                (errPrefix + " " + sm.getString
                 ("coyoteConnector.protocolHandlerStartFailed"), e);
        }
    }

connector的启动其实就是protocolHandler的启动,看org.apache.coyote.http11.Http11NioProtocol的启动过程(托管它的父类AbstractProtocol进行启动)

    @Override
    public void start() throws Exception {
        if (getLog().isInfoEnabled())
            getLog().info(sm.getString("abstractProtocolHandler.start",
                    getName()));
        try {
            endpoint.start();
        } catch (Exception ex) {
            getLog().error(sm.getString("abstractProtocolHandler.startError",
                    getName()), ex);
            throw ex;
        }
    }

会发现启动也很简单,就是启用了endpoint,那么endpoint又是干嘛的呢?

首先先跳回协议处理器org.apache.coyote.http11.Http11NioProtocol(protocolHandler )的构造方法

    public Http11NioProtocol() {
        endpoint=new NioEndpoint();
        cHandler = new Http11ConnectionHandler(this);
        ((NioEndpoint) endpoint).setHandler(cHandler);
        setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
        setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
        setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
    }

在创建Http11NioProtocol对象时就已经创建好了endpoint组件,同时也给endpoint组件设置了一个Http11ConnectionHandler,用来处理socket操作

然后看endpoint的start方法,这个方法在NioEndpoint的父类(AbstractEndpoint)中实现

    public final void start() throws Exception {
        if (bindState == BindState.UNBOUND) {
            bind();
            bindState = BindState.BOUND_ON_START;
        }
        startInternal();
    }

首先绑定端口号,然后启动真正的NioEndpoint组件

先看绑定操作

    @Override
    public void bind() throws Exception {
        //绑定地址和端口
        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());

        // Initialize thread count defaults for acceptor, poller
        if (acceptorThreadCount == 0) {//设置接受请求的线程数,一般为1个
            // FIXME: Doesn't seem to work that well with multiple accept threads
            acceptorThreadCount = 1;
        }
        if (pollerThreadCount <= 0) {//设置poller线程数,一般未cpu的核数, private int pollerThreadCount = Math.min(2,Runtime.getRuntime().availableProcessors());
            //minimum one poller thread
            pollerThreadCount = 1;
        }
        stopLatch = new CountDownLatch(pollerThreadCount);

        // SSL
        if (isSSLEnabled()) {
            ...
        }
        ...
    }

最后看Connector的启动过程

    @Override
    public void startInternal() throws Exception {

        if (!running) {
            running = true;
            paused = false;
            
            //对象缓冲
            processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                    socketProperties.getProcessorCache());
            eventCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                            socketProperties.getEventCache());
            nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                    socketProperties.getBufferPool());

            // Create worker collection
            if ( getExecutor() == null ) {//创建worker线程池
                createExecutor();
            }

            initializeConnectionLatch();//初始化连接数控制器组件

            // Start poller threads
            pollers = new Poller[getPollerThreadCount()];
            for (int i=0; i<pollers.length; i++) {//创建poller线程池
                pollers[i] = new Poller();
                Thread pollerThread = new Thread(pollers[i], getName() + "-ClientPoller-"+i);
                pollerThread.setPriority(threadPriority);
                pollerThread.setDaemon(true);
                pollerThread.start();
            }

            startAcceptorThreads();//创建acceptor线程池
        }
    }

可以看到,endpoint的组件就是把poller线程,acceptor线程,worker线程给跑起来,最后形成一个典型的reactor多线程模型的线程模型

最后总结下整个过程,然后逐个分析里面的每一个组件

  1. 因为connector是在service中组件中,所以启动service组件之后也会启动connector组件

  2. 初始化connector时主要根据server.xml的配置设置协议并创建协议处理器,然后在生命周期初始化创建了一个协议适配器( Coyote )

  3. 启动协议处理器(根据不同的协议创建不同的协议处理器,默认Http11NioProtocol )

  4. 启动协议处理器只做一件事,把endpoint组件给启动了,endpoint在创建协议处理器时就已经创建了,不同的协议处理器有不同的endpoint,这里的Http11NioProtocol对应的endpoint是NioEndpoint,同时也创建了一个http连接处理器(Http11ConnectionHandler),启动endpoint过程中先绑定端口号,最后执行endpoint的生命周期

  5. 创建工作线程池(最小MinSpareThreads,最大MaxThreads),创建最大连接数的门栅,创建poller线程(线程数根据cpu核数,最小为2),创建acceptor线程(默认为1个)

以上连接器所有组件已全部启动完成,接下来就是等待客户端请求连接和处理

下面是对每个组件的具体分析:
LimitLatch:连接数控制器
Acceptor:接收对外请求
Poller:对连接通道的读与写
WorkerThreadPool:工作线程池,真正执行业务逻辑的地方
Pipeline:管道,连接内部组件的数据输入