【Tomcat源碼分析】從零開(kāi)始理解 HTTP 請(qǐng)求處理
前言
終于步入 Connector 的解析階段,這無(wú)疑是 Tomcat 架構(gòu)中最為復(fù)雜的一環(huán)。作為連接器,它的職責(zé)顯而易見(jiàn)——連接。那么,它連接的究竟是什么呢?
Connector 宛如一座橋梁,將來(lái)自客戶端的請(qǐng)求,經(jīng)過(guò)精心封裝成 Request 和 Response 對(duì)象,傳遞給 Container 進(jìn)行處理。Container 完成業(yè)務(wù)邏輯后,Connector 再將處理后的結(jié)果,通過(guò) Response 對(duì)象返回給遠(yuǎn)方的客戶端。
要深入理解 Connector 的精髓,需要我們從四個(gè)關(guān)鍵問(wèn)題出發(fā),逐一探索。
- Connector 如何接收來(lái)自遠(yuǎn)方的請(qǐng)求?
- 如何將這呼喚化作 Request 和 Response 的身影?
- 封裝后的 Request 和 Response 如何被遞交給 Container 處理?
- Container 處理完畢后,如何將結(jié)果托付給 Connector,并最終送回客戶端手中?
為了更好地理解 Connector 的內(nèi)部運(yùn)作,讓我們先來(lái)欣賞一幅 Connector 結(jié)構(gòu)圖,它將幫助我們更直觀地感受其內(nèi)部的精妙設(shè)計(jì)。
圖片
【注意】:不同的協(xié)議和通信方式,將催生出不同的 ProtocolHandler 實(shí)現(xiàn)。在 Tomcat 8.5 版本中,ProtocolHandler 的類繼承關(guān)系圖譜如下:
圖片
針對(duì)這幅類繼承層級(jí)圖,我們可以做如下解讀:
ajp 和 http11 代表著兩種截然不同的協(xié)議,而 nio、nio2 和 apr 則分別代表著三種不同的通信方式。值得注意的是,協(xié)議與通信方式并非相互獨(dú)立,它們可以靈活組合,以適應(yīng)不同的場(chǎng)景需求。
ProtocolHandler 內(nèi)部,包含著三個(gè)核心部件:Endpoint、Processor 和 Adapter,它們共同協(xié)作,完成請(qǐng)求的接收、處理和響應(yīng)。
- Endpoint 負(fù)責(zé)處理底層的 Socket 網(wǎng)絡(luò)連接,它就像是一位網(wǎng)絡(luò)守衛(wèi),負(fù)責(zé)迎接來(lái)自網(wǎng)絡(luò)的呼喚,并將其轉(zhuǎn)化為可供處理的 Socket 連接。Processor 則肩負(fù)著將 Endpoint 接收到的 Socket 封裝成 Request 對(duì)象的重任,它就像一位翻譯官,將網(wǎng)絡(luò)語(yǔ)言轉(zhuǎn)化為服務(wù)器可以理解的語(yǔ)言。Adapter 則充當(dāng)著連接器,它將 Request 對(duì)象傳遞給 Container,以便 Container 進(jìn)行具體的處理。
- 由于 Endpoint 負(fù)責(zé)處理底層的 Socket 網(wǎng)絡(luò)連接,因此它需要實(shí)現(xiàn) TCP/IP 協(xié)議,而 Processor 則需要實(shí)現(xiàn) HTTP 協(xié)議,以解析 HTTP 請(qǐng)求。Adapter 則將請(qǐng)求適配到 Servlet 容器,使其能夠理解并處理來(lái)自外部的請(qǐng)求。
- Endpoint 的抽象實(shí)現(xiàn)類 AbstractEndpoint 定義了 Acceptor、AsyncTimeout 兩個(gè)內(nèi)部類和一個(gè) Handler 接口。Acceptor 負(fù)責(zé)監(jiān)聽(tīng)來(lái)自網(wǎng)絡(luò)的請(qǐng)求,一旦有新的請(qǐng)求到來(lái),便會(huì)將其捕獲。AsyncTimeout 則負(fù)責(zé)檢查異步 Request 的超時(shí),確保請(qǐng)求在合理的時(shí)間內(nèi)得到處理。Handler 則負(fù)責(zé)處理接收到的 Socket,它將調(diào)用 Processor 進(jìn)行處理,將 Socket 轉(zhuǎn)換為 Request 對(duì)象,并最終傳遞給 Container。
至此,我們已經(jīng)解開(kāi)了 Connector 如何接收請(qǐng)求、如何將請(qǐng)求封裝成 Request 和 Response,以及封裝后的 Request 和 Response 如何被傳遞給 Container 進(jìn)行處理這三個(gè)關(guān)鍵問(wèn)題。而對(duì)于最后一個(gè)問(wèn)題,即 Container 處理完后如何將結(jié)果返回給客戶端,我們將在深入了解 Container 的運(yùn)作機(jī)制后自然明了,前面章節(jié)已對(duì)此進(jìn)行了詳細(xì)的分析。
Connector 源碼分析入口
在 Service 的標(biāo)準(zhǔn)實(shí)現(xiàn) StandardService 的源碼中,我們發(fā)現(xiàn)其 init()、start()、stop() 和 destroy() 方法分別會(huì)對(duì) Connectors 的同名方法進(jìn)行調(diào)用。值得注意的是,一個(gè) Service 通常會(huì)對(duì)應(yīng)多個(gè) Connector,這意味著 Service 的生命周期管理會(huì)影響到所有與其關(guān)聯(lián)的 Connector。
Service.initInternal()
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
if (engine != null) {
engine.init();
}
// Initialize any Executors
for (Executor executor : findExecutors()) {
if (executor instanceof JmxEnabled) {
((JmxEnabled) executor).setDomain(getDomain());
}
executor.init();
}
// Initialize mapper listener
mapperListener.init();
// Initialize our defined Connectors
synchronized (connectorsLock) {
for (Connector connector : connectors) {
try {
connector.init();
} catch (Exception e) {
String message = sm.getString(
"standardService.connector.initFailed", connector);
log.error(message, e);
if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
throw new LifecycleException(message);
}
}
}
}
Service.startInternal()
@Override
protected void startInternal() throws LifecycleException {
if(log.isInfoEnabled())
log.info(sm.getString("standardService.start.name", this.name));
setState(LifecycleState.STARTING);
// Start our defined Container first
if (engine != null) {
synchronized (engine) {
engine.start();
}
}
synchronized (executors) {
for (Executor executor: executors) {
executor.start();
}
}
mapperListener.start();
// Start our defined Connectors second
synchronized (connectorsLock) {
for (Connector connector: connectors) {
try {
// If it has already failed, don't try and start it
if (connector.getState() != LifecycleState.FAILED) {
connector.start();
}
} catch (Exception e) {
log.error(sm.getString(
"standardService.connector.startFailed",
connector), e);
}
}
}
}
正如我們所知,Connector 實(shí)現(xiàn)了 Lifecycle 接口,這使得它成為一個(gè)擁有生命周期的組件。因此,Connector 的啟動(dòng)邏輯入口自然而然地落在 init() 和 start() 方法之中。
Connector 構(gòu)造方法
在深入分析 Connector 的啟動(dòng)邏輯之前,不妨先來(lái)觀摩一下 server.xml 文件。這份文件如同 Tomcat 架構(gòu)的藍(lán)圖,清晰地展現(xiàn)了各個(gè)組件之間的聯(lián)系和布局,為我們理解 Connector 的運(yùn)作提供了一個(gè)宏觀的視角。
<?xml versinotallow='1.0' encoding='utf-8'?>
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<GlobalNamingResources>
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
</Server>
在 server.xml 文件中,我們發(fā)現(xiàn) Connector 擁有多個(gè)關(guān)鍵屬性,其中 port 和 protocol 尤為重要。默認(rèn)情況下,server.xml 支持兩種協(xié)議:HTTP/1.1 和 AJP/1.3。HTTP/1.1 用于支持傳統(tǒng)的 HTTP 1.1 協(xié)議,而 AJP/1.3 則專門用于支持與 Apache 服務(wù)器的通信,為 Apache 服務(wù)器提供一個(gè)與 Tomcat 交互的橋梁。
現(xiàn)在,讓我們將目光轉(zhuǎn)向 Connector 的構(gòu)造方法:
public Connector() {
this(null); // 1. 無(wú)參構(gòu)造方法,傳入?yún)?shù)為空協(xié)議,會(huì)默認(rèn)使用`HTTP/1.1`
}
public Connector(String protocol) {
setProtocol(protocol);
// Instantiate protocol handler
// 5. 使用protocolHandler的類名構(gòu)造ProtocolHandler的實(shí)例
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;
}
if (Globals.STRICT_SERVLET_COMPLIANCE) {
uriCharset = StandardCharsets.ISO_8859_1;
} else {
uriCharset = StandardCharsets.UTF_8;
}
}
@Deprecated
public void setProtocol(String protocol) {
boolean aprConnector = AprLifecycleListener.isAprAvailable() &&
AprLifecycleListener.getUseAprConnector();
// 2. `HTTP/1.1`或`null`,protocolHandler使用`org.apache.coyote.http11.Http11NioProtocol`,不考慮apr
if ("HTTP/1.1".equals(protocol) || protocol == null) {
if (aprConnector) {
setProtocolHandlerClassName("org.apache.coyote.http11.Http11AprProtocol");
} else {
setProtocolHandlerClassName("org.apache.coyote.http11.Http11NioProtocol");
}
}
// 3. `AJP/1.3`,protocolHandler使用`org.apache.coyote.ajp.AjpNioProtocol`,不考慮apr
else if ("AJP/1.3".equals(protocol)) {
if (aprConnector) {
setProtocolHandlerClassName("org.apache.coyote.ajp.AjpAprProtocol");
} else {
setProtocolHandlerClassName("org.apache.coyote.ajp.AjpNioProtocol");
}
}
// 4. 其他情況,使用傳入的protocol作為protocolHandler的類名
else {
setProtocolHandlerClassName(protocol);
}
}
在 Connector 的構(gòu)造方法中,我們發(fā)現(xiàn)它主要完成了以下幾項(xiàng)工作:
- 當(dāng)傳入的參數(shù)為空協(xié)議時(shí),它會(huì)默認(rèn)使用 HTTP/1.1 協(xié)議。
- 當(dāng)傳入的協(xié)議為 HTTP/1.1 或 null 時(shí),它會(huì)選擇 org.apache.coyote.http11.Http11NioProtocol 作為 ProtocolHandler,并忽略 apr 選項(xiàng)。
- 當(dāng)傳入的協(xié)議為 AJP/1.3 時(shí),它會(huì)選擇 org.apache.coyote.ajp.AjpNioProtocol 作為 ProtocolHandler,同樣忽略 apr 選項(xiàng)。
- 對(duì)于其他情況,它會(huì)直接使用傳入的 protocol 作為 ProtocolHandler 的類名。
- 最后,它會(huì)使用 ProtocolHandler 的類名來(lái)構(gòu)造 ProtocolHandler 的實(shí)例。
Connector.initInternal()
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
// Initialize adapter
// 1. 初始化adapter
adapter = new CoyoteAdapter(this);
protocolHandler.setAdapter(adapter);
// Make sure parseBodyMethodsSet has a default
// 2. 設(shè)置接受body的method列表,默認(rèn)為POST
if (null == parseBodyMethodsSet) {
setParseBodyMethods(getParseBodyMethods());
}
if (protocolHandler.isAprRequired() && !AprLifecycleListener.isAprAvailable()) {
throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoApr",
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());
}
}
// 3. 初始化protocolHandler
try {
protocolHandler.init();
} catch (Exception e) {
throw new LifecycleException(
sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
}
}
Connector 的 init() 方法主要完成了三項(xiàng)重要的初始化工作:
- 初始化 adapter:Adapter 負(fù)責(zé)將請(qǐng)求傳遞給 Container,因此需要在 init() 方法中完成初始化,以便后續(xù)能夠正常地將請(qǐng)求傳遞給 Container 進(jìn)行處理。
- 設(shè)置接受 body 的 method 列表:默認(rèn)情況下,Connector 只允許 POST 方法提交 body 數(shù)據(jù),但在某些情況下,可能需要允許其他方法提交 body 數(shù)據(jù),因此需要在 init() 方法中設(shè)置允許提交 body 的方法列表。
- 初始化 protocolHandler:ProtocolHandler 是 Connector 的核心組件,負(fù)責(zé)處理請(qǐng)求和響應(yīng),因此需要在 init() 方法中完成 protocolHandler 的初始化,以便后續(xù)能夠正常地處理請(qǐng)求和響應(yīng)。
從 ProtocolHandler 的類繼承層級(jí)關(guān)系圖 中,我們可以看到 ProtocolHandler 的子類都必須實(shí)現(xiàn) AbstractProtocol 抽象類。而 protocolHandler.init(); 方法的具體實(shí)現(xiàn)則取決于具體的 ProtocolHandler 子類,它會(huì)根據(jù)不同的協(xié)議和通信方式進(jìn)行相應(yīng)的初始化操作。
代碼正是在這個(gè)抽象類里面。我們來(lái)分析一下。
@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) {
rgOname = new ObjectName(domain + ":type=GlobalRequestProcessor,name=" + getName());
Registry.getRegistry(null, null).registerComponent(
getHandler().getGlobal(), rgOname, null);
}
// 1. 設(shè)置endpoint的名字,默認(rèn)為:http-nio-{port}
String endpointName = getName();
endpoint.setName(endpointName.substring(1, endpointName.length()-1));
endpoint.setDomain(domain);
// 2. 初始化endpoint
endpoint.init();
}
接下來(lái),讓我們一同探究 Endpoint.init() 方法的內(nèi)部。它位于 AbstractEndpoint 抽象類中,采用模板方法模式,巧妙地將核心邏輯委托給子類的 bind() 方法。
public abstract void bind() throws Exception;
public abstract void unbind() throws Exception;
public abstract void startInternal() throws Exception;
public abstract void stopInternal() throws Exception;
public void init() throws Exception {
// 執(zhí)行bind()方法
if (bindOnInit) {
bind();
bindState = BindState.BOUND_ON_INIT;
}
if (this.domain != null) {
// Register endpoint (as ThreadPool - historical name)
oname = new ObjectName(domain + ":type=ThreadPool,name=\"" + getName() + "\"");
Registry.getRegistry(null, null).registerComponent(this, oname, null);
ObjectName socketPropertiesOname = new ObjectName(domain +
":type=ThreadPool,name=\"" + getName() + "\",subType=SocketProperties");
socketProperties.setObjectName(socketPropertiesOname);
Registry.getRegistry(null, null).registerComponent(socketProperties, socketPropertiesOname, null);
for (SSLHostConfig sslHostConfig : findSslHostConfigs()) {
registerJmx(sslHostConfig);
}
}
}
繼續(xù)追尋著代碼的蹤跡,我們終于來(lái)到了 bind() 方法,它揭示了 Connector 初始化的精髓所在。關(guān)鍵的代碼片段 serverSock.socket().bind(addr, getAcceptCount());用于 將 ServerSocket 綁定到指定的 IP 地址和端口
@Override
public void bind() throws Exception {
if (!getUseInheritedChannel()) {
serverSock = ServerSocketChannel.open();
socketProperties.setProperties(serverSock.socket());
InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));
//綁定ServerSocket到指定的IP和端口
serverSock.socket().bind(addr,getAcceptCount());
} else {
// Retrieve the channel provided by the OS
Channel ic = System.inheritedChannel();
if (ic instanceof ServerSocketChannel) {
serverSock = (ServerSocketChannel) ic;
}
if (serverSock == null) {
throw new IllegalArgumentException(sm.getString("endpoint.init.bind.inherited"));
}
}
serverSock.configureBlocking(true); //mimic APR behavior
// Initialize thread count defaults for acceptor, poller
if (acceptorThreadCount == 0) {
// FIXME: Doesn't seem to work that well with multiple accept threads
acceptorThreadCount = 1;
}
if (pollerThreadCount <= 0) {
//minimum one poller thread
pollerThreadCount = 1;
}
setStopLatch(new CountDownLatch(pollerThreadCount));
// Initialize SSL if needed
initialiseSsl();
selectorPool.open();
}
至此,我們已將 Connector 的 init() 方法剖析完畢,接下來(lái),讓我們將目光轉(zhuǎn)向 start() 方法。start() 方法的核心邏輯,僅僅是簡(jiǎn)潔的一行代碼:調(diào)用 ProtocolHandler.start() 方法,將 Connector 的啟動(dòng)大任委托給 ProtocolHandler。
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);
}
}
現(xiàn)在,讓我們深入 ProtocolHandler.start() 方法,探索啟動(dòng)過(guò)程中的關(guān)鍵步驟。它首先會(huì)調(diào)用 Endpoint.start() 方法,啟動(dòng) Endpoint,以便監(jiān)聽(tīng)來(lái)自網(wǎng)絡(luò)的請(qǐng)求。接著,它會(huì)開(kāi)啟異步超時(shí)線程,負(fù)責(zé)監(jiān)控異步請(qǐng)求的超時(shí)情況。該線程的執(zhí)行單元為 AsyncTimeout。
@Override
public void start() throws Exception {
if (getLog().isInfoEnabled()) {
getLog().info(sm.getString("abstractProtocolHandler.start", getName()));
}
// 1. 調(diào)用`Endpoint.start()`方法
endpoint.start();
// Start async timeout thread
// 2. 開(kāi)啟異步超時(shí)線程,線程執(zhí)行單元為`Asynctimeout`
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();
}
現(xiàn)在,我們將注意力集中在 Endpoint.start() 方法,它負(fù)責(zé)啟動(dòng) Endpoint,為 Connector 迎接來(lái)自網(wǎng)絡(luò)的請(qǐng)求做好準(zhǔn)備。
public final void start() throws Exception {
// 1. `bind()`已經(jīng)在`init()`中分析過(guò)了
if (bindState == BindState.UNBOUND) {
bind();
bindState = BindState.BOUND_ON_START;
}
startInternal();
}
@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
// 2. 創(chuàng)建工作者線程池
if ( getExecutor() == null ) {
createExecutor();
}
// 3. 初始化連接latch,用于限制請(qǐng)求的并發(fā)量
initializeConnectionLatch();
// Start poller threads
// 4. 開(kāi)啟poller線程。poller用于對(duì)接受者線程生產(chǎn)的消息(或事件)進(jìn)行處理,poller最終調(diào)用的是Handler的代碼
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();
}
// 5. 開(kāi)啟acceptor線程
startAcceptorThreads();
}
}
protected final void startAcceptorThreads() {
int count = getAcceptorThreadCount();
acceptors = new Acceptor[count];
for (int i = 0; i < count; i++) {
acceptors[i] = createAcceptor();
String threadName = getName() + "-Acceptor-" + i;
acceptors[i].setThreadName(threadName);
Thread t = new Thread(acceptors[i], threadName);
t.setPriority(getAcceptorThreadPriority());
t.setDaemon(getDaemon());
t.start();
}
}
在 Endpoint.start() 方法中,我們首先會(huì)調(diào)用 bind() 方法,完成 Socket 的綁定,確保 Connector 能夠監(jiān)聽(tīng)來(lái)自網(wǎng)絡(luò)的請(qǐng)求。接著,我們會(huì)創(chuàng)建工作者線程池,為后續(xù)處理請(qǐng)求提供充足的線程資源。隨后,我們會(huì)初始化連接 latch,用于限制請(qǐng)求的并發(fā)量,避免過(guò)多的請(qǐng)求涌入,造成系統(tǒng)崩潰。
接下來(lái),我們會(huì)創(chuàng)建一個(gè)輪詢 Poller 線程,負(fù)責(zé)處理來(lái)自 Acceptor 線程的事件,并將處理后的事件傳遞給 Handler。Poller 線程會(huì)調(diào)用 Handler 的代碼進(jìn)行處理,最終完成對(duì)請(qǐng)求的處理。最后,我們會(huì)創(chuàng)建一個(gè) Acceptor 線程,專門負(fù)責(zé)監(jiān)聽(tīng)網(wǎng)絡(luò)請(qǐng)求,并將接收到的請(qǐng)求傳遞給 Poller 線程進(jìn)行處理。
至此,我們已將 Connector 源碼入口的分析告一段落,揭開(kāi)了 Connector 啟動(dòng)過(guò)程的神秘面紗。接下來(lái)我們將繼續(xù)深入探索 Connector 的請(qǐng)求邏輯,深入理解 Connector 如何接收請(qǐng)求,如何將請(qǐng)求封裝成 Request 和 Response 對(duì)象,以及如何將這些對(duì)象傳遞給 Container 進(jìn)行處理。讓我們一起探索 Tomcat 的內(nèi)部世界。