Tomcat處理連接請求的模式:
- BIO:阻塞模型
- NIO:非阻塞模型
- APR: 高性能埃儿,可擴(kuò)展的模式制市,Tomcat8版本默認(rèn)模式
Tomcat連接器(Connector)是處理請求的主要組件,它負(fù)責(zé)接收請求嘉冒,創(chuàng)建Request和Response對象用于和前端進(jìn)行數(shù)據(jù)的交換亿汞;然后分配線程讓Servlet容器來處理這個請求亮垫,并把產(chǎn)生的Request和Response對象傳給Servlet容器。當(dāng)Engine處理完請求后躺涝,也會通過Connector將結(jié)果返回給請求端厨钻。即Connector進(jìn)行請求的調(diào)度和控制。
根據(jù)協(xié)議的不同诞挨,可以分為Http Connector和AJP Connector莉撇,兩種協(xié)議的不同可以看這篇博文
一、NIO惶傻、BIO棍郎、APR
1. Connector的模式
Connector在處理HTTP請求時,會使用不同的模式银室。不同版本的Tomcat支持的模式不行同(Tomcat8+版本支持NIO以及APR去掉了對BIO的支持)
BIO與NIO大家都是比較熟悉的涂佃,APR是Apache Portable Runtime,是Apache可移植運(yùn)行庫蜈敢,利用本地庫可以實(shí)現(xiàn)高擴(kuò)展性辜荠、高性能;APR是在Tomcat上運(yùn)行高并發(fā)應(yīng)用的首選模式抓狭,但是需要安裝apr伯病、apr-utils、tomcat-native等包
2. Connector的請求流程
Connector啟動后會啟動三種線程組用于不同階段的處理:
- Acceptor線程組否过。用于接收新連接午笛,并將新連接封裝以下,選擇一個Poller將新連接添加到Poller的事件隊(duì)列中
- Poller線程組苗桂。用于監(jiān)聽Socket事件药磺,當(dāng)Socket可讀或可寫時,將Socket封裝一下添加到worker線程池的任務(wù)隊(duì)列中
- Worker線程組煤伟。用于對請求進(jìn)行處理癌佩,包括分析請求報文并創(chuàng)建Request對象,調(diào)用容器的pipeline
Acceptor便锨、Poller围辙、Worker的線程池(ThreadPoolExcutor)均維護(hù)在NioEndPoint中
Connector的初始化及啟動
- initServerSocket(),通過ServerSocketChannel.open()打開一個ServerSocket,默認(rèn)綁定到8080端口放案,默認(rèn)的連接等待隊(duì)列長度為100姚建,當(dāng)超過100時會拒絕服務(wù)。我們可以通過在conf/server.xml中的Connector的acceptCount屬性對其進(jìn)行設(shè)置卿叽。
- createExecutor()用于創(chuàng)建Worker線程池桥胞。默認(rèn)會啟動10個Worker線程,Tomcat處理請求過程中考婴,Worker最多不超過200個贩虾。我們可以通過配置conf/server.xml中的Connector的minSpareThreads 和 maxThreads 對這兩個屬性進(jìn)行定制。
- Poller用于檢測已經(jīng)就緒的Socket沥阱,默認(rèn)不超過2個線程缎罢,我們可以通過配置 pollerThreadCount 設(shè)置。
4.Acceptor用于接收新的連接考杉,默認(rèn)1個線程策精,我們可以通過配置 acceptorThreadCount 對其進(jìn)行設(shè)置。
Acceptor請求過程
- Acceptor在啟動后會阻塞在ServerSocketChannel.accept()處崇棠,當(dāng)有新連接到達(dá)時咽袜,該方法返回一個SocketChannel
2.配置完Socket以后會將Socket封裝到NioChannel中,并注冊到 Poller,由于我們一開始就啟動了多個 Poller 線程枕稀,注冊的時候询刹,連接是公平的分配到每個 Poller 的。
3.addEvent() 方法會將 Socket 添加到該 Poller 的 PollerEvent 隊(duì)列中萎坷。到此 Acceptor 的任務(wù)就完成了凹联。
Poller過程
- selector.select(1000)。當(dāng) Poller 啟動后因?yàn)?selector 中并沒有已注冊的 Channel哆档,所以當(dāng)執(zhí)行到該方法時只能阻塞蔽挠。所有的 Poller 共用一個 Selector,其實(shí)現(xiàn)類是 sun.nio.ch.EPollSelectorImpl
2.events() 方法會將通過 addEvent() 方法添加到事件隊(duì)列中的 Socket 注冊到 EPollSelectorImpl瓜浸,當(dāng) Socket 可讀時澳淑,Poller 才對其進(jìn)行處理
3.createSocketProcessor() 方法將 Socket 封裝到 SocketProcessor 中,SocketProcessor 實(shí)現(xiàn)了 Runnable 接口斟叼。worker 線程通過調(diào)用其 run() 方法來對 Socket 進(jìn)行處理偶惠。
4.execute(SocketProcessor) 方法將 SocketProcessor 提交到線程池,放入線程池的 workQueue 中朗涩。workQueue 是 BlockingQueue 的實(shí)例忽孽。到此 Poller 的任務(wù)就完成了。
Worker過程
- worker 線程被創(chuàng)建以后就執(zhí)行 ThreadPoolExecutor 的 runWorker() 方法谢床,試圖從 workQueue 中取待處理任務(wù)兄一,但是一開始 workQueue 是空的,所以 worker 線程會阻塞在 workQueue.take() 方法识腿。
- 當(dāng)新任務(wù)添加到 workQueue后出革,workQueue.take() 方法會返回一個 Runnable,通常是 SocketProcessor,然后 worker 線程調(diào)用 SocketProcessor 的 run() 方法對 Socket 進(jìn)行處理渡讼。
- createProcessor() 會創(chuàng)建一個 Http11Processor, 它用來解析 Socket骂束,將 Socket 中的內(nèi)容封裝到 Request 中耳璧。注意這個 Request 是臨時使用的一個類,它的全類名是 org.apache.coyote.Request
- postParseRequest() 方法封裝一下 Request展箱,并處理一下映射關(guān)系(從 URL 映射到相應(yīng)的 Host旨枯、Context、Wrapper)混驰。
參考:談?wù)?Tomcat 請求處理流程(http://www.importnew.com/27729.html)