大家好,我是二哥呀!
今天來給大家推薦一款直擊痛點的 HTTP 用戶端架構,可以超高效率地完成和第三方接口的對接。
在介紹本篇的主角之前,我們先來了解下 Java 生态中的 HTTP 元件庫,大緻可以分為三類:
JDK 自帶的 HttpURLConnection 标準庫;
Apache HttpComponents HttpClient;
OkHttp。
使用 HttpURLConnection 發起 HTTP 請求最大的優點是不需要引入額外的依賴,但是使用起來非常繁瑣,也缺乏連接配接池管理、域名機械控制等特性支援。
使用标準庫的最大好處就是不需要引入額外的依賴,但使用起來比較繁瑣,就像直接使用 JDBC 連接配接資料庫那樣,需要很多模闆代碼。來發起一個簡單的 HTTP POST 請求吧。
public class HttpUrlConnectionDemo {
public static void main(String[] args) throws IOException {
String urlString = "https://httpbin.org/post";
String bodyString = "password=123";
URL url = new URL(urlString);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
OutputStream os = conn.getOutputStream();
os.write(bodyString.getBytes("utf-8"));
os.flush();
os.close();
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
InputStream is = conn.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
System.out.println("響應内容:" + sb.toString());
} else {
System.out.println("響應碼:" + conn.getResponseCode());
}
}
}
HttpURLConnection 發起的 HTTP 請求比較原始,基本上算是對網絡傳輸層的一次淺層次的封裝;有了 HttpURLConnection 對象後,就可以擷取到輸出流,然後把要發送的内容發送出去;再通過輸入流讀取到伺服器端響應的内容;最後列印。
不過 HttpURLConnection 不支援 HTTP/2.0,為了解決這個問題,Java 9 的時候官方的标準庫增加了一個更進階别的 HttpClient,再發起 POST 請求就顯得高大上多了,不僅支援異步,還支援順滑的鍊式調用。
public class HttpClientDemo {
public static void main(String[] args) throws URISyntaxException {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI("https://postman-echo.com/post"))
.headers("Content-Type", "text/plain;charset=UTF-8")
.POST(HttpRequest.BodyPublishers.ofString("二哥牛逼"))
.build();
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(System.out::println)
.join();
}
}
Apache HttpComponents HttpClient 支援的特性也非常豐富:
基于标準、純淨的Java語言,實作了HTTP1.0和HTTP1.1;
以可擴充的面向對象的結構實作了HTTP全部的方法;
支援加密的HTTPS協定(HTTP通過SSL協定);
Request的輸出流可以避免流中内容體直接從socket緩沖到伺服器;
Response的輸入流可以有效的從socket伺服器直接讀取相應内容。
public class HttpComponentsDemo {
public static void main(String[] args) throws IOException, IOException, ParseException {
try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
HttpPost httpPost = new HttpPost("http://httpbin.org/post");
List<NameValuePair> nvps = new ArrayList<>();
nvps.add(new BasicNameValuePair("name", "二哥"));
httpPost.setEntity(new UrlEncodedFormEntity(nvps, Charset.forName("UTF-8")));
try (CloseableHttpResponse response2 = httpclient.execute(httpPost)) {
System.out.println(response2.getCode() + " " + EntityUtils.toString(response2.getEntity()));
}
}
}
}
OkHttp 是一個執行效率比較高的 HTTP 用戶端:
支援 HTTP/2.0,當多個請求對應同一個 Host 位址時,可共用同一個 Socket;
連接配接池可減少請求延遲;
支援 GZIP 壓縮,減少網絡傳輸的資料大小;
支援 Response 資料緩存,避免重複網絡請求;
public class OkHttpPostDemo {
public static final MediaType JSON = MediaType.get("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {
RequestBody body = RequestBody.create(json, JSON);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}
public static void main(String[] args) throws IOException {
OkHttpPostDemo example = new OkHttpPostDemo();
String json = "{'name':'二哥'}";
String response = example.post("https://httpbin.org/post", json);
System.out.println(response);
}
}
那今天介紹的這款輕量級的 HTTP 用戶端架構 Forest,正是基于 Httpclient和OkHttp 的,屏蔽了不同細節的 HTTP 元件庫所帶來的所有差異。
Forest 的字面意思是森林的意思,更内涵點的話,可以拆成For和Rest兩個單詞,也就是“為了Rest”(Rest為一種基于HTTP的架構風格)。 而合起來就是森林,森林由很多樹木花草組成(可以了解為各種不同的服務),它們表面上看獨立,實則在地下根莖交錯縱橫、互相連接配接依存,這樣看就有點現代分布式服務化的味道了。 最後,這兩個單詞反過來讀就像是Resultful。
項目位址:
https://gitee.com/dromara/forest雖然 star 數還不是很多,但 star 趨勢圖正在趨于爬坡階段,大家可以拿來作為一個練手項目,我覺得還是不錯的選擇。
Forest 本身是處理前端過程的架構,是對後端 HTTP API 架構的進一步封裝。