zoukankan      html  css  js  c++  java
  • HTTP连接客户端,选 HttpClient 还是 OkHttp ?

    作者:何甜甜在吗 

    https://www.jianshu.com/p/68c30beca612

    前言

    为什么会写这篇文章,起因于和朋友的聊天

    这又触及到我的知识盲区了,首先来一波面向百度学习,直接根据关键字httpclient和okhttp的区别、性能比较进行搜索,没有找到想要的答案,于是就去overstackflow上看看是不是有人问过这个问题,果然不会让你失望的

    所以从使用、性能、超时配置方面进行比较

    使用

    HttpClient和OkHttp一般用于调用其它服务,一般服务暴露出来的接口都为http,http常用请求类型就为GET、PUT、POST和DELETE,因此主要介绍这些请求类型的调用。

    HttpClient使用介绍

    使用HttpClient发送请求主要分为以下几步骤:

    • 创建 CloseableHttpClient对象或CloseableHttpAsyncClient对象,前者同步,后者为异步

    • 创建Http请求对象

    • 调用execute方法执行请求,如果是异步请求在执行之前需调用start方法

    创建连接:

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

    该连接为同步连接

    GET请求:

    @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请求,HttpClient调用execute方法发送GET请求

    PUT请求:

    @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请求:

    添加对象

    @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()));  
    }  
    

    该请求是一个创建对象的请求,需要传入一个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/学习/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请求:

    @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()));  
    }  
    

    请求的取消:

    @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);  //设置超时时间  
        //测试连接的取消  
    
        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()));  
    }  
    

    调用abort方法取消请求 执行结果:

    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发送请求主要分为以下几步骤:

    • 创建OkHttpClient对象

    • 创建Request对象

    • 将Request 对象封装为Call

    • 通过Call 来执行同步或异步请求,调用execute方法同步执行,调用enqueue方法异步执行

    创建连接:

    private OkHttpClient client = new OkHttpClient();  
    

    GET请求:

    @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请求:

    @Test  
    public void testPut() throws IOException {  
        String api = "/api/user";  
        String url = String.format("%s%s", BASE_URL, api);  
        //请求参数  
        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请求:

    添加对象

    @Test  
    public void testPost() throws IOException {  
        String api = "/api/user";  
        String url = String.format("%s%s", BASE_URL, api);  
        //请求参数  
        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请求  
               .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/学习/docker_practice.pdf")))  
                .build();  
        Request request = new Request.Builder()  
                .url(url)  
                .post(requestBody)  //默认为GET请求,可以不写  
                .build();  
        final Call call = client.newCall(request);  
        Response response = call.execute();  
        System.out.println(response.body().string());  
    }  
    

    通过addFormDataPart方法模拟表单方式上传文件

    DELETE请求:

    @Test  
    public void testDelete() throws IOException {  
      String url = String.format("%s%s", BASE_URL, api);  
      //请求参数  
      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());  
    }  
    

    请求的取消:

    @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();  
        //测试连接的取消  
        while (true) {  
             //1分钟获取不到结果就取消请求  
            if (System.currentTimeMillis() - start > 1000) {  
                call.cancel();  
                System.out.println("task canceled");  
                break;  
            }  
        }  
    
        System.out.println(response.body().string());  
    }  
    

    调用cancel方法进行取消 测试结果:

    task canceled  
    cost 9110 msc  
    
    java.net.SocketException: socket closed...【省略】  
    

    小结

    OkHttp使用build模式创建对象来的更简洁一些,并且使用.post/.delete/.put/.get方法表示请求类型,不需要像HttpClient创建HttpGet、HttpPost等这些方法来创建请求类型。

    依赖包上,如果HttpClient需要发送异步请求、实现文件上传,需要额外的引入异步请求依赖。

     <!---文件上传-->  
     <dependency>  
         <groupId>org.apache.httpcomponents</groupId>  
         <artifactId>httpmime</artifactId>  
         <version>4.5.3</version>  
     </dependency>  
     <!--异步请求-->  
     <dependency>  
         <groupId>org.apache.httpcomponents</groupId>  
         <artifactId>httpasyncclient</artifactId>  
         <version>4.5.3</version>  
     </dependency>  
    

    请求的取消,HttpClient使用abort方法,OkHttp使用cancel方法,都挺简单的,如果使用的是异步client,则在抛出异常时调用取消请求的方法即可。关注微信公众号:Java技术栈,在后台回复:http,可以获取我整理的 N 篇 HTTP 教程,都是干货。

    超时设置

    HttpClient超时设置:

    在HttpClient4.3+版本以上,超时设置通过RequestConfig进行设置

    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);  //设置超时时间  
    

    超时时间是设置在请求类型HttpGet上,而不是HttpClient上

    OkHttp超时设置:

    直接在OkHttp上进行设置

    private OkHttpClient client = new OkHttpClient.Builder()  
            .connectTimeout(60, TimeUnit.SECONDS)//设置连接超时时间  
            .readTimeout(60, TimeUnit.SECONDS)//设置读取超时时间  
            .build();  
    

    小结:

    如果client是单例模式,HttpClient在设置超时方面来的更灵活,针对不同请求类型设置不同的超时时间,OkHttp一旦设置了超时时间,所有请求类型的超时时间也就确定

    HttpClient和OkHttp性能比较

    测试环境:

    • CPU 六核

    • 内存 8G

    • windows10

    每种测试用例都测试五次,排除偶然性

    client连接为单例:

    client连接不为单例:

    单例模式下,HttpClient的响应速度要更快一些,单位为毫秒,性能差异相差不大

    非单例模式下,OkHttp的性能更好,HttpClient创建连接比较耗时,因为多数情况下这些资源都会写成单例模式,因此图一的测试结果更具有参考价值。

    总结

    OkHttp和HttpClient在性能和使用上不分伯仲,根据实际业务选择即可。

    END
    
    【推荐阅读】
    再见,Navicat!这个IDEA的兄弟,真香!
    JAVA 线上故障排查完整套路!牛掰!
    
    浅析 VO、DTO、DO、PO 的概念、区别和用处!
    
    再见!Lombok ...
    
    优秀的代码都是如何分层的?
    
    阿里规定超过三张表禁止join,为啥?
    
    国产芯片制造有多烂?十年饮冰,热血难凉
    
    
    
  • 相关阅读:
    一、docker安装CentOS7
    c#使用资源文件完成国际化
    .netcore 读取ansi编码
    省市区数据库
    .netcore2.0发送邮件
    使用py,根据日志记录自动生成周报
    mysql监控每一条执行的sql语句
    根据json生成c#实体类
    使用.net core efcore根据数据库结构自动生成实体类
    winform,同个程序只允许启动一次
  • 原文地址:https://www.cnblogs.com/Java-Road/p/13236409.html
Copyright © 2011-2022 走看看