深入理解Tomcat系列之三:Connector

简介:

前言

Connector是Tomcat的连接器,其主要任务是负责处理浏览器发送过来的请求,并创建一个Request和Response的对象用于和浏览器交换数据,然后产生一个线程用于处理请求,Connector会把Request和Response对象传递给该线程,该线程的具体的处理过程是Container容器的事了。执行过程分为以下几个步骤

  1. 实例化Connector,构造一个Connector对象
  2. 调用Connector的initIntenal方法,初始化Connetor
  3. 调用ProtocolHanlder的init方法,完成ProtocolHanlder的初始化。这个过程包括了创建线程池并创建一个线程处理浏览器请求
  4. 调用Connector的startIntenal方法,启动Connector
  5. 调用ProtocolHandler的start方法,启动Protocolhanlder
  6. 调用MapperListener的start方法,启动监听器程序

为了对Connector的执行过程有一个大概的印象,可以参考下面的序列图:

Connector启动过程

注意:由于Tomcat还支持AJP协议,但为了简化,我画的这个序列图是基于Http协议的,这也是我们在Web开发中接触最多的协议了。

在深入Connector之前我们先看看Connector类的结构:

Connector属性
Connector部分方法

既然是处理浏览器请求,那么需要支持http协议,在Tomcat中有两种协议处理器:HTTP/1.1与AJP/1.3协议处理器。在server.xml中已经指明tomcat所支持的两种协议:

代码清单3-1:

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

在tomcat中是怎么样分别处理这两种协议的呢,我们可以ProtocolHanlder类中找到答案:

ProtocolHanlder协议处理器

图中被选中的就是Tomcat默认使用协议处理器,其实现过程与Java标准Socket编程是一样的,在tomcat中可以使用Connetor类的setProtocol方法,看看源码就知道了:

代码清单3-2:

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

从第2个if子句的最后一个else可以知道tomcat默认使用的是http1.1协议。
我们再看看Connector的初始化过程:

代码清单3-3:

    @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);
        }
        // Initialize mapper listener
        mapperListener.init();
    }

从这段代码中可以看到:首先调用父类org.apache.catalina.util.LifecycleMBeanBase的初始化方法,然后创建一个Adapter,然后设置protocolHanlder(协议处理器)的Adapter,同时判断传过来的请求的请求方法(比如get或者post),如果没有指明请求方法,默认使用post处理,然后调用protocolHanlder的初始化方法,最后调用mapperListener的初始化方法,而mapperListener的初始化方法调用的是org.apache.catalina.util.LifecycleBase的init方法,我们重点关注protocolHanlder的初始化方法,具体是实现在AbstractProtocol抽象类中,其直接子类有AbstractAjpProtocol和AbstractHttp11Protocol,分别对应的是两种不同的处理协议,所以协议处理器的初始化方法是在其子抽象类(实现ProtocolHanlder接口的抽象类)来实现的,这里看看AbstractHttp11Protocol的初始化方法:

代码清单3-4:

    @Override
    public void init() throws Exception {
        if (getLog().isInfoEnabled())
            getLog().info(sm.getString("abstractProtocolHandler.init",
                    getName()));
        if (oname == null) {
            // Component not pre-registered so register it
            oname = createObjectName();
            if (oname != null) {
                Registry.getRegistry(null, null).registerComponent(this, oname,
                    null);
            }
        }
        if (this.domain != null) {
            try {
                tpOname = new ObjectName(domain + ":" +
                        "type=ThreadPool,name=" + getName());
                Registry.getRegistry(null, null).registerComponent(endpoint,
                        tpOname, null);
            } catch (Exception e) {
                getLog().error(sm.getString(
                        "abstractProtocolHandler.mbeanRegistrationFailed",
                        tpOname, getName()), e);
            }
            rgOname=new ObjectName(domain +
                    ":type=GlobalRequestProcessor,name=" + getName());
            Registry.getRegistry(null, null).registerComponent(
                    getHandler().getGlobal(), rgOname, null );
        }
        String endpointName = getName();
        endpoint.setName(endpointName.substring(1, endpointName.length()-1));
        try {
            endpoint.init();
        } catch (Exception ex) {
            getLog().error(sm.getString("abstractProtocolHandler.initError",
                    getName()), ex);
            throw ex;
        }
    }

打断点调试可以知道oname的值是Tomcat:type=ProtocolHandler,port=auto-1,address="127.0.0.1",tpOname是Tomcat:type=ProtocolHandler,port=auto-1,address="127.0.0.1",rOname是Tomcat:type=GlobalRequestProcessor,name="http-bio-127.0.0.1-auto-1",我们重点关注endpoint的init方法,主要完成以下几个过程:

  1. 设置线程接收数和最大连接数
  2. 创建线程池,启动监听的线程监听用户请求
  3. 启动一个线程处理请求

初始化完成Connector就可以启动了,启动阶段调用startInternal方法:

代码清单3-5:

    @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);
        }
        mapperListener.start();
    }

可以看出Connector调用protocolHandler.start()方法,继续看看这个方法的源码:

代码清单3-6:

    @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.start()方法:

代码清单3-7:

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

然后又调用了org.apache.tomcat.util.net.AbstractEndpoint.startInternal()方法:

代码清单3-8:

    @Override
    public void startInternal() throws Exception {
        if (!running) {
            running = true;
            paused = false;
            // Create worker collection
            if (getExecutor() == null) {
                createExecutor();
            }
            initializeConnectionLatch();
            startAcceptorThreads();
            // Start async timeout thread
            Thread timeoutThread = new Thread(new AsyncTimeout(),
                    getName() + "-AsyncTimeout");
            timeoutThread.setPriority(threadPriority);
            timeoutThread.setDaemon(true);
            timeoutThread.start();
        }
    }
  1. 设置线程接收数和最大连接数
  2. 创建线程池,启动监听的线程监听用户请求
  3. 启动一个线程处理异步请求

这里启动了一个异步线程处理请求,这个异步线程是如何执行的呢?

代码清单3-9:

    /**
     * Async timeout thread
     */
    protected class AsyncTimeout implements Runnable {
        /**
         * The background thread that checks async requests and fires the
         * timeout if there has been no activity.
         */
        @Override
        public void run() {
            // Loop until we receive a shutdown command
            while (running) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // Ignore
                }
                long now = System.currentTimeMillis();
                Iterator<SocketWrapper<Socket>> sockets =
                    waitingRequests.iterator();
                while (sockets.hasNext()) {
                    SocketWrapper<Socket> socket = sockets.next();
                    long access = socket.getLastAccess();
                    if (socket.getTimeout() > 0 &&
                            (now-access)>socket.getTimeout()) {
                        processSocketAsync(socket,SocketStatus.TIMEOUT);
                    }
                }
                // Loop if endpoint is paused
                while (paused && running) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // Ignore
                    }
                }
            }
        }
    }

    //processSocket
    public boolean processSocketAsync(SocketWrapper<Socket> socket,
            SocketStatus status) {
        try {
            synchronized (socket) {
                if (waitingRequests.remove(socket)) {
                    SocketProcessor proc = new SocketProcessor(socket,status);
                    ClassLoader loader = Thread.currentThread().getContextClassLoader();
                    try {
                        //threads should not be created by the webapp classloader
                        if (Constants.IS_SECURITY_ENABLED) {
                            PrivilegedAction<Void> pa = new PrivilegedSetTccl(
                                    getClass().getClassLoader());
                            AccessController.doPrivileged(pa);
                        } else {
                            Thread.currentThread().setContextClassLoader(
                                    getClass().getClassLoader());
                        }
                        // During shutdown, executor may be null - avoid NPE
                        if (!running) {
                            return false;
                        }
                        getExecutor().execute(proc);
                        //TODO gotta catch RejectedExecutionException and properly handle it
                    } finally {
                        if (Constants.IS_SECURITY_ENABLED) {
                            PrivilegedAction<Void> pa = new PrivilegedSetTccl(loader);
                            AccessController.doPrivileged(pa);
                        } else {
                            Thread.currentThread().setContextClassLoader(loader);
                        }
                    }
                }
            }
        } 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;
    }

org.apache.tomcat.util.net.JIoEndpoint.SocketProcessor的职责是把具体的请求处理过程委派给org.apache.tomcat.util.net.JIoEndpoint.Handler,然后根据handler返回的不同SocketState,来决定是否关闭连接或者进行下一轮处理。

代码清单3-10:

public void run() {
            boolean launch = false;
            synchronized (socket) {
                try {
                    SocketState state = SocketState.OPEN;
                    try {
                        // SSL handshake
                        serverSocketFactory.handshake(socket.getSocket());
                    } catch (Throwable t) {
                        ExceptionUtils.handleThrowable(t);
                        if (log.isDebugEnabled()) {
                            log.debug(sm.getString("endpoint.err.handshake"), t);
                        }
                        // Tell to close the socket
                        state = SocketState.CLOSED;
                    }
                    if ((state != SocketState.CLOSED)) {
                        if (status == null) {
                            state = handler.process(socket, SocketStatus.OPEN_READ);
                        } else {
                            state = handler.process(socket,status);
                        }
                    }
                    if (state == SocketState.CLOSED) {
                        // Close socket
                        if (log.isTraceEnabled()) {
                            log.trace("Closing socket:"+socket);
                        }
                        countDownConnection();
                        try {
                            socket.getSocket().close();
                        } catch (IOException e) {
                            // Ignore
                        }
                    } else if (state == SocketState.OPEN ||
                            state == SocketState.UPGRADING ||
                            state == SocketState.UPGRADING_TOMCAT  ||
                            state == SocketState.UPGRADED){
                        socket.setKeptAlive(true);
                        socket.access();
                        launch = true;
                    } else if (state == SocketState.LONG) {
                        socket.access();
                        waitingRequests.add(socket);
                    }
                } finally {
                    if (launch) {
                        try {
                            getExecutor().execute(new SocketProcessor(socket, SocketStatus.OPEN_READ));
                        } catch (RejectedExecutionException x) {
                            log.warn("Socket reprocessing request was rejected for:"+socket,x);
                            try {
                                //unable to handle connection at this time
                                handler.process(socket, SocketStatus.DISCONNECT);
                            } finally {
                                countDownConnection();
                            }
                        } catch (NullPointerException npe) {
                            if (running) {
                                log.error(sm.getString("endpoint.launch.fail"),
                                        npe);
                            }
                        }
                    }
                }
            }
            socket = null;
            // Finish up this request
        }
    }

其中的process方法主要完成对request的解析,包括请求头、请求行和请求体
代码清单3-11:

    //process method of org.apache.coyote.http11.AbstractHttp11Processor<S>.HttpProcessor extends org.apache.coyote.http11.AbstractHttp11Processor<S>
    @Override
    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);
        // Flags
        error = false;
        keepAlive = true;
        comet = false;
        openSocket = false;
        sendfileInProgress = false;
        readComplete = true;
        if (endpoint.getUsePolling()) {
            keptAlive = false;
        } else {
            keptAlive = socketWrapper.isKeptAlive();
        }
        if (disableKeepAlive()) {
            socketWrapper.setKeepAliveLeft(0);
        }
        while (!error && keepAlive && !comet && !isAsync() &&
                upgradeInbound == null &&
                httpUpgradeHandler == null && !endpoint.isPaused()) {
            // Parsing the request header
            try {
                setRequestLineReadTimeout();
                if (!getInputBuffer().parseRequestLine(keptAlive)) {
                    if (handleIncompleteRequestLineRead()) {
                        break;
                    }
                }
                if (endpoint.isPaused()) {
                    // 503 - Service unavailable
                    response.setStatus(503);
                    error = true;
                } else {
                    // Make sure that connectors that are non-blocking during
                    // header processing (NIO) only set the start time the first
                    // time a request is processed.
                    if (request.getStartTime() < 0) {
                        request.setStartTime(System.currentTimeMillis());
                    }
                    keptAlive = true;
                    // Set this every time in case limit has been changed via JMX
                    request.getMimeHeaders().setLimit(endpoint.getMaxHeaderCount());
                    // Currently only NIO will ever return false here
                    if (!getInputBuffer().parseHeaders()) {
                        // We've read part of the request, don't recycle it
                        // instead associate it with the socket
                        openSocket = true;
                        readComplete = false;
                        break;
                    }
                    if (!disableUploadTimeout) {
                        setSocketTimeout(connectionUploadTimeout);
                    }
                }
            } catch (IOException e) {
                if (getLog().isDebugEnabled()) {
                    getLog().debug(
                            sm.getString("http11processor.header.parse"), e);
                }
                error = true;
                break;
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                UserDataHelper.Mode logMode = userDataHelper.getNextMode();
                if (logMode != null) {
                    String message = sm.getString(
                            "http11processor.header.parse");
                    switch (logMode) {
                        case INFO_THEN_DEBUG:
                            message += sm.getString(
                                    "http11processor.fallToDebug");
                            //$FALL-THROUGH$
                        case INFO:
                            getLog().info(message);
                            break;
                        case DEBUG:
                            getLog().debug(message);
                    }
                }
                // 400 - Bad Request
                response.setStatus(400);
                adapter.log(request, response, 0);
                error = true;
            }
            if (!error) {
                // Setting up filters, and parse some request headers
                rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
                try {
                    prepareRequest();
                } catch (Throwable t) {
                    ExceptionUtils.handleThrowable(t);
                    if (getLog().isDebugEnabled()) {
                        getLog().debug(sm.getString(
                                "http11processor.request.prepare"), t);
                    }
                    // 400 - Internal Server Error
                    response.setStatus(400);
                    adapter.log(request, response, 0);
                    error = true;
                }
            }
            if (maxKeepAliveRequests == 1) {
                keepAlive = false;
            } else if (maxKeepAliveRequests > 0 &&
                    socketWrapper.decrementKeepAlive() <= 0) {
                keepAlive = false;
            }
            // Process the request in the adapter
            if (!error) {
                try {
                    rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
                    adapter.service(request, response);

                    if(keepAlive && !error) { // Avoid checking twice.
                        error = response.getErrorException() != null ||
                                (!isAsync() &&
                                statusDropsConnection(response.getStatus()));
                    }
                    setCometTimeouts(socketWrapper);
                } catch (InterruptedIOException e) {
                    error = true;
                } catch (HeadersTooLargeException e) {
                    error = true;
                    // The response should not have been committed but check it
                    // anyway to be safe
                    if (!response.isCommitted()) {
                        response.reset();
                        response.setStatus(500);
                        response.setHeader("Connection", "close");
                    }
                } catch (Throwable t) {
                    ExceptionUtils.handleThrowable(t);
                    getLog().error(sm.getString(
                            "http11processor.request.process"), t);
                    // 500 - Internal Server Error
                    response.setStatus(500);
                    adapter.log(request, response, 0);
                    error = true;
                }
            }
            // Finish the handling of the request
            rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);
            if (!isAsync() && !comet) {
                if (error) {

                    getInputBuffer().setSwallowInput(false);
                }
                if (response.getStatus() < 200 || response.getStatus() > 299) {
                    if (expectation) {
                        // Client sent Expect: 100-continue but received a
                        // non-2xx response. Disable keep-alive (if enabled) to
                        // ensure the connection is closed. Some clients may
                        // still send the body, some may send the next request.
                        // No way to differentiate, so close the connection to
                        // force the client to send the next request.
                        getInputBuffer().setSwallowInput(false);
                        keepAlive = false;
                    }
                }
                endRequest();
            }
            rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);
            // If there was an error, make sure the request is counted as
            // and error, and update the statistics counter
            if (error) {
                response.setStatus(500);
            }
            request.updateCounters();
            if (!isAsync() && !comet || error) {
                getInputBuffer().nextRequest();
                getOutputBuffer().nextRequest();
            }
            if (!disableUploadTimeout) {
                if(endpoint.getSoTimeout() > 0) {
                    setSocketTimeout(endpoint.getSoTimeout());
                } else {
                    setSocketTimeout(0);
                }
            }
            rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
            if (breakKeepAliveLoop(socketWrapper)) {
                break;
            }
        }
        rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
        }
    }

首先在Http11Processor的process方法里,会先从socket里读取http请求数据,并解析请求头,构造Request对象和Response对象,然后调用Adapter.service()方法。Adapter.service()完成请求行以及请求体的解析,并把解析出来的信息封装到Request和Response对象中,Adapter(确切说是org.apache.catalina.connector.CoyoteAdapter)是connector和container的桥梁,经过这一步,请求就从connector传递到container里了,Adapter.service()方法之后便将封装了Request以及Response对象的Socket传给Container容器了。
要注意的是:最先处理请求的Request是org.apache.coyote.Request类型,这是一个Tomcat中一个轻量级对象,完成基本的请求处理后很容易被JVM回收,那为什么不直接交给Connector.Request对象处理呢?由于后者是Servlet容器真正传递的对象其完成的职责比前者复杂,这里使用org.apache.coyote.Request主要减轻后者的任务负担,出于性能考虑才这么设计。
具体service方法清单如下:

代码清单3-12:

@Override
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);
      if (request == null) {
          // 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());
      }
      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());
          //postParseRequest方法把CoyoteRequest转换为Connector.Request对象
          //后一类型的对象才是在Tomcat容器流转时真正传递的对象
          boolean postParseSuccess = postParseRequest(req, request, res, response);
          if (postParseSuccess) {
              //check valves if we support async
              request.setAsyncSupported(connector.getService().getContainer().getPipeline().isAsyncSupported());
    // 调用Container容器的invoke方法,把请求交给Container容器
              connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
              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_READ)) {
                              comet = true;
                              res.action(ActionCode.COMET_BEGIN, null);
                          }
                      } else {
                          comet = true;
                          res.action(ActionCode.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) {
              async = true;
          } else if (!comet) {
              request.finishRequest();
              response.finishResponse();
              if (postParseSuccess &&
                      request.getMappingData().context != null) {
                  ((Context) request.getMappingData().context).logAccess(
                          request, response,
                          System.currentTimeMillis() - req.getStartTime(),
                          false);
              }
              req.action(ActionCode.POST_REQUEST , null);
          }
      } catch (IOException e) {
          // Ignore
      } finally {
         //ignore
      }
  }

connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);这句代码中可以知道下一步的处理需要交给Container容器了。

经过上面一系列复杂的操作流程,Tomcat的Connector已经完成了protocol.start()方法,返回Connector的startIntenal方法,还有一个步骤要完成就是mapperListener.start()的方法了,整个执行过程比较简单,有两步:

  1. 执行Connector的startIntenal方法
  2. 执行MapperListener的startIntenal方法

代码清单3-14:

    @Override
    public void startInternal() throws LifecycleException {
        setState(LifecycleState.STARTING);
        findDefaultHost();
        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())) {
                // Registering the host will register the context and wrappers
                registerHost(host);
            }
        }
    }

首先注册已初始化的组件,然后为这些组件添加监听器,最后添加容器之间的映射关系。这样经过上面两个大步骤以及N个小步骤,我们的Connector才算启动完毕,可谓是路途艰辛啊!

目录
相关文章
|
5月前
|
应用服务中间件
The Tomcat connector configured to listen on port 10000 failed to start. The port may already be in
The Tomcat connector configured to listen on port 10000 failed to start. The port may already be in
|
9月前
|
应用服务中间件 Windows
The Tomcat connector configured to listen on port 18081 failed to start. The port may already be in
The Tomcat connector configured to listen on port 18081 failed to start. The port may already be in
140 0
|
应用服务中间件
The Tomcat connector configured to listen on port 8200 failed to start.【异常详情处理】
The Tomcat connector configured to listen on port 8200 failed to start.【异常详情处理】
169 0
The Tomcat connector configured to listen on port 8200 failed to start.【异常详情处理】
|
Java 应用服务中间件 Apache
Tomcat架构解析之2 connector BIO
在上文已介绍过,connector组件是service容器中的一部分。 它主要是接收,解析HTTP请求,然后调用本service下的相关Servlet Tomcat从架构上采用的是一个分层结构,因此根据解析过的HTTP请求,定位到相应的Servlet也...
1218 0
|
Java 应用服务中间件
Tomcat架构解析之3 Connector NIO
上文简单记录了默认的Connector的内部构造及消息流,同时此Connector也是基于BIO的实现。 除BIO,也可以通过配置快速部署NIO的connector。
1206 0
|
缓存 Java 应用服务中间件
Java网络编程与NIO详解11:Tomcat中的Connector源码分析(NIO)
Tomcat 中的 NIO 源码分析 转自https://www.javadoop.com/post/tomcat-nio#toc1 之前写了两篇关于 NIO 的文章,第一篇介绍了 NIO 的 Channel、Buffer、Selector 使用,第二篇介绍了非阻塞 IO 和异步 IO,并展示了简单的用例。
|
网络协议 Java 应用服务中间件
Tomcat源码分析----Connector初始化与加载
一个应用服务器的性能很大程度上取决于网络通信模块的实现,因此Connector对于tomcat而言是重中之重。在本章节中以下两个问题会被回答:一个http请求是怎么被tomcat监听到的,会有哪些处理;为什么请求可以有需要通过nginx的,也可以不需要nginx的直接请求到tomcat上?
6073 0
|
前端开发 应用服务中间件 数据格式