Jetty 的基本架構


Jetty的基本架構

 

jetty的主要組件的類圖

整個 Jetty 的核心是圍繞着 Server 類來構建,Server 類繼承了 Handler,關聯了 Connector 和 Container。Container 是管理 Mbean 的容器。

Jetty 的 Server 的擴展主要是實現一個個 Handler 並將 Handler 加到 Server 中,Server 中提供了調用這些 Handler 的訪問規則。

整個 Jetty 的所有組件的生命周期管理是基於觀察者模板設計,它和 Tomcat 的管理是類似的。下面是 LifeCycle 的類關系圖

Handler 的體系結構

Jetty 主要提供了兩種 Handler 類型,一種是 HandlerWrapper,它可以將一個 Handler 委托給另外一個類去執行,如我們要將一個 Handler 加到 Jetty 中,那么就必須將這個 Handler 委托給 Server 去調用。配合 ScopeHandler 類我們可以攔截 Handler 的執行,在調用 Handler 之前或之后,可以做一些另外的事情,類似於 Tomcat 中的 Valve;

另外一個 Handler 類型是 HandlerCollection,這個 Handler 類可以將多個 Handler 組裝在一起,構成一個 Handler 鏈,方便我們做擴展。

Jetty 的啟動過程

Jetty 的入口是 Server 類,Server 類啟動完成了,就代表 Jetty 能為你提供服務了。它到底能提供哪些服務,就要看 Server 類啟動時都調用了其它組件的 start 方法。從 Jetty 的配置文件我們可以發現,配置 Jetty 的過程就是將那些類配置到 Server 的過程。下面是 Jetty 的啟動時序圖:

 

因為 Jetty 中所有的組件都會繼承 LifeCycle,所以 Server 的 start 方法調用就會調用所有已經注冊到 Server 的組件,

Server 啟動其它組件的順序是:

首先啟動設置到 Server 的 Handler,通常這個 Handler 會有很多子 Handler,這些 Handler 將組成一個 Handler 鏈。Server 會依次啟動這個鏈上的所有 Handler。

接着會啟動注冊在 Server 上 JMX 的 Mbean,讓 Mbean 也一起工作起來。

最后會啟動 Connector,打開端口,接受客戶端請求,啟動邏輯非常簡單。

接受請求

Jetty 作為一個獨立的 Servlet 引擎可以獨立提供 Web 服務,基於HTTP協議,也可以與其他 Web 應用服務器集成,基於 AJP 協議。

基於 HTTP 協議工作

如果前端沒有其它 web 服務器,那么 Jetty 應該是基於 HTTP 協議工作。也就是當 Jetty 接收到一個請求時,必須要按照 HTTP 協議解析請求和封裝返回的數據。

我們設置 Jetty 的 Connector 實現類為 org.eclipse.jetty.server.bi.SocketConnector 讓 Jetty 以 BIO 的方式工作,Jetty 在啟動時將會創建 BIO 的工作環境,它會創建 HttpConnection 類用來解析和封裝 HTTP1.1 的協議。

ConnectorEndPoint 類是以 BIO 的處理方式處理連接請求。

ServerSocket 是建立 socket 連接接受和傳送數據。

Executor 是處理連接的線程池,它負責處理每一個請求隊列中任務。acceptorThread 是監聽連接請求,一有 socket 連接,它將進入下面的處理流程。

當 socket 被真正執行時,HttpConnection 將被調用,這里定義了如何將請求傳遞到 servlet 容器里,有如何將請求最終路由到目的 servlet,關於這個細節可以參考《 servlet 工作原理解析》一文。

下圖是 Jetty 啟動創建建立連接的時序圖:

Jetty 創建接受連接環境需要三個步驟:

  1. 創建一個隊列線程池,用於處理每個建立連接產生的任務,這個線程池可以由用戶來指定,這個和 Tomcat 是類似的。
  2. 創建 ServerSocket,用於准備接受客戶端的 socket 請求,以及客戶端用來包裝這個 socket 的一些輔助類。
  3. 創建一個或多個監聽線程,用來監聽訪問端口是否有連接進來。

 

 

Accetptor 線程將會為這個請求創建 ConnectorEndPoint。HttpConnection 用來表示這個連接是一個 HTTP 協議的連接,它會創建 HttpParse 類解析 HTTP 協議,並且會創建符合 HTTP 協議的 Request 和 Response 對象。接下去就是將這個線程交給隊列線程池去執行了。

基於 AJP 工作

通常一個 web 服務站點的后端服務器不是將 Java 的應用服務器直接暴露給服務訪問者,而是在應用服務器,如 Jboss 的前面在加一個 web 服務器,如 Apache 或者 nginx,我想這個原因大家應該很容易理解,如做日志分析、負載均衡、權限控制、防止惡意請求以及靜態資源預加載等等。

下圖是通常的 web 服務端的架構圖:

這種架構下 servlet 引擎就不需要解析和封裝返回的 HTTP 協議,因為 HTTP 協議的解析工作已經在 Apache 或 Nginx 服務器上完成了,Jboss 只要基於更加簡單的 AJP 協議工作就行了,這樣能加快請求的響應速度。

類 Ajp13Parserer 而不是 HttpParser

需要配置 connector 的實現類為 Ajp13SocketConnector

將 SocketConnector 類替換成了 Ajp13SocketConnector

基於 NIO 方式工作

Selector selector = Selector.open(); 
ServerSocketChannel ssc = ServerSocketChannel.open(); 
ssc.configureBlocking( false ); 
SelectionKey key = ssc.register( selector, SelectionKey.OP_ACCEPT ); 
ServerSocketChannel ss = (ServerSocketChannel)key.channel(); 
SocketChannel sc = ss.accept(); 
sc.configureBlocking( false ); 
SelectionKey newKey = sc.register( selector, SelectionKey.OP_READ ); 
Set selectedKeys = selector.selectedKeys();

后遍歷這個觀察者觀察到事件,取出感興趣的事件再處理。這里有個最核心的地方就是,我們不需要為每個被觀察者創建一個線程來監控它隨時發生的事件。而是把這些被觀察者都注冊一個地方統一管理,然后由它把觸發的事件統一發送給感興趣的程序模塊。這里的核心是能夠統一的管理每個被觀察者的事件,所以我們就可以把服務端上每個建立的連接傳送和接受數據作為一個事件統一管理,這樣就不必要每個連接需要一個線程來維護了。

下面是 Jetty 的 NIO 處理時序圖:

處理請求

實際上 Jetty 的工作方式非常簡單,當 Jetty 接受到一個請求時,Jetty 就把這個請求交給在 Server 中注冊的代理 Handler 去執行,如何執行你注冊的 Handler,同樣由你去規定,Jetty 要做的就是調用你注冊的第一個 Handler 的 handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) 方法,接下去要怎么做,完全由你決定。

要能接受一個 web 請求訪問,首先要創建一個 ContextHandler,如下代碼所示:

Server server = new Server(8080); 
ContextHandler context = new ContextHandler(); 
context.setContextPath("/"); 
context.setResourceBase("."); 
context.setClassLoader(Thread.currentThread().getContextClassLoader()); 
server.setHandler(context); 
context.setHandler(new HelloHandler()); 
server.start(); 
server.join();

當我們在瀏覽器里敲入 http://localhost:8080 時的請求將會代理到 Server 類的 handle 方法,Server 的 handle 的方法將請求代理給 ContextHandler 的 handle 方法,ContextHandler 又調用 HelloHandler 的 handle 方法。這個調用方式是不是和 Servlet 的工作方式類似,在啟動之前初始化,然后創建對象后調用 Servlet 的 service 方法。在 Servlet 的 API 中我通常也只實現它的一個包裝好的類,在 Jetty 中也是如此,雖然 ContextHandler 也只是一個 Handler,但是這個 Handler 通常是由 Jetty 幫你實現了,我們一般只要實現一些我們具體要做的業務邏輯有關的 Handler 就好了,而一些流程性的或某些規范的 Handler,我們直接用就好了,如下面的關於 Jetty 支持 Servlet 的規范的 Handler 就有多種實現,下面是一個簡單的 HTTP 請求的流程。

Server server = new Server(); 
Connector connector = new SelectChannelConnector(); 
connector.setPort(8080); 
server.setConnectors(new Connector[]{ connector }); 
ServletContextHandler root = new 
ServletContextHandler(null,"/",ServletContextHandler.SESSIONS); 
server.setHandler(root); 
root.addServlet(new ServletHolder(new 
org.eclipse.jetty.embedded.HelloServlet("Hello")),"/"); 
server.start(); 
server.join();

創建一個 ServletContextHandler 並給這個 Handler 添加一個 Servlet,這里的 ServletHolder 是 Servlet 的一個裝飾類,它十分類似於 Tomcat 中的 StandardWrapper。下面是請求這個 Servlet 的時序圖:

上圖可以看出 Jetty 處理請求的過程就是 Handler 鏈上 handle 方法的執行過程,在這里需要解釋的一點是 ScopeHandler 的處理規則,ServletContextHandler、SessionHandler 和 ServletHandler 都繼承了 ScopeHandler,那么這三個類組成一個 Handler 鏈,它們的執行規則是:ServletContextHandler.handleServletContextHandler.doScope SessionHandler. doScopeServletHandler. doScopeServletContextHandler. doHandleSessionHandler. doHandleServletHandler. doHandle,它這種機制使得我們可以在 doScope 做一些額外工作。

與 Jboss 集成

 https://www.ibm.com/developerworks/cn/java/j-lo-jetty/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM