java http异步调用_HttpClient的异步调用,你造吗?
一、前言
HttpClient提供了兩種I/O模型:經(jīng)典的java阻塞I/O模型和基于Java NIO的異步非阻塞事件驅(qū)動I/O模型。
Java中的阻塞I/O是一種高效、便捷的I/O模型,非常適合并發(fā)連接數(shù)量相對適中的高性能應(yīng)用程序。只要并發(fā)連接的數(shù)量在1000個以下并且連接大多忙于傳輸數(shù)據(jù),阻塞I/O模型就可以提供最佳的數(shù)據(jù)吞吐量性能。然而,對于連接大部分時間保持空閑的應(yīng)用程序,上下文切換的開銷可能會變得很大,這時非阻塞I/O模型可能會提供更好的替代方案。
異步I/O模型可能更適合于比較看重資源高效利用、系統(tǒng)可伸縮性、以及可以同時支持更多HTTP連接的場景。
二、HttpClient中的Future
在HttpClient官網(wǎng)Tutorial的高級話題中,我們可以發(fā)現(xiàn)其提供了用于異步執(zhí)行的FutureRequestExecutionService服務(wù)類。
使用FutureRequestExecutionService,允許我們發(fā)起http調(diào)用后,調(diào)用函數(shù)馬上返回(調(diào)用線程不會阻塞等到相應(yīng)結(jié)果返回)一個Future對象,然后調(diào)用線程可以在需要響應(yīng)結(jié)果的地方調(diào)用Future對象的get方法來阻塞等待結(jié)果。
使用FutureRequestExecutionService的優(yōu)點(diǎn)是,我們可以使用多個線程并發(fā)調(diào)度請求、設(shè)置任務(wù)超時,或者在不再需要響應(yīng)時取消它們。
FutureRequestExecutionService其實(shí)是用一個HttpRequestFutureTask包裝請求,該HttpRequestFutureTask擴(kuò)展了JDK中的FutureTask。這個類允許我們?nèi)∠蝿?wù)、跟蹤各種執(zhí)行指標(biāo),如請求持續(xù)時間等。
下面我們看一個例子:
// 1.創(chuàng)建線程池
private static ExecutorService executorService = Executors.newFixedThreadPool(5);
// 2.創(chuàng)建http回調(diào)函數(shù)
private static final class OkidokiHandler implements ResponseHandler
public String handleResponse(final HttpResponse response) throws ClientProtocolException, IOException {
// 2.1處理響應(yīng)結(jié)果
return EntityUtils.toString(response.getEntity());
}
}
public static void main(String[] args) throws IOException, InterruptedException, ExecutionException {
// 3.創(chuàng)建httpclient對象
CloseableHttpClient httpclient = HttpClients.createDefault();
// 4.創(chuàng)建FutureRequestExecutionService實(shí)例
FutureRequestExecutionService futureRequestExecutionService = new FutureRequestExecutionService(httpclient,
executorService);
// 5.發(fā)起調(diào)用
try {
// 5.1請求參數(shù)
HttpGet httpget1 = new HttpGet("http://127.0.0.1:8080/test1");
HttpGet httpget2 = new HttpGet("http://127.0.0.1:8080/test2");
// 5.2發(fā)起請求,不阻塞,馬上返回
HttpRequestFutureTask
HttpClientContext.create(), new OkidokiHandler());
HttpRequestFutureTask
HttpClientContext.create(), new OkidokiHandler());
// 5.3 do somthing
// 5.4阻塞獲取結(jié)果
String str1 = task1.get();
String str2 = task2.get();
System.out.println("response:" + str1 + " " + str2);
} finally {
httpclient.close();
}
}
如上代碼1創(chuàng)建了一個線程池用來作為http異步執(zhí)行的后臺線程,代碼2創(chuàng)建了一個http響應(yīng)結(jié)果處理器,用來異步處理http的響應(yīng)結(jié)果。
代碼3創(chuàng)建了一個HttpClient對象,代碼4創(chuàng)建一個FutureRequestExecutionService,參數(shù)1為創(chuàng)建的httpclient對象,參數(shù)2為創(chuàng)建的線程池。
代碼5則創(chuàng)建2個Get請求參數(shù),然后執(zhí)行代碼5.2發(fā)起兩個http請求,該調(diào)用會馬上返回自己對于的HttpRequestFutureTask對象,調(diào)用線程也會馬上返回,然后調(diào)用線程就可以在5.3做其他的事情,最后在需要獲取http響應(yīng)結(jié)果的地方,比如代碼5.4調(diào)用兩個future的get()方法來獲取結(jié)果。
如上基于Future方式,我們可以并發(fā)的發(fā)起兩個http請求,而之前阻塞方式,則是順序執(zhí)行的。
但是基于上面Future方式,我們調(diào)用線程還是會在代碼5.4阻塞等待響應(yīng)結(jié)果,這并不是我們想要的,我們想要的是事件通知,http確實(shí)也提供了注冊CallBack的方式:
首先我們需要實(shí)現(xiàn)FutureCallback接口,用來接收通知:
private static final class MyCallback implements FutureCallback
public void failed(final Exception ex) {
System.out.println(ex.getLocalizedMessage());
}
public void completed(final String result) {
System.out.println(result);
}
public void cancelled() {
System.out.println("cancelled");
}
}
然后我們只需要修改代碼5.2,使用三個參數(shù)的execute方法發(fā)起調(diào)用:
// 5.發(fā)起調(diào)用
try {
// 5.1請求參數(shù)
HttpGet httpget1 = new HttpGet("http://127.0.0.1:8080/test1");
HttpGet httpget2 = new HttpGet("http://127.0.0.1:8080/test2");
// 5.2發(fā)起請求,不阻塞,馬上返回
HttpRequestFutureTask
HttpClientContext.create(), new OkidokiHandler(), new MyCallback());
HttpRequestFutureTask
HttpClientContext.create(), new OkidokiHandler(), new MyCallback());
//main線程休眠10s,避免請求結(jié)束前,關(guān)閉了鏈接
Thread.sleep(10000);
...
如上代碼,使用CallBack后,調(diào)用線程就得到了徹底解放,就不必再阻塞獲取結(jié)果了,當(dāng)http返回結(jié)果后,會自動調(diào)用我們注冊的CallBack。
三、HttpAsyncClient-真正的異步
上面HttpClient提供的CallBack的方式,雖然解放了調(diào)用線程,但是并不是真正意義上的異步調(diào)用,因?yàn)槠洚惒秸{(diào)用的支持是基于我們創(chuàng)建的executorService線程。即:雖然發(fā)起http調(diào)用后,調(diào)用線程馬上返回了,但是其內(nèi)部還是使用executorService中的一個線程阻塞等待響應(yīng)結(jié)果。
HttpAsyncClient則使用Java NIO的異步非阻塞事件驅(qū)動I/O模型,實(shí)現(xiàn)了真正意義的異步調(diào)用,使用HttpAsyncClient我們需要引入其專門的包:
然后改造后代碼如下:
// 1.創(chuàng)建CallBack
private static final class MyCallback implements FutureCallback
public void failed(final Exception ex) {
System.out.println(ex.getLocalizedMessage());
}
public void completed(final HttpResponse response) {
try {
System.out.println(EntityUtils.toString(response.getEntity()));
} catch (ParseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public void cancelled() {
System.out.println("cancelled");
}
}
public static void main(String[] args) throws IOException, InterruptedException, ExecutionException {
// 2.創(chuàng)建異步httpclient對象
CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom().build();
// 3.發(fā)起調(diào)用
try {
// 3.0啟動
httpclient.start();
// 3.1請求參數(shù)
HttpGet httpget1 = new HttpGet("http://127.0.0.1:8080/test1");
HttpGet httpget2 = new HttpGet("http://127.0.0.1:8080/test2");
// 3.2發(fā)起請求,不阻塞,馬上返回
httpclient.execute(httpget1, new MyCallback());
httpclient.execute(httpget2, new MyCallback());
// 3.3休眠10s,避免請求執(zhí)行完成前,關(guān)閉了鏈接
Thread.sleep(10000);
} finally {
httpclient.close();
}
}
如上代碼1,創(chuàng)建異步回調(diào)實(shí)現(xiàn),用于處理Http響應(yīng)結(jié)果。代碼2創(chuàng)建了異步HttpClient,代碼3.0啟動client,代碼3.2發(fā)起請求。
基于Java NIO的異步,當(dāng)發(fā)起請求后,調(diào)用方不會使用任何線程同步等待http服務(wù)端的響應(yīng)結(jié)果(少量的NIO線程不算哦,因?yàn)槠鋫€數(shù)固定,并且不隨并發(fā)請求數(shù)量變化),而是會使用少量內(nèi)存來記錄請求信息,以便服務(wù)端響應(yīng)結(jié)果回來后,可以找到對應(yīng)的回調(diào)函數(shù)進(jìn)行執(zhí)行。
四、總結(jié)
本文概要講解了Http的異步調(diào)用,關(guān)于更多Java中異步調(diào)用與異步執(zhí)行的知識,可以參考《Java異步編程實(shí)戰(zhàn)》
更多技術(shù)分享,請掃描關(guān)注微信公眾號:
file0人點(diǎn)贊博文
總結(jié)
以上是生活随笔為你收集整理的java http异步调用_HttpClient的异步调用,你造吗?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深入浅出:机器学习与人工智能代码的实现(
- 下一篇: 路由器停产,360的其他硬件产品是否安好