千家信息网

连接器在Tomcat中是怎么设计的

发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,这篇文章将为大家详细讲解有关连接器在Tomcat中是怎么设计的,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。从连接器(Connector)源码说起既然
千家信息网最后更新 2026年11月01日连接器在Tomcat中是怎么设计的

这篇文章将为大家详细讲解有关连接器在Tomcat中是怎么设计的,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。

从连接器(Connector)源码说起

既然是来解析连接器(Connector),那么我们直接从源码入手,后面所有源码我会剔除不重要部分,所以会忽略大部分源码细节,只关注流程。源码如下(高能预警,大量代码):

public class Connector extends LifecycleMBeanBase {  public Connector() {  this("org.apache.coyote.http11.Http11NioProtocol");  }  public Connector(String protocol) {  boolean aprConnector = AprLifecycleListener.isAprAvailable() &&  AprLifecycleListener.getUseAprConnector();  if ("HTTP/1.1".equals(protocol) || protocol == null) {  if (aprConnector) {  protocolHandlerClassName = "org.apache.coyote.http11.Http11AprProtocol";  } else {  protocolHandlerClassName = "org.apache.coyote.http11.Http11NioProtocol";  }  } else if ("AJP/1.3".equals(protocol)) {  if (aprConnector) {  protocolHandlerClassName = "org.apache.coyote.ajp.AjpAprProtocol";  } else {  protocolHandlerClassName = "org.apache.coyote.ajp.AjpNioProtocol";  }  } else {  protocolHandlerClassName = protocol;  }  // Instantiate protocol handler  ProtocolHandler p = null;  try {  Class clazz = Class.forName(protocolHandlerClassName);  p = (ProtocolHandler) clazz.getConstructor().newInstance();  } catch (Exception e) {  log.error(sm.getString(  "coyoteConnector.protocolHandlerInstantiationFailed"), e);  } finally {  this.protocolHandler = p;  }  // Default for Connector depends on this system property  setThrowOnFailure(Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"));  }

我们来看看Connector的构造方法,其实只做了一件事情,就是根据协议设置对应的ProtocolHandler,根据名称我们知道,这是协议处理类,所以连接器内部的一个重要子模块就是ProtocolHandler。

关于生命周期

我们看到Connector继承了LifecycleMBeanBase,我们来看看Connector的最终继承关系:

我们看到最终实现的是Lifecycle接口,我们看看这个接口是何方神圣。我把其接口的注释拿下来解释下

/**  * Common interface for component life cycle methods. Catalina components  * may implement this interface (as well as the appropriate interface(s) for  * the functionality they support) in order to provide a consistent mechanism  * to start and stop the component.  * start()  * -----------------------------  * | |  * | init() |  * NEW -»-- INITIALIZING |  * | | | | ------------------«-----------------------  * | | |auto | | |  * | | \|/ start() \|/ \|/ auto auto stop() |  * | | INITIALIZED --»-- STARTING_PREP --»- STARTING --»- STARTED --»--- |  * | | | | |  * | |destroy()| | |  * | --»-----«-- ------------------------«-------------------------------- ^  * | | | |  * | | \|/ auto auto start() |  * | | STOPPING_PREP ----»---- STOPPING ------»----- STOPPED -----»-----  * | \|/ ^ | ^  * | | stop() | | |  * | | -------------------------- | |  * | | | | |  * | | | destroy() destroy() | |  * | | FAILED ----»------ DESTROYING ---«----------------- |  * | | ^ | |  * | | destroy() | |auto |  * | --------»----------------- \|/ |  * | DESTROYED |  * | |  * | stop() |  * ----»-----------------------------»------------------------------  *  * Any state can transition to FAILED.  *  * Calling start() while a component is in states STARTING_PREP, STARTING or  * STARTED has no effect.  *  * Calling start() while a component is in state NEW will cause init() to be  * called immediately after the start() method is entered.  *  * Calling stop() while a component is in states STOPPING_PREP, STOPPING or  * STOPPED has no effect.  *  * Calling stop() while a component is in state NEW transitions the component  * to STOPPED. This is typically encountered when a component fails to start and  * does not start all its sub-components. When the component is stopped, it will  * try to stop all sub-components - even those it didn't start.  *  * Attempting any other transition will throw {@link LifecycleException}.  *  * 
* The {@link LifecycleEvent}s fired during state changes are defined in the * methods that trigger the changed. No {@link LifecycleEvent}s are fired if the * attempted transition is not valid.

这段注释翻译就是,这个接口是提供给组件声明周期管理的,并且提供了声明周期流转图。这里我们只需要知道正常流程即可:

New--->Init()---->Start()---->Stop()--->Destory()

从生命周期探索连接器

根据上面的生命周期说明,我们可以知道连接器(Connector)就是按照如此的声明周期管理的,所以我们找到了线索,所以连接器肯定会先初始化然后再启动。我们查看其initInternal()方法可以知道连接器初始化做了什么事情,源码如下:

@Override  protected void initInternal() throws LifecycleException {  super.initInternal();  if (protocolHandler == null) {  throw new LifecycleException(  sm.getString("coyoteConnector.protocolHandlerInstantiationFailed"));  }  // Initialize adapter  adapter = new CoyoteAdapter(this);  protocolHandler.setAdapter(adapter);  if (service != null) {  protocolHandler.setUtilityExecutor(service.getServer().getUtilityExecutor());  }  // Make sure parseBodyMethodsSet has a default  if (null == parseBodyMethodsSet) {  setParseBodyMethods(getParseBodyMethods());  }  if (protocolHandler.isAprRequired() && !AprLifecycleListener.isInstanceCreated()) {  throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoAprListener",  getProtocolHandlerClassName()));  }  if (protocolHandler.isAprRequired() && !AprLifecycleListener.isAprAvailable()) {  throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoAprLibrary",  getProtocolHandlerClassName()));  }  if (AprLifecycleListener.isAprAvailable() && AprLifecycleListener.getUseOpenSSL() &&  protocolHandler instanceof AbstractHttp11JsseProtocol) {  AbstractHttp11JsseProtocol jsseProtocolHandler =  (AbstractHttp11JsseProtocol) protocolHandler;  if (jsseProtocolHandler.isSSLEnabled() &&  jsseProtocolHandler.getSslImplementationName() == null) {  // OpenSSL is compatible with the JSSE configuration, so use it if APR is available  jsseProtocolHandler.setSslImplementationName(OpenSSLImplementation.class.getName());  }  }  try {  protocolHandler.init();  } catch (Exception e) {  throw new LifecycleException(  sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);  }  } }

根据上面源码,我们发现主要是处理protocolHandler并初始化它,同时我们注意到了protocolHandler 设置了一个适配器,我们看看这个适配器是做啥的,跟踪源码如下:

/**  * The adapter, used to call the connector.  *  * @param adapter The adapter to associate  */  public void setAdapter(Adapter adapter);

这个注释已经说的很直白了,这个适配器就是用来调用连接器的。我们再继续看看protocolHandler的初始化方法

 /**  * Endpoint that provides low-level network I/O - must be matched to the  * ProtocolHandler implementation (ProtocolHandler using NIO, requires NIO  * Endpoint etc.).  */ private final AbstractEndpoint endpoint; public void init() throws Exception {  if (getLog().isInfoEnabled()) {  getLog().info(sm.getString("abstractProtocolHandler.init", getName()));  logPortOffset();  }  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) {  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));  endpoint.setDomain(domain);  endpoint.init();  }

这里出现了一个新的对象,endpoint,根据注释我们可以知道endpoint是用来处理网络IO的,而且必须匹配到指定的子类(比如Nio,就是NioEndPoint处理)。endpoint.init()实际上就是做一些网络的配置,然后就是初始化完毕了。根据我们上面的周期管理,我们知道init()后就是start(),所以我们查看Connector的start()源码:

protected void startInternal() throws LifecycleException { // Validate settings before starting if (getPortWithOffset() < 0) { throw new LifecycleException(sm.getString( "coyoteConnector.invalidPort", Integer.valueOf(getPortWithOffset()))); } setState(LifecycleState.STARTING); try { protocolHandler.start(); } catch (Exception e) { throw new LifecycleException( sm.getString("coyoteConnector.protocolHandlerStartFailed"), e); } }

其实就是主要调用 protocolHandler.start()方法,继续跟踪,为了方便表述,我会把接下来的代码统一放在一起说明,代码如下:

//1.类:AbstractProtocol implements ProtocolHandler,  MBeanRegistration  public void start() throws Exception {  // 省略部分代码  endpoint.start();  } //2. 类:AbstractEndPoint  public final void start() throws Exception {  // 省略部分代码  startInternal();  }  /**3.类:NioEndPoint extends AbstractJsseEndpoint  * Start the NIO endpoint, creating acceptor, poller threads.  */  @Override  public void startInternal() throws Exception {  //省略部分代码    // Start poller thread  poller = new Poller();  Thread pollerThread = new Thread(poller, getName() + "-ClientPoller");  pollerThread.setPriority(threadPriority);  pollerThread.setDaemon(true);  pollerThread.start();  startAcceptorThread();  }  }

到这里,其实整个启动代码就完成了,我们看到最后是在NioEndPoint创建了一个Poller,并且启动它,这里需要补充说明下,这里只是以NioEndPoint为示列,其实Tomcat 主要提供了三种实现,分别是AprEndPoint,NioEndPoint,Nio2EndPoint,这里表示了tomcat支持的I/O模型:

  • APR:采用 Apache 可移植运行库实现,它根据不同操作系统,分别用c重写了大部分IO和系统线程操作模块,据说性能要比其他模式要好(未实测)。

  • NIO:非阻塞 I/O

  • NIO.2:异步 I/O

上述代码主要是开启两个线程,一个是Poller,一个是开启Acceptor,既然是线程,核心的代码肯定是run方法,我们来查看源码,代码如下:

//4.类:Acceptor implements Runnable  public void run() {  //省略了部分代码  U socket = null;  socket = endpoint.serverSocketAccept();  // Configure the socket  if (endpoint.isRunning() && !endpoint.isPaused()) {  // setSocketOptions() will hand the socket off to  // an appropriate processor if successful  //核心逻辑  if (!endpoint.setSocketOptions(socket)) {  endpoint.closeSocket(socket);  }  } else {  endpoint.destroySocket(socket);  }    state = AcceptorState.ENDED; } //5.类:NioEndpoint protected boolean setSocketOptions(SocketChannel socket) {  // Process the connection  //省略部分代码  try {  // Disable blocking, polling will be used  socket.configureBlocking(false);  Socket sock = socket.socket();  socketProperties.setProperties(sock);  NioSocketWrapper socketWrapper = new NioSocketWrapper(channel, this);  channel.setSocketWrapper(socketWrapper);  socketWrapper.setReadTimeout(getConnectionTimeout());  socketWrapper.setWriteTimeout(getConnectionTimeout());  socketWrapper.setKeepAliveLeft(NioEndpoint.this.getMaxKeepAliveRequests());  socketWrapper.setSecure(isSSLEnabled());  //核心逻辑  poller.register(channel, socketWrapper);  return true;    }

这里可以发现Acceptor主要就是接受socket,然后把它注册到poller中,我们继续看看是如何注册的。

/**6.类NioEndpoint  * Registers a newly created socket with the poller.  *  * @param socket The newly created socket  * @param socketWrapper The socket wrapper  */  public void register(final NioChannel socket, final NioSocketWrapper socketWrapper) {  socketWrapper.interestOps(SelectionKey.OP_READ);//this is what OP_REGISTER turns into.  PollerEvent r = null;  if (eventCache != null) {  r = eventCache.pop();  }  if (r == null) {  r = new PollerEvent(socket, OP_REGISTER);  } else {  r.reset(socket, OP_REGISTER);  }  addEvent(r);  } /** 7.类:PollerEvent implements Runnable  public void run() {  //省略部分代码  socket.getIOChannel().register(socket.getSocketWrapper().getPoller().getSelector(), SelectionKey.OP_READ, socket.getSocketWrapper());  }

这里发现最终就是采用NIO模型把其注册到通道中。(这里涉及NIO网络编程知识,不了解的同学可以传送这里)。那么注册完毕后,我们看看Poller做了什么事情。

*/   /**8.类:NioEndPoint内部类 Poller implements Runnable  **/   @Override  public void run() {  // Loop until destroy() is called  while (true) {  //省略部分代码  Iterator iterator =  keyCount > 0 ? selector.selectedKeys().iterator() : null;  // Walk through the collection of ready keys and dispatch  // any active event.  while (iterator != null && iterator.hasNext()) {  SelectionKey sk = iterator.next();  NioSocketWrapper socketWrapper = (NioSocketWrapper) sk.attachment();  // Attachment may be null if another thread has called  // cancelledKey()  if (socketWrapper == null) {  iterator.remove();  } else {  iterator.remove();  //sock处理  processKey(sk, socketWrapper);  }  }  //省略部分代码  }

这个就是通过selector把之前注册的事件取出来,从而完成了调用。

//9.类: NioEndPoint内部类 Poller implements Runnable  protected void processKey(SelectionKey sk, NioSocketWrapper socketWrapper) {  //省略大部分代码  processSocket(socketWrapper, SocketEvent.OPEN_WRITE, true)   }   //10.类:AbstractEndPoint  public boolean processSocket(SocketWrapperBase socketWrapper,  SocketEvent event, boolean dispatch) {  //省略部分代码  Executor executor = getExecutor();  if (dispatch && executor != null) {  executor.execute(sc);  } else {  sc.run();  }    return true;  }  //11.类:SocketProcessorBase implements Runnable  public final void run() {  synchronized (socketWrapper) {  // It is possible that processing may be triggered for read and  // write at the same time. The sync above makes sure that processing  // does not occur in parallel. The test below ensures that if the  // first event to be processed results in the socket being closed,  // the subsequent events are not processed.  if (socketWrapper.isClosed()) {  return;  }  doRun();  }  }   //类:12.NioEndPoint extends AbstractJsseEndpoint  protected void doRun() {  //省略部分代码  if (handshake == 0) {  SocketState state = SocketState.OPEN;  // Process the request from this socket  if (event == null) {  state = getHandler().process(socketWrapper, SocketEvent.OPEN_READ);  } else {  state = getHandler().process(socketWrapper, event);  }  if (state == SocketState.CLOSED) {  poller.cancelledKey(key, socketWrapper);  }  }  }

Poller调用的run方法或者用Executor线程池去执行run(),最终调用都是各个子EndPoint中的doRun()方法,最终会取一个Handler去处理socketWrapper。继续看源码:

//类:13.AbstractProtocol内部类ConnectionHandler implements AbstractEndpoint.Handler  public SocketState process(SocketWrapperBase wrapper, SocketEvent status) {  //省略部分代码    state = processor.process(wrapper, status);    return SocketState.CLOSED;  }   //类:14.AbstractProcessorLight implements Processor  public SocketState process(SocketWrapperBase socketWrapper, SocketEvent status)  throws IOException {  //省略部分代码    state = service(socketWrapper);    return state;  }

这部分源码表明最终调用的process是通过一个Processor接口的实现类来完成的,这里最终也是会调用到各个子类中,那么这里的处理器其实就是处理应用协议,我们可以查看AbstractProcessorLight的实现类,分别有AjpProcessor、Http11Processor、StreamProcessor,分别代表tomcat支持三种应用层协议,分别是:

  • AJP协议

  • HTTP.1协议

  • HTTP2.0协议

这里我们以常用的HTTP1.1为例,继续看源码:

//类:15. Http11Processor extends AbstractProcessor public SocketState service(SocketWrapperBase socketWrapper)  throws IOException {  //省略大部分代码  getAdapter().service(request, response);  //省略大部分代码   }  //类:16 CoyoteAdapter implements Adapter 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);  postParseSuccess = postParseRequest(req, request, res, response);  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);  }    }

这里我们发现协议处理器最终会调用适配器(CoyoteAdapter),而适配器最终的工作是转换Request和Response对象为HttpServletRequest和HttpServletResponse,从而可以去调用容器,到这里整个连接器的流程和作用我们就已经分析完了。

小结

那么我们来回忆下整个流程,我画了一张时序图来说明:

这张图包含了两个流程,一个是组件的初始化,一个是调用的流程。连接器(Connector)主要初始化了两个组件,ProtcoHandler和EndPoint,但是我们从代码结构发现,他们两个是父子关系,也就是说ProtcoHandler包含了EndPoint。后面的流程就是各个子组件的调用链关系,总结来说就是Acceptor负责接收请求,然后注册到Poller,Poller负责处理请求,然后调用processor处理器来处理,最后把请求转成符合Servlet规范的request和response去调用容器(Container)。点击免费"领取Java架构资料"

我们流程梳理清楚了,接下来我们来结构化的梳理下:

回到连接器(Connector)是源码,我们发现,上述说的模块只有ProtocolHandler和Adapter两个属于连接器中,也就是说,连接器只包含了这两大子模块,那么后续的EndPoint、Acceptor、Poller、Processor都是ProtocolHandler的子模块。 而Acceptor和Poller两个模块的核心功能都是在EndPoint 中完成的,所以是其子模块,而Processor比较独立,所以它和EndPoint是一个级别的子模块。

我们用图来说明下上述的关系:

根据上图我们可以知道,连接器主要负责处理连接请求,然后通过适配器调用容器。那么具体流程细化可以如下:

  • Acceptor监听网络请求,获取请求。

  • Poller获取到监听的请求提交线程池进行处理。

  • Processor根据具体的应用协议(HTTP/AJP)来生成Tomcat Request对象。

  • Adapter把Request对象转换成Servlet标准的Request对象,调用容器。

关于连接器在Tomcat中是怎么设计的就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

代码 连接器 就是 源码 处理 部分 流程 模块 周期 方法 两个 适配器 适配 大部分 对象 接口 线程 容器 核心 注释 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网金融科技 广州大一互联网络科技有限公司 深圳恒合互联网络科技有限公司 南京欣网互联网科技 壕鑫互联网络科技 互联网科技生活的内容 互联网科技公司的市场部门 深圳国际航运科技和互联网 互联网科技实习生岗位职责 互联网科技有哪些公司 湖南一路畅行互联网科技 深圳市优智互联网科技有限公司 链环科技传统企业怎么转型互联网 乌鲁木齐新华智原互联网科技学校 每天都有哪些好的互联网科技 半导体是互联网高科技吗 九思连强互联网电子科技有限公司 信承诺互联网科技有限公司 普陀区互联网教育科技管理办法 东风日产互联网科技公司叫什么 互联网科技 互联网下的科技发展 互联网科技资迅 奇酷互联网科技 掌上梦互联网科技 互联网科技股有哪些 中正互联网科技招聘 互联网实体化科技化 中国科技互联网公司收入 字节跳动和互联网科技
0