<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          HTTP客戶端連接,選擇HttpClient還是OkHttp?

          共 9237字,需瀏覽 19分鐘

           ·

          2021-09-15 17:18


          作者:何甜甜在嗎

          https://juejin.im/post/6844904040644476941)

          寫在前面

          為什么會(huì)寫這篇文章,起因于和朋友的聊天

          這又觸及到我的知識(shí)盲區(qū)了,首先來一波面向百度學(xué)習(xí),直接根據(jù)關(guān)鍵字 httpclient 和 okhttp 的區(qū)別、性能比較進(jìn)行搜索,沒有找到想要的答案,于是就去 overstackflow 上看看是不是有人問過這個(gè)問題,果然不會(huì)讓你失望的

          所以從使用、性能、超時(shí)配置方面進(jìn)行比較

          使用

          HttpClient 和 OkHttp 一般用于調(diào)用其它服務(wù),一般服務(wù)暴露出來的接口都為 http,http 常用請(qǐng)求類型就為 GET、PUT、POST 和 DELETE,因此主要介紹這些請(qǐng)求類型的調(diào)用

          HttpClient 使用介紹

          使用 HttpClient 發(fā)送請(qǐng)求主要分為一下幾步驟:

          • 創(chuàng)建 CloseableHttpClient 對(duì)象或 CloseableHttpAsyncClient 對(duì)象,前者同步,后者為異步

          • 創(chuàng)建 Http 請(qǐng)求對(duì)象

          • 調(diào)用 execute 方法執(zhí)行請(qǐng)求,如果是異步請(qǐng)求在執(zhí)行之前需調(diào)用 start 方法

          創(chuàng)建連接:

          CloseableHttpClient httpClient = HttpClientBuilder.create().build();

          該連接為同步連接

          GET 請(qǐng)求:

          @Test
          public void testGet() throws IOException {
          String api = "/api/files/1";
          String url = String.format("%s%s", BASE\_URL, api);
          HttpGet httpGet = new HttpGet(url);
          CloseableHttpResponse response = httpClient.execute(httpGet);
          System.out.println(EntityUtils.toString(response.getEntity()));
          }

          使用 HttpGet 表示該連接為 GET 請(qǐng)求,HttpClient 調(diào)用 execute 方法發(fā)送 GET 請(qǐng)求

          PUT 請(qǐng)求:

          @Test
          public void testPut() throws IOException {
          String api = "/api/user";
          String url = String.format("%s%s", BASE\_URL, api);
          HttpPut httpPut = new HttpPut(url);
          UserVO userVO = UserVO.builder().name("h2t").id(16L).build();
          httpPut.setHeader("Content-Type", "application/json;charset=utf8");
          httpPut.setEntity(new StringEntity(JSONObject.toJSONString(userVO), "UTF-8"));
          CloseableHttpResponse response = httpClient.execute(httpPut);
          System.out.println(EntityUtils.toString(response.getEntity()));
          }

          POST 請(qǐng)求:

          • 添加對(duì)象

          @Test
          public void testPost() throws IOException {
          String api = "/api/user";
          String url = String.format("%s%s", BASE\_URL, api);
          HttpPost httpPost = new HttpPost(url);
          UserVO userVO = UserVO.builder().name("h2t2").build();
          httpPost.setHeader("Content-Type", "application/json;charset=utf8");
          httpPost.setEntity(new StringEntity(JSONObject.toJSONString(userVO), "UTF-8"));
          CloseableHttpResponse response = httpClient.execute(httpPost);
          System.out.println(EntityUtils.toString(response.getEntity()));
          }

          該請(qǐng)求是一個(gè)創(chuàng)建對(duì)象的請(qǐng)求,需要傳入一個(gè) json 字符串

          • 上傳文件

          @Test
          public void testUpload1() throws IOException {
          String api = "/api/files/1";
          String url = String.format("%s%s", BASE\_URL, api);
          HttpPost httpPost = new HttpPost(url);
          File file = new File("C:/Users/hetiantian/Desktop/學(xué)習(xí)/docker\_practice.pdf");
          FileBody fileBody = new FileBody(file);
          MultipartEntityBuilder builder = MultipartEntityBuilder.create();
          builder.setMode(HttpMultipartMode.BROWSER\_COMPATIBLE);
          builder.addPart("file", fileBody); //addPart上傳文件
          HttpEntity entity = builder.build();
          httpPost.setEntity(entity);
          CloseableHttpResponse response = httpClient.execute(httpPost);
          System.out.println(EntityUtils.toString(response.getEntity()));
          }

          通過 addPart 上傳文件

          DELETE 請(qǐng)求:

          @Test
          public void testDelete() throws IOException {
          String api = "/api/user/12";
          String url = String.format("%s%s", BASE\_URL, api);
          HttpDelete httpDelete = new HttpDelete(url);
          CloseableHttpResponse response = httpClient.execute(httpDelete);
          System.out.println(EntityUtils.toString(response.getEntity()));
          }

          請(qǐng)求的取消:

          @Test
          public void testCancel() throws IOException {
          String api = "/api/files/1";
          String url = String.format("%s%s", BASE\_URL, api);
          HttpGet httpGet = new HttpGet(url);
          httpGet.setConfig(requestConfig); //設(shè)置超時(shí)時(shí)間
          //測(cè)試連接的取消

          long begin = System.currentTimeMillis();
          CloseableHttpResponse response = httpClient.execute(httpGet);
          while (true) {
          if (System.currentTimeMillis() - begin > 1000) {
          httpGet.abort();
          System.out.println("task canceled");
          break;
          }
          }

          System.out.println(EntityUtils.toString(response.getEntity()));
          }

          調(diào)用 abort 方法取消請(qǐng)求 執(zhí)行結(jié)果:

          task canceled
          cost 8098 msc
          Disconnected from the target VM, address: '127.0.0.1:60549', transport: 'socket'

          java.net.SocketException: socket closed...【省略】

          OkHttp 使用

          使用 OkHttp 發(fā)送請(qǐng)求主要分為一下幾步驟:

          • 創(chuàng)建 OkHttpClient 對(duì)象

          • 創(chuàng)建 Request 對(duì)象

          • 將 Request 對(duì)象封裝為 Call

          • 通過 Call 來執(zhí)行同步或異步請(qǐng)求,調(diào)用 execute 方法同步執(zhí)行,調(diào)用 enqueue 方法異步執(zhí)行

          創(chuàng)建連接:

          private OkHttpClient client = new OkHttpClient();

          GET 請(qǐng)求:

          @Test
          public void testGet() throws IOException {
          String api = "/api/files/1";
          String url = String.format("%s%s", BASE\_URL, api);
          Request request = new Request.Builder()
          .url(url)
          .get()
          .build();
          final Call call = client.newCall(request);
          Response response = call.execute();
          System.out.println(response.body().string());
          }

          PUT 請(qǐng)求:

          @Test
          public void testPut() throws IOException {
          String api = "/api/user";
          String url = String.format("%s%s", BASE\_URL, api);
          //請(qǐng)求參數(shù)
          UserVO userVO = UserVO.builder().name("h2t").id(11L).build();
          RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"),
          JSONObject.toJSONString(userVO));
          Request request = new Request.Builder()
          .url(url)
          .put(requestBody)
          .build();
          final Call call = client.newCall(request);
          Response response = call.execute();
          System.out.println(response.body().string());
          }

          POST 請(qǐng)求:

          • 添加對(duì)象

          @Test
          public void testPost() throws IOException {
          String api = "/api/user";
          String url = String.format("%s%s", BASE\_URL, api);
          //請(qǐng)求參數(shù)
          JSONObject json = new JSONObject();
          json.put("name", "hetiantian");
          RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), String.valueOf(json));
          Request request = new Request.Builder()
          .url(url)
          .post(requestBody) //post請(qǐng)求
          .build();
          final Call call = client.newCall(request);
          Response response = call.execute();
          System.out.println(response.body().string());
          }

          • 上傳文件

          @Test
          public void testUpload() throws IOException {
          String api = "/api/files/1";
          String url = String.format("%s%s", BASE\_URL, api);
          RequestBody requestBody = new MultipartBody.Builder()
          .setType(MultipartBody.FORM)
          .addFormDataPart("file", "docker\_practice.pdf",
          RequestBody.create(MediaType.parse("multipart/form-data"),
          new File("C:/Users/hetiantian/Desktop/學(xué)習(xí)/docker\_practice.pdf")))
          .build();
          Request request = new Request.Builder()
          .url(url)
          .post(requestBody) //默認(rèn)為GET請(qǐng)求,可以不寫
          .build();
          final Call call = client.newCall(request);
          Response response = call.execute();
          System.out.println(response.body().string());
          }

          通過 addFormDataPart 方法模擬表單方式上傳文件

          DELETE 請(qǐng)求:

          @Test
          public void testDelete() throws IOException {
          String url = String.format("%s%s", BASE\_URL, api);
          //請(qǐng)求參數(shù)
          Request request = new Request.Builder()
          .url(url)
          .delete()
          .build();
          final Call call = client.newCall(request);
          Response response = call.execute();
          System.out.println(response.body().string());
          }

          請(qǐng)求的取消:

          @Test
          public void testCancelSysnc() throws IOException {
          String api = "/api/files/1";
          String url = String.format("%s%s", BASE\_URL, api);
          Request request = new Request.Builder()
          .url(url)
          .get()
          .build();
          final Call call = client.newCall(request);
          Response response = call.execute();
          long start = System.currentTimeMillis();
          //測(cè)試連接的取消
          while (true) {
          //1分鐘獲取不到結(jié)果就取消請(qǐng)求
          if (System.currentTimeMillis() - start > 1000) {
          call.cancel();
          System.out.println("task canceled");
          break;
          }
          }

          System.out.println(response.body().string());
          }

          調(diào)用 cancel 方法進(jìn)行取消 測(cè)試結(jié)果:

          task canceled
          cost 9110 msc

          java.net.SocketException: socket closed...【省略】

          小結(jié)
          • OkHttp 使用 build 模式創(chuàng)建對(duì)象來的更簡(jiǎn)潔一些,并且使用. post/.delete/.put/.get 方法表示請(qǐng)求類型,不需要像 HttpClient 創(chuàng)建 HttpGet、HttpPost 等這些方法來創(chuàng)建請(qǐng)求類型

          • 依賴包上,如果 HttpClient 需要發(fā)送異步請(qǐng)求、實(shí)現(xiàn)文件上傳,需要額外的引入異步請(qǐng)求依賴

           <!---文件上傳-->
          <dependency>
          <groupId>org.apache.httpcomponents</groupId>
          <artifactId>httpmime</artifactId>
          <version>4.5.3</version>
          </dependency>
          <!--異步請(qǐng)求-->
          <dependency>
          <groupId>org.apache.httpcomponents</groupId>
          <artifactId>httpasyncclient</artifactId>
          <version>4.5.3</version>
          </dependency>

          • 請(qǐng)求的取消,HttpClient 使用 abort 方法,OkHttp 使用 cancel 方法,都挺簡(jiǎn)單的,如果使用的是異步 client,則在拋出異常時(shí)調(diào)用取消請(qǐng)求的方法即可

          超時(shí)設(shè)置

          HttpClient 超時(shí)設(shè)置:
          在 HttpClient4.3 + 版本以上,超時(shí)設(shè)置通過 RequestConfig 進(jìn)行設(shè)置

          private CloseableHttpClient httpClient = HttpClientBuilder.create().build();
          private RequestConfig requestConfig = RequestConfig.custom()
          .setSocketTimeout(60 \* 1000)
          .setConnectTimeout(60 \* 1000).build();
          String api = "/api/files/1";
          String url = String.format("%s%s", BASE\_URL, api);
          HttpGet httpGet = new HttpGet(url);
          httpGet.setConfig(requestConfig); //設(shè)置超時(shí)時(shí)間

          超時(shí)時(shí)間是設(shè)置在請(qǐng)求類型 HttpGet 上,而不是 HttpClient 上

          OkHttp 超時(shí)設(shè)置:
          直接在 OkHttp 上進(jìn)行設(shè)置

          private OkHttpClient client = new OkHttpClient.Builder()
          .connectTimeout(60, TimeUnit.SECONDS)//設(shè)置連接超時(shí)時(shí)間
          .readTimeout(60, TimeUnit.SECONDS)//設(shè)置讀取超時(shí)時(shí)間
          .build();

          小結(jié):
          如果 client 是單例模式,HttpClient 在設(shè)置超時(shí)方面來的更靈活,針對(duì)不同請(qǐng)求類型設(shè)置不同的超時(shí)時(shí)間,OkHttp 一旦設(shè)置了超時(shí)時(shí)間,所有請(qǐng)求類型的超時(shí)時(shí)間也就確定

          HttpClient 和 OkHttp 性能比較

          測(cè)試環(huán)境:

          • CPU 六核

          • 內(nèi)存 8G

          • windows10

          每種測(cè)試用例都測(cè)試五次,排除偶然性

          client 連接為單例:

           client 連接不為單例:  單例模式下,HttpClient 的響應(yīng)速度要更快一些,單位為毫秒,性能差異相差不大 非單例模式下,OkHttp 的性能更好,HttpClient 創(chuàng)建連接比較耗時(shí),因?yàn)槎鄶?shù)情況下這些資源都會(huì)寫成單例模式,因此圖一的測(cè)試結(jié)果更具有參考價(jià)值

          總結(jié)

          OkHttp 和 HttpClient 在性能和使用上不分伯仲,根據(jù)實(shí)際業(yè)務(wù)選擇即可
          最后附:示例代碼:https://github.com/TiantianUpup/http-call,歡迎 fork 與 star*
          好久沒有對(duì)外輸出文章了

          主要是寫的前兩篇沒有人看,受打擊了,急需網(wǎng)友的肯定【點(diǎn)贊呀


          (完)


          瀏覽 19
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲国产色情 | 亚洲黄色小电影 | 69精品又硬又爽又粗少妇 | 五月丁香婷婷社区 | 豆花av在线观看 豆花AV在线入口 豆花精品在线视频 |