Jetty 是 Eclipse 基金會的開源項目,與 Tomcat 一樣是「HTTP 伺服器 + Servlet 容器」。Jetty 以輕量級和可嵌入性著稱,被廣泛應用於 Google App Engine、Spring Boot 等項目中。通過對比學習 Jetty 和 Tomcat,我們能更深入理解 Web 容器的設計思想。
整體架構#
核心組件#
Jetty 架構三要素
Jetty Server = 多個 Connector + 多個 Handler + 一個線程池
┌────────────────────────────────────────────────────────────┐
│ Server │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Connector │ │ Connector │ │ Connector │ │
│ │ (HTTP) │ │ (HTTPS) │ │ (HTTP/2) │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
│ └────────────────┼────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Handler │ │
│ │ (Handler Tree)│ │
│ └─────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ ThreadPool │ │
│ │ (全局共享) │ │
│ └─────────────────┘ │
└────────────────────────────────────────────────────────────┘與 Tomcat 的架構對比#
| 特性 | Tomcat | Jetty |
|---|---|---|
| 容器結構 | 多層容器 (Engine/Host/Context/Wrapper) | Handler 樹狀結構 |
| 線程池 | 每個 Connector 獨立線程池 | 全局共享線程池 |
| Service 概念 | 有,包裝 Connector 和 Container | 無,Connector 直接關聯 Handler |
| 設計風格 | 功能全面,組態豐富 | 輕量級,高度可定制 |
| I/O 模型 | 支持 NIO、NIO.2、APR | 只支持 NIO |
Connector 組件#
Connector 的職責#
Jetty 的 Connector 負責:
- 監聽網路連線埠
- 接收連線請求
- 讀寫網路資料
- 封裝應用層協定
核心子組件#
┌───────────────────── Connector ─────────────────────┐
│ │
│ ┌────────────┐ │
│ │ Acceptor │ ← 接收新連線(阻塞方式) │
│ └─────┬──────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ SelectorManager │ ← 管理多個 Selector │
│ │ ┌───────────────┐ │ │
│ │ │ManagedSelector │ │ ← I/O 事件檢測 │
│ │ └───────┬───────┘ │ │
│ └──────────┼──────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ EndPoint │ ← 封裝 Channel │
│ │ + Connection │ ← 應用層協定處理 │
│ └──────────────────┘ │
│ │
└──────────────────────────────────────────────────────┘Acceptor#
Acceptor 負責接收新連線,使用阻塞方式:
public void accept(int acceptorID) throws IOException {
ServerSocketChannel serverChannel = _acceptChannel;
if (serverChannel != null && serverChannel.isOpen()) {
// 阻塞等待新連線
SocketChannel channel = serverChannel.accept();
// 設置為非阻塞模式
channel.configureBlocking(false);
// 交給 SelectorManager 處理
_manager.accept(channel);
}
}SelectorManager 與 ManagedSelector#
SelectorManager 管理多個 ManagedSelector,ManagedSelector 封裝了 Java NIO 的 Selector:
public class SelectorManager {
// 管理多個 ManagedSelector
private final ManagedSelector[] _selectors;
public void accept(SelectableChannel channel, Object attachment) {
// 選擇一個 Selector 來處理
final ManagedSelector selector = chooseSelector();
// 提交 Accept 任務
selector.submit(selector.newAccept(channel, attachment));
}
}ManagedSelector 的工作流程:
- 將 Channel 註冊到 Selector 上
- 創建 EndPoint 和 Connection
- 將它們與 SelectionKey 綁定
- 持續檢測 I/O 事件
EndPoint 與 Connection#
EndPoint 與 Connection 的關係
- EndPoint:封裝 SocketChannel,負責底層資料讀寫
- Connection:負責應用層協定解析(類似 Tomcat 的 Processor)
一個 EndPoint 對應一個 Connection。
// 創建 EndPoint 和 Connection
private void createEndPoint(SelectableChannel channel,
SelectionKey selectionKey) {
// 1. 創建 EndPoint
EndPoint endPoint = _selectorManager.newEndPoint(channel, this, selectionKey);
// 2. 創建 Connection(處理 HTTP 協定)
Connection connection = _selectorManager.newConnection(channel, endPoint, ...);
// 3. 綁定
endPoint.setConnection(connection);
selectionKey.attach(endPoint);
}非同步 I/O 模擬#
回呼函式模擬非同步
Jetty 在應用層面通過回呼函式模擬了非同步 I/O:Connection 向 EndPoint 註冊回呼方法,當資料就緒時由 EndPoint 呼叫回呼方法。
// HttpConnection 註冊讀取回呼
getEndPoint().fillInterested(_readCallback);
// 回呼方法中讀取資料
private class ReadCallback implements Callback {
@Override
public void succeeded() {
// 資料就緒,讀取並解析
onFillable();
}
}Connector 工作流程#
1. Acceptor 接收新連線,獲得 SocketChannel
2. Acceptor 將 Channel 交給 SelectorManager
3. SelectorManager 選擇一個 ManagedSelector
4. ManagedSelector 將 Channel 註冊到 Selector
5. ManagedSelector 創建 EndPoint 和 Connection
6. ManagedSelector 檢測到 I/O 事件
7. 呼叫 EndPoint 的方法,回傳一個 Runnable
8. 將 Runnable 提交到線程池執行
9. Runnable 執行時呼叫 Connection 的回呼
10. Connection 讀取資料,解析協定,呼叫 HandlerHandler 組件#
Handler 是什麼?#
Handler 是 Jetty 的靈魂
Jetty 將所有請求處理邏輯都抽象為 Handler。通過組合不同的 Handler,可以實現高度定制化。
public interface Handler extends LifeCycle, Destroyable {
// 處理請求
void handle(String target,
Request baseRequest,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException;
// 關聯 Server
void setServer(Server server);
Server getServer();
}Handler 的繼承體系#
Handler (介面)
│
└── AbstractHandler
│
├── AbstractHandlerContainer
│ │
│ ├── HandlerWrapper (包含單個 Handler)
│ │ │
│ │ ├── Server
│ │ ├── ScopedHandler
│ │ │ ├── ContextHandler
│ │ │ ├── SessionHandler
│ │ │ ├── SecurityHandler
│ │ │ └── ServletHandler
│ │ └── WebAppContext
│ │
│ └── HandlerCollection (包含 Handler 陣列)
│ │
│ └── ContextHandlerCollection
│
└── ResourceHandler (靜態資源)Handler 的三種類型#
| 類型 | 說明 | 示例 |
|---|---|---|
| 協調 Handler | 將請求路由到一組 Handler | HandlerCollection |
| 過濾器 Handler | 處理請求後轉發給下一個 Handler | HandlerWrapper, ContextHandler |
| 內容 Handler | 真正處理請求,生成回應 | ServletHandler, ResourceHandler |
HandlerWrapper 與 HandlerCollection#
// HandlerWrapper:持有單個 Handler 引用
public class HandlerWrapper extends AbstractHandlerContainer {
protected Handler _handler;
@Override
public void handle(String target, Request baseRequest,
HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
if (_handler != null) {
_handler.handle(target, baseRequest, request, response);
}
}
}
// HandlerCollection:持有 Handler 陣列
public class HandlerCollection extends AbstractHandlerContainer {
private Handler[] _handlers;
@Override
public void handle(String target, Request baseRequest,
HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
for (Handler handler : _handlers) {
handler.handle(target, baseRequest, request, response);
}
}
}如何實現 Servlet 規範#
Jetty 通過以下 Handler 實現 Servlet 規範:
| Handler | 對應 Servlet 規範 |
|---|---|
| ContextHandler | ServletContext |
| SessionHandler | HttpSession |
| SecurityHandler | 安全約束 |
| ServletHandler | Servlet, Filter, Listener |
WebAppContext (繼承 ContextHandler)
│
└── SessionHandler
│
└── SecurityHandler
│
└── ServletHandler ──> Filter Chain ──> ServletHandler 鏈的執行流程#
// 啟動一個 Web 應用
WebAppContext webapp = new WebAppContext();
webapp.setContextPath("/myapp");
webapp.setWar("myapp.war");
// 將 Handler 添加到 Server
server.setHandler(webapp);
// 啟動
server.start();請求處理流程:
請求 /myapp/hello
│
▼
WebAppContext.handle()
│
├── 設置 ClassLoader
├── 初始化 ServletContext
│
▼
SessionHandler.handle()
│
├── 創建或獲取 Session
│
▼
SecurityHandler.handle()
│
├── 安全檢查
│
▼
ServletHandler.handle()
│
├── 創建 Filter Chain
├── 呼叫 Filters
└── 呼叫 ServletScopedHandler#
什麼是 ScopedHandler?#
ScopedHandler 實現了「具有上下文資訊的責任鏈呼叫」,確保 doScope() 方法在 doHandle() 之前執行。
呼叫順序:
A.doScope() → B.doScope() → C.doScope()
→ A.doHandle() → B.doHandle() → C.doHandle()為什麼需要這樣的設計?#
Servlet 規範要求 Servlet 執行時有上下文環境(如 ServletContext、Session)。這些上下文需要在處理請求之前初始化。
doScope():初始化上下文(如設置類載入器、創建 Session)doHandle():處理請求
ScopedHandler 的實現原理
public abstract class ScopedHandler extends HandlerWrapper {
protected ScopedHandler _outerScope;
protected ScopedHandler _nextScope;
@Override
public final void handle(String target, Request baseRequest,
HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
if (_outerScope == null) {
// 頭節點,先執行 doScope
doScope(target, baseRequest, request, response);
} else {
// 非頭節點,執行 doHandle
doHandle(target, baseRequest, request, response);
}
}
public void doScope(String target, Request baseRequest,
HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
nextScope(target, baseRequest, request, response);
}
public final void nextScope(String target, Request baseRequest,
HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
if (_nextScope != null) {
// 還有下一個 ScopedHandler,繼續 doScope
_nextScope.doScope(target, baseRequest, request, response);
} else if (_outerScope != null) {
// doScope 都執行完了,回到頭節點開始 doHandle
_outerScope.doHandle(target, baseRequest, request, response);
} else {
doHandle(target, baseRequest, request, response);
}
}
public abstract void doHandle(String target, Request baseRequest,
HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException;
}嵌入式使用#
Jetty 的嵌入式優勢#
Jetty 是嵌入式 Web 容器的首選
Jetty 設計之初就考慮了嵌入式場景:
- 輕量級,啟動快
- API 簡單,易於編程
- 高度可定制
基本使用示例#
public class EmbeddedJetty {
public static void main(String[] args) throws Exception {
// 創建 Server,指定連線埠
Server server = new Server(8080);
// 創建 Servlet Handler
ServletContextHandler context = new ServletContextHandler();
context.setContextPath("/");
context.addServlet(new ServletHolder(new HelloServlet()), "/hello");
// 設置 Handler
server.setHandler(context);
// 啟動
server.start();
server.join();
}
}
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
resp.setContentType("text/plain");
resp.getWriter().println("Hello, Jetty!");
}
}完整的 Web 應用組態#
public class FullWebApp {
public static void main(String[] args) throws Exception {
Server server = new Server(8080);
// 組態 Web 應用
WebAppContext webapp = new WebAppContext();
webapp.setContextPath("/app");
webapp.setResourceBase("src/main/webapp");
webapp.setDescriptor("src/main/webapp/WEB-INF/web.xml");
// 添加 Filter
webapp.addFilter(MyFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
// 添加 Listener
webapp.addEventListener(new MyServletContextListener());
// 組態 Session
SessionHandler sessionHandler = new SessionHandler();
sessionHandler.setMaxInactiveInterval(1800); // 30 分鐘
webapp.setSessionHandler(sessionHandler);
server.setHandler(webapp);
server.start();
server.join();
}
}Spring Boot 中使用 Jetty#
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>Jetty vs Tomcat 深度對比#
架構設計對比#
| 方面 | Tomcat | Jetty |
|---|---|---|
| 容器模型 | 多層容器,固定層級 | Handler 樹,靈活組合 |
| 請求處理 | Pipeline-Valve 責任鏈 | Handler 鏈 |
| 線程模型 | 每個 Connector 獨立線程池 | 全局共享線程池 |
| I/O 模型 | NIO, NIO.2, APR | 僅 NIO |
| 組態方式 | 主要通過 XML | XML 和編程方式都很方便 |
效能特點對比#
| 方面 | Tomcat | Jetty |
|---|---|---|
| 啟動速度 | 較慢(功能多) | 快 |
| 內存佔用 | 較多 | 較少 |
| 大文件處理 | 一般 | 優秀(流式處理) |
| WebSocket | 支持 | 支持,且效能優秀 |
| HTTP/2 | 支持 | 支持 |
適用場景對比#
| 場景 | 推薦 | 原因 |
|---|---|---|
| 傳統企業應用 | Tomcat | 生態成熟,文檔豐富 |
| 嵌入式應用 | Jetty | 輕量級,啟動快 |
| 微服務 | 兩者皆可 | Spring Boot 默認 Tomcat,可切換 |
| 高並行長連線 | Jetty | 線程模型更適合 |
| 需要 APR | Tomcat | Jetty 不支持 APR |
設計理念對比#
不同的設計哲學
- Tomcat:功能完整,開箱即用,遵循「約定優於組態」
- Jetty:輕量靈活,高度可定制,遵循「微內核 + 插件」
總結#
Jetty 的核心設計思想#
- 組件化:所有功能都是 Handler,可自由組合
- 輕量級:只保留核心功能,其他都是可選
- 嵌入式友好:API 設計簡潔,易於編程使用
- 非同步優先:通過回呼模擬非同步 I/O
從 Jetty 學到的設計經驗#
| 設計原則 | 在 Jetty 中的體現 |
|---|---|
| 高內聚低耦合 | Connector 和 Handler 分離 |
| 開閉原則 | 通過 Handler 機制擴展功能 |
| 單一職責 | 每種 Handler 只做一件事 |
| 組合優於繼承 | Handler 樹的組合方式 |