Tomcat 处理 HTTP 请求源码分析(下)【转】
原文地址:https://www.infoq.cn/article/zh-tomcat-http-request-2
很多開源應用服務器都是集成 tomcat 作為 web container 的,而且對于 tomcat 的 servlet container 這部分代碼很少改動。這樣,這些應用服務器的性能基本上就取決于 Tomcat 處理 HTTP 請求的 connector 模塊的性能。本文首先從應用層次分析了 tomcat 所有的 connector 種類及用法,接著從架構上分析了 connector 模塊在整個 tomcat 中所處的位置,最后對 connector 做了詳細的源代碼分析。并且我們以 Http11NioProtocol 為例詳細說明了 tomcat 是如何通過實現 ProtocolHandler 接口而構建 connector 的。
上篇地址為《Tomcat 處理 HTTP 請求源碼分析(上)》 ,本文是系列下篇。
4 如何實現 Connector
由上面的介紹我們可以知道,實現 Connector 就是實現 ProtocolHander 接口的過程。
AjpAprProtocol、AjpProtocol、Http11AprProtocol、Http11Protocol、JkCoyoteHandler、MemoryProtocolHandler 這些實現類的實現流程與 Http11NioProtocol 相同,下面我們以 Http11NioProtocol 為類重點說明 tomcat 中如何實現 ProtocolHander 接口的。
Http11NioProtocol 實現了 ProtocolHander 接口,它將所有的操作委托給 NioEndpoint 類去做,如下圖:
NioEndpoint 類中的 init 方法中首先以普通阻塞方式啟動了 SocketServer:
NioEndpoint 類的 start 方法是關鍵,如下:
可以看出,在 start 方法中啟動了兩個線程和一個線程池:
- Acceptor 線程,該線程以普通阻塞方式接收客戶端請求(socket.accep()),將客戶 Socket 交由線程池是處理,線程池要將該 Socket 配置成非阻塞模式(socket.configureBlocking(false)), 并且向 Selector 注冊 READ 事件。該線程數目可配置,默認為 1 個。
- Poller 線程,由于 Acceptor 委托線程為客戶端 Socket 注冊了 READ 事件,當 READ 準備好時,就會進入 Poller 線程的循環,Poller 線程也是委托線程池去做,線程池將 NioChannel 加入到 ConcurrentLinkedQueue<NioChannel> 隊列中。該線程數目可配置,默認為 1 個。
- 線程池,就是上面說的做 Acceptor 與 Poller 線程委托要做的事情。
4.1 Init 接口實現方法中阻塞方式啟動 ServerSocketChannel
在 Init 接口實現方法中阻塞方式啟動 ServerSocketChannel。
4.2 Start 接口實現方法中啟動所有線程
Start 方法中啟動了線程池,acceptor 線程與 Poller 線程。其中 acceptor 與 poller 線程一般數目為 1,當然,數目也可配置。
可以看出,線程池有兩種實現方式:
- 普通 queue + wait + notify 方式,默認使用的方式,據說實際測試這種比下種效率高
- JDK1.5 自帶的線程池方式
4.3 Acceptor 線程接收客戶請求、注冊 READ 事件
在 Acceptor 線程中接收了客戶請求,同時委托線程池注冊 READ 事件。
在 setSocketOptions 方法中,首先將 socket 配置成非阻塞模式:
在 setSocketOptions 方法中,最后調用 getPoller0().register(channel); 一句為 SocketChannel 注冊 READ 事件,register 方法代碼如下 (注意:這是 Poller 線程的方法):
其中 attachment 的結構如下,它可以看做是一個共享的數據結構:
4.4 Poller線程讀請求、生成響應數據、注冊 WRITE 事件
注意:
- 調用了 hanler.process(socket) 來生成響應數據)
- 數據生成完之后,注冊 WRITE 事件的,代碼如下:
4.5 Handle 接口實現類通過 Adpater 調用 Servlet 容器生成響應數據
NioEndpoint 類中的 Handler 接口定義如下:
其中 process 方法通過 Adapter 來調用 Servlet Container 生成返回結果。Adapter 接口定義如下:
4.6 小結
實現一個 tomcat 連接器 Connector 就是實現 ProtocolHander 接口的過程。Connector 用來接收 Socket Client 端的請求,通過內置的線程池去調用 Servlet Container 生成響應結果,并將響應結果同步或異步的返回給 Socket Client。在第三方應用集成 tomcat 作為 Web 容器時,一般不會動 Servlet Container 端的代碼,那么 connector 的性能將是整個 Web 容器性能的關鍵。
轉載于:https://www.cnblogs.com/davidwang456/articles/11253524.html
總結
以上是生活随笔為你收集整理的Tomcat 处理 HTTP 请求源码分析(下)【转】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Tomcat 处理 HTTP 请求源码分
- 下一篇: JVM 生态系统 2018 调查报道