摘要:在中也可以直接使用返回的是,然后通過來獲取結果阻塞線程,從中獲取結果四一點嘮叨非常的年輕,網絡資料不多,且代碼非常精細和復雜,目前來看底層應該是使用了線程池搭配進行異步通訊。
零 前期準備 0 版本
JDK 版本 : OpenJDK 11.0.1
IDE : idea 2018.3
1 HttpClient 簡介java.net.http.HttpClient 是 jdk11 中正式啟用的一個 http 工具類(其實早在 jdk9 的時候就已經存在了,只是處于孵化期),官方寓意為想要取代 HttpURLConnection 和 Apache HttpClient 等比較古老的開發工具。
新增的 HttpClient 截止到目前(2019年3月)為止其實網絡資料還比較少,筆者只是根據一些博文和官方 Demo 自己摸索了一下,做了下總結。
【由于是 jdk11 中才正式使用的工具類,距離開發者還很遙遠,所以對于源碼筆者暫不打算深挖,淺淺的理解怎么使用就行】
一 HttpClient在 Apache HttpClient 中,一般會創建一個 HttpClient 對象來作為門面。java.net.http.HttpClient 的邏輯也差不多,只是創建方式更加時髦了:
</>復制代碼
//創建 builder
HttpClient.Builder builder = HttpClient.newBuilder();
//鏈式調用
HttpClient client = builder
//http 協議版本 1.1 或者 2
.version(HttpClient.Version.HTTP_2) //.version(HttpClient.Version.HTTP_1_1)
//連接超時時間,單位為毫秒
.connectTimeout(Duration.ofMillis(5000)) //.connectTimeout(Duration.ofMinutes(1))
//連接完成之后的轉發策略
.followRedirects(HttpClient.Redirect.NEVER) //.followRedirects(HttpClient.Redirect.ALWAYS)
//指定線程池
.executor(Executors.newFixedThreadPool(5))
//認證,默認情況下 Authenticator.getDefault() 是 null 值,會報錯
//.authenticator(Authenticator.getDefault())
//代理地址
//.proxy(ProxySelector.of(new InetSocketAddress("http://www.baidu.com", 8080)))
//緩存,默認情況下 CookieHandler.getDefault() 是 null 值,會報錯
//.cookieHandler(CookieHandler.getDefault())
//創建完成
.build();
在 builder() 方法中,最終會調用到 HttpClientImpl 的構造器,完成 HttpClient 的創建工作:
</>復制代碼
//HttpClientImpl.class
private HttpClientImpl(HttpClientBuilderImpl builder,
SingleFacadeFactory facadeFactory) {
//CLIENT_IDS 是 AtomicLong 類型的變量,使用 incrementAndGet() 方法實現自增長的 id
id = CLIENT_IDS.incrementAndGet();
//記錄下存有 id 的字符串
dbgTag = "HttpClientImpl(" + id +")";
//ssl 認證
if (builder.sslContext == null) {
try {
sslContext = SSLContext.getDefault();
} catch (NoSuchAlgorithmException ex) {
throw new InternalError(ex);
}
} else {
sslContext = builder.sslContext;
}
//線程池,沒有的話就默認創建一個
Executor ex = builder.executor;
if (ex == null) {
ex = Executors.newCachedThreadPool(new DefaultThreadFactory(id));
isDefaultExecutor = true;
} else {
isDefaultExecutor = false;
}
delegatingExecutor = new DelegatingExecutor(this::isSelectorThread, ex);
facadeRef = new WeakReference<>(facadeFactory.createFacade(this));
//處理 http 2 的 client 類
client2 = new Http2ClientImpl(this);‘
//緩存操作
cookieHandler = builder.cookieHandler;
//超時時間
connectTimeout = builder.connectTimeout;
//轉發策略,默認為 NEVER
followRedirects = builder.followRedirects == null ?
Redirect.NEVER : builder.followRedirects;
//代理設置
this.userProxySelector = Optional.ofNullable(builder.proxy);
this.proxySelector = userProxySelector
.orElseGet(HttpClientImpl::getDefaultProxySelector);
if (debug.on())
debug.log("proxySelector is %s (user-supplied=%s)",
this.proxySelector, userProxySelector.isPresent());
//認證設置
authenticator = builder.authenticator;
//設置 http 協議版本
if (builder.version == null) {
version = HttpClient.Version.HTTP_2;
} else {
version = builder.version;
}
if (builder.sslParams == null) {
sslParams = getDefaultParams(sslContext);
} else {
sslParams = builder.sslParams;
}
//連接線程池
connections = new ConnectionPool(id);
connections.start();
timeouts = new TreeSet<>();
//SelectorManager 本質上是 Thread 類的封裝
//selmgr 會開啟一條線程,HttpClient 的主要邏輯運行在此線程中
//所以說 HttpClient 是非阻塞的,因為并不跑在主線程中
try {
selmgr = new SelectorManager(this);
} catch (IOException e) {
throw new InternalError(e);
}
//設置為守護線程
selmgr.setDaemon(true);
filters = new FilterFactory();
initFilters();
assert facadeRef.get() != null;
}
主要是一些儲存操作,大致理解即可,不細究。
二 HttpRequestHttpRequest 是發起請求的主體配置:
</>復制代碼
//創建 builder
HttpRequest.Builder reBuilder = HttpRequest.newBuilder();
//鏈式調用
HttpRequest request = reBuilder
//存入消息頭
//消息頭是保存在一張 TreeMap 里的
.header("Content-Type", "application/json")
//http 協議版本
.version(HttpClient.Version.HTTP_2)
//url 地址
.uri(URI.create("http://openjdk.java.net/"))
//超時時間
.timeout(Duration.ofMillis(5009))
//發起一個 post 消息,需要存入一個消息體
.POST(HttpRequest.BodyPublishers.ofString("hello"))
//發起一個 get 消息,get 不需要消息體
//.GET()
//method(...) 方法是 POST(...) 和 GET(...) 方法的底層,效果一樣
//.method("POST",HttpRequest.BodyPublishers.ofString("hello"))
//創建完成
.build();
三 發送
發起請求:
</>復制代碼
HttpResponse response =
client.send(request, HttpResponse.BodyHandlers.ofString());
這是同步式的發起請求方式,先來看一下它的實現:
</>復制代碼
public HttpResponse send(HttpRequest req, BodyHandler responseHandler)
throws IOException, InterruptedException{
CompletableFuture> cf = null;
try {
//調用 sendAsync(...) 方法異步地完成主邏輯,并獲取 Future
cf = sendAsync(req, responseHandler, null, null);
return cf.get();
//這之后的所有代碼都是在進行異常捕捉,所以可以忽略
} catch (InterruptedException ie) {
if (cf != null )
cf.cancel(true);
throw ie;
} catch (ExecutionException e) {
final Throwable throwable = e.getCause();
final String msg = throwable.getMessage();
if (throwable instanceof IllegalArgumentException) {
throw new IllegalArgumentException(msg, throwable);
} else if (throwable instanceof SecurityException) {
throw new SecurityException(msg, throwable);
} else if (throwable instanceof HttpConnectTimeoutException) {
HttpConnectTimeoutException hcte = new HttpConnectTimeoutException(msg);
hcte.initCause(throwable);
throw hcte;
} else if (throwable instanceof HttpTimeoutException) {
throw new HttpTimeoutException(msg);
} else if (throwable instanceof ConnectException) {
ConnectException ce = new ConnectException(msg);
ce.initCause(throwable);
throw ce;
} else if (throwable instanceof IOException) {
throw new IOException(msg, throwable);
} else {
throw new IOException(msg, throwable);
}
}
}
本質上是使用了異步實現方法 sendAsync(...)。
在 Demo 中也可以直接使用:
</>復制代碼
//返回的是 future,然后通過 future 來獲取結果
CompletableFuture future =
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body);
//阻塞線程,從 future 中獲取結果
String body = future.get();
四 一點嘮叨
java.net.http.HttpClient 非常的年輕,網絡資料不多,且代碼非常精細和復雜,目前來看底層應該是使用了線程池搭配 Socket 進行異步通訊。具體有待后續研究。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/73595.html
摘要:是一個倡議,它提倡提供一種帶有非阻塞背壓的異步流處理的標準。是標準的實現之一。的實現細節請求響應的與請求響應的暴露為是請求的的消費者是響應的的生產者內部的內部 北京時間 9 月 26 日,Oracle 官方宣布 Java 11 正式發布 一、JDK HTTP Client介紹 JDK11中的17個新特性 showImg(https://segmentfault.com/img/remo...
序 本文主要研究一下Java11的HttpClient的基本使用。 變化 從java9的jdk.incubator.httpclient模塊遷移到java.net.http模塊,包名由jdk.incubator.http改為java.net.http 原來的諸如HttpResponse.BodyHandler.asString()方法變更為HttpResponse.BodyHandlers.of...
摘要:序本文主要研究一下的參數這里有一個類型的變量,用來記錄請求次數另外還有一個,讀取的是值,讀取不到默認取,為進入該方法的時候,調用,遞增請求次數,然后判斷有無超出限制,有則返回帶有異常的,即通過返回如果沒有超出限制,但是執行請求失敗,則 序 本文主要研究一下jdk httpclient的retry參數 DEFAULT_MAX_ATTEMPTS java.net.http/jdk/inte...
摘要:鑒于它還處在,如果不是著急使用,建議還是使用的,它是遵循規范的,使用起來更加方便。貌似要在版本才支持。揭秘讓支持協議如何啟用命令支持 序 本文主要研究下JEP 110: HTTP/2 Client (Incubator) 基本實例 sync get /** * --add-modules jdk.incubator.httpclient * @throws ...
摘要:從版本開始,不再單獨發布或者版本了,有需要的可以自己通過去定制官方解讀官方細項解讀穩步推進系列六的小試牛刀一文讀懂的為何如此高效棄用引擎 Java語言特性系列 Java5的新特性 Java6的新特性 Java7的新特性 Java8的新特性 Java9的新特性 Java10的新特性 Java11的新特性 Java12的新特性 Java13的新特性 序 本文主要講述一下Java11的新...
閱讀 1430·2021-11-15 11:38
閱讀 3581·2021-11-09 09:47
閱讀 1979·2021-09-27 13:36
閱讀 3226·2021-09-22 15:17
閱讀 2564·2021-09-13 10:27
閱讀 2875·2019-08-30 15:44
閱讀 1191·2019-08-27 10:53
閱讀 2719·2019-08-26 14:00