Java HTTP/2 客戶端:從阻塞到非同步 - sanjeevr
一個HttpClient可以用來通過HTTP訪問網路上的任何資源。
在Java 11之前,開發者不得不使用傳統的HttpUrlConnection類,它被認為是更抽象的,或者使用第三方庫,如Apache HttpClient,或OkHttp。
從JDK11開始,它支援HTTP/1.1和HTTP/2,支援同步和非同步程式設計模型,將請求和響應體作為反應流處理,並遵循熟悉的構建器模式。預設情況下,客戶端將使用HTTP/2傳送請求。傳送到尚不支援HTTP/2的伺服器的請求將自動降級為HTTP/1.1。
新的API現在通過CompletableFutures提供非阻塞的請求和響應處理。其他概念,如反壓和流量控制,已經通過java.uti.consurrent.Flow API由反應式流提供。
讓我們深入瞭解一下使用Java HTTP客戶端執行普通任務的例子和配方。
同步GET
響應主體是一個字串
public void get(String uri) throws Exception { HttpClient client = HttpClient.newHttpClient()。 HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(uri)) .build()。 HttpResponse<String> response = client.send(request, BodyHandlers.ofString())。 System.out.println(response.body())。 } |
響應體是一個檔案
public void get(String uri) throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(uri)) .build()。 HttpResponse<Path> response = client.send(request, BodyHandlers.ofFile(Paths.get("body.txt"))。 System.out.println("檔案中的響應:" + response.body())。 } |
非同步GET
非同步API立即返回一個CompletableFuture,當HttpResponse可用時,它就會完成。CompletableFuture是在Java 8中新增的,支援可組合的非同步程式設計。
響應體是一個字串
public CompletableFuture<String> get(String uri) { HttpClient client = HttpClient.newHttpClient()。 HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(uri)) .build()。 return client.sendAsync(request, BodyHandlers.ofString()) .thenApply(HttpResponse::body)。 } CompletableFuture.thenApply(Function)方法可用於將HttpResponse對映到其body型別、狀態程式碼等。 |
POST
一個請求體可以由一個HttpRequest.BodyPublisher提供。
public void post(String uri, String data) throws Exception { HttpClient client = HttpClient.newBuilder().build()。 HttpRequest request = HttpRequest.newBuilder() .URI(URI.create(uri)) .POST(BodyPublishers.ofString(data)) .build()。 HttpResponse<?> response = client.send(request, BodyHandlers.discarding())。 System.out.println(response.statusCode())。 } |
上面的例子使用ofString BodyPublisher將給定的字串轉換為請求體位元組。
BodyPublisher是一個反應式流釋出器,按需釋出請求體的流。HttpRequest.Builder有一些允許設定BodyPublisher的方法;Builder::POST, Builder::PUT, 和Builder::method。HttpRequest.BodyPublishers類有一些方便的靜態工廠方法,可以為常見的資料型別建立一個BodyPublisher;ofString、ofByteArray、ofFile。
丟棄的BodyHandler可以用來接收和丟棄響應體,當它不感興趣的時候。
併發請求
結合Java Streams和CompletableFuture API來發出一些請求並等待其響應是很容易的。下面的例子為列表中的每個URI傳送了一個GET請求,並將所有的響應儲存為字串。
public void getURIs(List<URI> uris) { HttpClient client = HttpClient.newHttpClient(); List<HttpRequest> requests = uris.stream() .map(HttpRequest::newBuilder) .map(reqBuilder -> reqBuilder.build()) .collect(toList())。 CompletableFuture.allOf(request.stream() .map(request -> client.sendAsync(request, ofString())) .toArray(CompletableFuture<?>[]:new)) .join()。 } |
獲取JSON
在許多情況下,響應體將是一些更高階別的格式。可以使用方便的響應體處理程式,同時使用第三方庫將響應體轉換為該格式。
下面的例子演示瞭如何使用Jackson庫,結合BodyHandlers::ofString,將JSON響應轉換為String鍵/值對的Map。
public CompletableFuture<Map<String,String>> JSONBodyAsMap(URI uri) { UncheckedObjectMapper objectMapper = new UncheckedObjectMapper(); HttpRequest request = HttpRequest.newBuilder(uri) .header("Accept", "application/json") .build(); return HttpClient.newHttpClient() .sendAsync(request, BodyHandlers.ofString()) .thenApply(HttpResponse::body) .thenApply(objectMapper::readValue); } class UncheckedObjectMapper extends com.fasterxml.jackson.databind.ObjectMapper { /** Parses the given JSON string into a Map. */ Map<String,String> readValue(String content) { try { return this.readValue(content, new TypeReference<>(){}); } catch (IOException ioe) { throw new CompletionException(ioe); } } |
上面的例子使用ofString,它在記憶體中積累響應體的位元組。另外,也可以使用一個流式訂閱器,比如ofInputStream。
POST JSON
在許多情況下,請求體將是一些更高層次的格式。可以使用方便的請求體處理程式,以及一個第三方庫,將請求體轉換為該格式。
下面的例子演示瞭如何使用Jackson庫,結合BodyPublishers::ofString將String鍵/值對的Map轉換成JSON。
public CompletableFuture<Void> postJSON(URI uri, Map<String,String> map) throws IOException { ObjectMapper objectMapper = new ObjectMapper(); String requestBody = objectMapper .writerWithDefaultPrettyPrinter() .writeValueAsString(map); HttpRequest request = HttpRequest.newBuilder(uri) .header("Content-Type", "application/json") .POST(BodyPublishers.ofString(requestBody)) .build(); return HttpClient.newHttpClient() .sendAsync(request, BodyHandlers.ofString()) .thenApply(HttpResponse::statusCode) .thenAccept(System.out::println); } |
設定一個代理
可以通過客戶端的Builder::proxy方法在HttpClient上配置一個ProxySelector。ProxySelector API為一個給定的URI返回一個特定的代理。在許多情況下,一個單一的靜態代理就足夠了。ProxySelector::of static工廠方法可以用來建立這樣一個選擇器。
響應主體是一個帶有指定代理的字串
public CompletableFuture<String> get(String uri) { HttpClient client = HttpClient.newBuilder() .proxy(ProxySelector.of(new InetSocketAddress("www-proxy.com", 8080)) .build()。 HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(uri)) .build()。 return client.sendAsync(request, BodyHandlers.ofString()) .thenApply(HttpResponse::body)。 } |
另外,也可以使用全系統預設的代理選擇器,這在macOS上是預設的。
HttpClient.newBuilder() .proxy(ProxySelector.getDefault()) .build(); |
相關文章
- Java HTTP 客戶端的比較 - reflectoringJavaHTTP客戶端
- HTTP客戶端框架之RetrofitHTTP客戶端框架
- HTTP 客戶端:RestClient、WebClient、RestTemplateHTTP客戶端RESTclientWeb
- java websocket 客戶端JavaWeb客戶端
- 譯文——OkHttp, 安卓和Java應用的HTTP&HTTP2.0客戶端HTTP安卓Java客戶端
- 初探Thrift客戶端非同步模式客戶端非同步模式
- Zookeeper Java 客戶端搭建Java客戶端
- Zookeeper--Java客戶端Java客戶端
- 高效能 HTTP 客戶端 undici 初探HTTP客戶端
- 模板,從服務端到客戶端服務端客戶端
- 【windows socket+HTTP伺服器客戶端】WindowsHTTP伺服器客戶端
- Gofer是Node.js 的HTTP客戶端GoNode.jsHTTP客戶端
- Swoole 協程 MySQL 客戶端與非同步回撥 MySQL 客戶端的對比MySql客戶端非同步
- zookeeper的Java客戶端APIJava客戶端API
- JAVA FTP客戶端問題JavaFTP客戶端
- Oauth2(2)客戶端註冊OAuth客戶端
- Aiohttp是Python的最快的非同步HTTP客戶端/伺服器庫包AIHTTPPython非同步客戶端伺服器
- rsync 客戶端同步的時候報錯客戶端
- 實現客戶端與服務端的HTTP通訊客戶端服務端HTTP
- db2 客戶端安裝DB2客戶端
- Docker v2ray 客戶端Docker客戶端
- webService 客戶端呼叫 axis2Web客戶端
- Java OAuth 2.0 客戶端程式設計(二): 客戶端憑據授權JavaOAuth客戶端程式設計
- WCF 客戶端 BasicHttpBinding 相容 HTTPS 和 HTTP客戶端HTTP
- 修改CAS客戶端 使用簡單HTTP協議客戶端HTTP協議
- [jaeger] 二、客戶端使用 (Java版本)客戶端Java
- Elasticsearch及java客戶端jest使用ElasticsearchJava客戶端
- 從客戶端連線ASM例項客戶端ASM
- 全鏈路非同步Rest客戶端 ESA RestClient非同步REST客戶端client
- HTTP客戶端連線,選擇HttpClient還是OkHttp?HTTP客戶端client
- RetrofitJs – TypeScript實現的宣告式HTTP客戶端JSTypeScriptHTTP客戶端
- reqwest:簡單而強大的 Rust HTTP 客戶端RustHTTP客戶端
- Golang 學習筆記(一) - HTTP 客戶端 - 基礎Golang筆記HTTP客戶端
- InfluxDB 客戶端基礎操作2UX客戶端
- Tars-Java客戶端原始碼分析Java客戶端原始碼
- java 獲取客戶端真實ipJava客戶端
- java獲取客戶端ip和macJava客戶端Mac
- Redis介紹 && Java客戶端操作RedisRedisJava客戶端