上篇提到了高性能處理的關鍵是異步,而我們當中許多人依舊在使用同步模式的HttpClient訪問第三方Web資源,我認為原因之一是:異步的HttpClient誕生較晚,許多人不知道;另外也可能是大多數Web程序其實不在意這點性能損失了。
而要自己實現一個異步的HttpClient則比較困難,通常都是自己開一個新的工作線程,利用HttpClient的同步去訪問,完成后再回調這種形式,這樣做其實不是真正的異步,因為依舊會有一個線程處於阻塞中,等待着第三方Web資源的返回。
而如今訪問第三方Web資源的情景越來越多,最典型就是使用第三方登錄平台,如QQ或微信等,我們需要訪問騰訊的服務器去驗證登錄者的身份,根據我的經驗,這個過程可能會阻塞好幾秒鍾,可看作是一個“長時間調用”,所以最好要使用異步方式。
OK,廢話少說,要使用異步的HttpClient,請Maven中帶上:
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpasyncclient</artifactId> <version>4.1.1</version> </dependency>
接下來是一個完整的Demo代碼:
import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.concurrent.FutureCallback; import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; import org.apache.http.impl.nio.client.HttpAsyncClients; import org.apache.http.util.EntityUtils; import java.io.IOException; import java.util.concurrent.CountDownLatch; public class Main { public static void main(String[] argv) { CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault(); httpclient.start(); final CountDownLatch latch = new CountDownLatch(1); final HttpGet request = new HttpGet("https://www.alipay.com/"); System.out.println(" caller thread id is : " + Thread.currentThread().getId()); httpclient.execute(request, new FutureCallback<HttpResponse>() { public void completed(final HttpResponse response) { latch.countDown(); System.out.println(" callback thread id is : " + Thread.currentThread().getId()); System.out.println(request.getRequestLine() + "->" + response.getStatusLine()); try { String content = EntityUtils.toString(response.getEntity(), "UTF-8"); System.out.println(" response content is : " + content); } catch (IOException e) { e.printStackTrace(); } } public void failed(final Exception ex) { latch.countDown(); System.out.println(request.getRequestLine() + "->" + ex); System.out.println(" callback thread id is : " + Thread.currentThread().getId()); } public void cancelled() { latch.countDown(); System.out.println(request.getRequestLine() + " cancelled"); System.out.println(" callback thread id is : " + Thread.currentThread().getId()); } }); try { latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } try { httpclient.close(); } catch (IOException ignore) { } } }
呃……代碼很簡單,好像也沒什么好說的了,稍作封裝就可以實現如“getJson()”這樣的方法。
也許你還注意到了,這個HttpClient跟同步的版本一樣,直接支持https,但如果網站的證書是自簽的,默認還是不行的,解決方法當然有,但代碼有些麻煩,我覺得還不如直接買張證書來得簡單,如果網站是你管的話。