Tomcat 第四篇:请求处理流程(上) (2)

ProtocolHandler 的初始化稍微有些特殊,Server、Service、Connector 这三个容器的初始化顺序为: Server -> Service -> Connector 。值得注意的是, ProtocolHandler 作为 Connector 的子容器,其初始化过程并不是由 Connector 的 initInternal 方法调用的,而是与启动过程一道被 Connector 的 startInternal 方法所调用。

@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) { throw new LifecycleException( sm.getString("coyoteConnector.protocolHandlerStartFailed"), e); } }

这里也总共做了两件事儿:

将 Connector 容器的状态更改为启动中(LifecycleState.STARTING) 。

启动 ProtocolHandler 。

简单起见,以 Http11Protocol 为例介绍 ProtocolHandler 的 start 方法:

由于 ProtocolHandler 是一个接口,它的 start 方法有两个抽象类进行实现:

org.apache.coyote.AbstractAjpProtocol

org.apache.coyote.ajp.AbstractProtocol

这里我们仅讨论 org.apache.coyote.ajp.AbstractProtocol ,看下它的 start 的方法:

@Override public void start() throws Exception { if (getLog().isInfoEnabled()) { getLog().info(sm.getString("abstractProtocolHandler.start", getName())); } endpoint.start(); // Start timeout thread asyncTimeout = new AsyncTimeout(); Thread timeoutThread = new Thread(asyncTimeout, getNameInternal() + "-AsyncTimeout"); int priority = endpoint.getThreadPriority(); if (priority < Thread.MIN_PRIORITY || priority > Thread.MAX_PRIORITY) { priority = Thread.NORM_PRIORITY; } timeoutThread.setPriority(priority); timeoutThread.setDaemon(true); timeoutThread.start(); }

这里最核心的一句代码是调用了 endpoint.start() ,这里的 endpoint 是抽象类 AbstractEndpoint#start() :

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

这一段也做了两件事儿:

判断当前绑定状态,如果没有绑定,则会先去绑定,调用 bind() 。

然后调用 startInternal() 进行初始化。

AbstractEndpoint 有三个子类:

Tomcat 第四篇:请求处理流程(上)

我们专注于 AprEndpoint 这个子类,上面 AbstractEndpoint 抽象类中的两个方法 bind() 和 startInternal() 都会在这个类中进行实现。

我们先看 bind() 方法:

/** * Initialize the endpoint. */ @Override public void bind() throws Exception { // Create the root APR memory pool try { rootPool = Pool.create(0); } catch (UnsatisfiedLinkError e) { throw new Exception(sm.getString("endpoint.init.notavail")); } // Create the pool for the server socket serverSockPool = Pool.create(rootPool); // Create the APR address that will be bound String addressStr = null; if (getAddress() != null) { addressStr = getAddress().getHostAddress(); } int family = Socket.APR_INET; if (Library.APR_HAVE_IPV6) { if (addressStr == null) { if (!OS.IS_BSD) { family = Socket.APR_UNSPEC; } } else if (addressStr.indexOf(':') >= 0) { family = Socket.APR_UNSPEC; } } long inetAddress = Address.info(addressStr, family, getPort(), 0, rootPool); // Create the APR server socket serverSock = Socket.create(Address.getInfo(inetAddress).family, Socket.SOCK_STREAM, Socket.APR_PROTO_TCP, rootPool); if (OS.IS_UNIX) { Socket.optSet(serverSock, Socket.APR_SO_REUSEADDR, 1); } if (Library.APR_HAVE_IPV6) { if (getIpv6v6only()) { Socket.optSet(serverSock, Socket.APR_IPV6_V6ONLY, 1); } else { Socket.optSet(serverSock, Socket.APR_IPV6_V6ONLY, 0); } } // Deal with the firewalls that tend to drop the inactive sockets Socket.optSet(serverSock, Socket.APR_SO_KEEPALIVE, 1); // Bind the server socket int ret = Socket.bind(serverSock, inetAddress); if (ret != 0) { throw new Exception(sm.getString("endpoint.init.bind", "" + ret, Error.strerror(ret))); } // Start listening on the server socket ret = Socket.listen(serverSock, getAcceptCount()); if (ret != 0) { throw new Exception(sm.getString("endpoint.init.listen", "" + ret, Error.strerror(ret))); } if (OS.IS_WIN32 || OS.IS_WIN64) { // On Windows set the reuseaddr flag after the bind/listen Socket.optSet(serverSock, Socket.APR_SO_REUSEADDR, 1); } // Enable Sendfile by default if it has not been configured but usage on // systems which don't support it cause major problems if (!useSendFileSet) { setUseSendfileInternal(Library.APR_HAS_SENDFILE); } else if (getUseSendfile() && !Library.APR_HAS_SENDFILE) { setUseSendfileInternal(false); } // Initialize thread count default for acceptor if (acceptorThreadCount == 0) { // FIXME: Doesn't seem to work that well with multiple accept threads acceptorThreadCount = 1; } // Delay accepting of new connections until data is available // Only Linux kernels 2.4 + have that implemented // on other platforms this call is noop and will return APR_ENOTIMPL. if (deferAccept) { if (Socket.optSet(serverSock, Socket.APR_TCP_DEFER_ACCEPT, 1) == Status.APR_ENOTIMPL) { deferAccept = false; } } // Initialize SSL if needed if (isSSLEnabled()) { for (SSLHostConfig sslHostConfig : sslHostConfigs.values()) { createSSLContext(sslHostConfig); } SSLHostConfig defaultSSLHostConfig = sslHostConfigs.get(getDefaultSSLHostConfigName()); if (defaultSSLHostConfig == null) { throw new IllegalArgumentException(sm.getString("endpoint.noSslHostConfig", getDefaultSSLHostConfigName(), getName())); } Long defaultSSLContext = defaultSSLHostConfig.getOpenSslContext(); sslContext = defaultSSLContext.longValue(); SSLContext.registerDefault(defaultSSLContext, this); // For now, sendfile is not supported with SSL if (getUseSendfile()) { setUseSendfileInternal(false); if (useSendFileSet) { log.warn(sm.getString("endpoint.apr.noSendfileWithSSL")); } } } }

这个方法上面的注释已经写的比较清楚了,首先第一个方法注释就告诉我们这个方法是用来初始化 endpoint 的,大体做了这么几件事儿:

创建了 APR 的 rootPool ,从命名上看这应该是一个根连接池。

创建一个 serverSockPool ,使用刚才创建的 rootPool 进行创建,这个命名大家就都看得懂了。

创建用来做绑定的 APR 的地址。

创建一个 APR server socket -> serverSock ,这里开启了 socket 。

将刚才创建的 server 和 socket 进行绑定。

开启 server socket 上面的监听。

一些系统层面的设置。

如果需要的话,还会进行一些 SSL 的相关设置。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyjfgw.html