Jetty 的基本架構

Jetty的基本架構前端

 

jetty的主要組件的類圖java

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

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

整個 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 的組件,app

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/

相關文章
相關標籤/搜索