1. 響應式編程 1.1. 使用基於事件的範式處理非同步數據流 1.2. 和非同步編程提供了相同的性能優勢 1.3. 能夠擴展程式(特別是擴展I/O)以處理很多連接和數據源 2. 非阻塞I/O 2.1. 有效擴展伺服器的基礎 2.2. 允許伺服器用相對較少的線程處理相對較多的連接 2.2.1. 傳統的服 ...
1. 響應式編程
1.1. 使用基於事件的範式處理非同步數據流
1.2. 和非同步編程提供了相同的性能優勢
1.3. 能夠擴展程式(特別是擴展I/O)以處理很多連接和數據源
2. 非阻塞I/O
2.1. 有效擴展伺服器的基礎
2.2. 允許伺服器用相對較少的線程處理相對較多的連接
-
2.2.1. 傳統的伺服器利用這一點來處理基本的客戶端連接
-
2.2.2. 新的伺服器可以將非阻塞特性擴展到其他應用程式
3. 優化伺服器線程池
3.1. 選擇器線程
- 3.1.1. 在I/O可用時通知系統調用的線程
3.2. 選擇器通知有客戶端I/O待處理之後,另一個包含工作線程的線程池會處理實際的請求和響應
3.3. 要足夠多的工作線程來處理伺服器所能處理的併發請求數(而非併發連接數)
3.4. 像所有受限於CPU的情況一樣,線程的數量沒有必要比運行伺服器的機器或容器的虛擬CPU數量多,因為永遠沒有辦法運行那麼多的線程
3.5. 將長請求推遲到另一個線程池中,這為主線程池提供了更強大的請求處理能力
3.6. 非同步REST伺服器
-
3.6.1. 使用非同步響應有3個原因
-
3.6.1.1. 為了在業務邏輯中引入更多的並行性
-
3.6.1.2. 為了限制活躍線程的數量
-
3.6.1.3. 為了適當地對伺服器限流
-
-
3.6.2. 添加了@Suspended註解的AsyncResponse正在等待邏輯完成,一旦完成,它就會繼續將響應發回給用戶
-
3.6.3. 在排隊等待響應之前,看看非同步線程池的狀態,如果系統太忙,就拒絕請求
-
3.6.4. 如果線程數量等於池大小,響應會被立即取消
- 3.6.4.1. 調用者會立即收到錯誤提示“HTTP 503服務不可用”,表示此時不能處理該請求
-
3.6.5. 立即返回該錯誤能減少過載伺服器的負載,這最終會使整體性能大幅提高
- 3.6.5.1. 這是REST處理過載伺服器的首選方式
4. 非同步出站調用
4.1. HTTP客戶端
-
4.1.1. 處理髮向伺服器的HTTP請求類
-
4.1.2. 可以通過在多個線程之間分配工作來提高性能,從而增加併發量
-
4.1.3. java.net.HttpURLConnection類
- 4.1.3.1. Java 8
-
4.1.4. java.net.HttpsURLConnection
- 4.1.4.1. Java 8
-
4.1.5. java.net.http. HttpClient類
-
4.1.5.1. 也處理HTTPS
-
4.1.5.2. Java 11
-
-
4.1.6. org.apache.http.client.HttpClient
- 4.1.6.1. Apache軟體基金會
-
4.1.7. org.asynchttpclient.AsyncHttpClient
- 4.1.7.1. Netty項目
-
4.1.8. org.eclipse.jetty.client.HttpClient
- 4.1.8.1. Eclipse基金
4.2. JAX-RS
-
4.2.1. JAX-RS連接器提供了一個Client對象用於REST調用
-
4.2.2. HTTP客戶端可以正確地池化連接並使用keepalive來保持連接開放
4.3. -Dhttp.maxConnections=N
-
4.3.1. 改變池的大小
-
4.3.2. 預設值是5
-
4.3.3. 適用於HTTPS連接
-
4.3.4. 不能起到限流的作用
4.4. HttpClient類
-
4.4.1. JDK 11
-
4.4.2. 預設的池大小是無限制的
-
4.4.3. -Djdk.httpclient.connectionPoolSize=N
-
4.4.3.1. 不能起到限流的作用
-
4.4.3.2. 請求的連接超過了配置的數量,它們會在需要的時候被創建,在完成任務後被銷毀
-
4.5. 與使用傳統I/O構建的客戶端相比,使用NIO構建的非同步HTTP客戶端需要的線程更少
4.6. 但REST伺服器仍然需要相當多的線程來處理非同步請求
5. 非同步資料庫調用
5.1. 使用最廣泛的是Spring項目的Spring Data R2DBC
- 5.1.1. 對於關係資料庫的非阻塞訪問來說,這是最好的選擇
5.2. 針對響應式NoSQL資料庫的Spring項目可以用於真正的非同步訪問
6. 格式
6.1. Apache Avro
6.2. Google的Protocol Buffers
6.3. XML
6.4. JSON
7. JSON處理
7.1. 解碼(unmarshaling)
- 7.1.1. 輸出的是一個Java對象
7.2. 解析(parsing)
- 7.2.1. 數據在讀取時被處理,這個過程就叫作解析
7.3. 給定一系列JSON字元串,程式必須將這些字元串轉換為適合Java處理的數據
7.4. 編碼(marshaling)
- 7.4.1. 從其他數據生成JSON字元串的過程
7.5. 通用技術
-
7.5.1. 拉解析器(pull parser)
- 7.5.1.1. 輸入的數據與解析器相關聯,程式從解析器請求(或拉取)一系列標記(token)
-
7.5.2. 文檔模型(document model)
- 7.5.2.1. 輸入的數據被轉換為一個文檔風格的對象,應用程式可以在尋找數據片段時遍歷這個對象
-
7.5.3. 對象表示(object representation)
-
7.5.3.1. 通過使用一組反映數據結構的預定義類,將輸入的數據轉換成一個或多個Java對象
-
7.5.3.2. POJO(plain old Java object)
-
7.6. JSON數據表示形式
-
7.6.1. 簡單JSON對象
-
7.6.1.1. 通用介面進行操作,如JsonObject和JsonArray
-
7.6.1.2. 不需要具體表示數據的類
-
7.6.1.3. 生成簡單JSON對象比生成自定義Java類要快得多
-
-
7.6.2. JSON對象表示形式
-
7.6.2.1. 從編程的角度來看,這些Java類更容易使用
-
7.6.2.2. 使用可以產生POJO的JSON-B(JSON Binding),將JSON數據綁定到一個完整的Java類
-
7.7. Jackson
- 7.7.1. Jackson解析器通常是最快的解析器,應該優先選擇它,而不是選擇預設的實現
7.8. 所有的JSON解析器都是拉解析器
-
7.8.1. 從流中按需檢索數據
-
7.8.2. 實際的解析器不能被重覆使用,它們也不是線程安全的。因此,解析器通常是按需創建的
7.9. 處理JSON有兩種方式:創建POJO對象和直接解析
-
7.9.1. 直接解析提供了過濾的能力和通用的性能提升機會
-
7.9.2. 當對象很大時,創建JSON對象往往會導致GC問題