优秀的编程知识分享平台

网站首页 > 技术文章 正文

HTTP通讯框架选型HttpClient/OkHttp

nanyue 2024-08-03 18:12:00 技术文章 5 ℃

最近在总结项目中,其中包含调用第三方做HTTP通讯的处理,主要涉及了两类通讯框架的依赖,即HttpClient和OkHttp;

虽然,HTTP协议目前已经进化到HTTP2了,但有没有发现,在HTTP客户端调用这一层很少用到HTTP2的特性;

下面就HttpClient及OkHttp在项目中的使用、及遇到的问题做简单说明。

HttpClient

传送门

https://hc.apache.org

说明

HttpClient被用来提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包,并且它支持HTTP协议最新的版本和建议。

特性:

基于标准、纯净的java语言。实现了Http1.0和Http1.1

以可扩展的面向对象的结构实现了Http全部的方法(GET, POST, PUT, DELETE, HEAD, OPTIONS, and TRACE)。

支持HTTPS协议。

通过Http代理建立透明的连接。

利用CONNECT方法通过Http代理建立隧道的https连接。

Basic, Digest, NTLMv1, NTLMv2, NTLM2 Session, SNPNEGO/Kerberos认证方案。

插件式的自定义认证方案。

便携可靠的套接字工厂使它更容易地使用第三方解决方案。

连接管理器支持多线程应用。支持设置最大连接数,同时支持设置每个主机的最大连接数,发现并关闭过期的连接。

自动处理Set-Cookie中的Cookie。

插件式的自定义Cookie策略。

Request的输出流可以避免流中内容直接缓冲到socket服务器。

Response的输入流可以有效的从socket服务器直接读取相应内容。

在http1.0和http1.1中利用KeepAlive保持持久连接。

直接获取服务器发送的response code和 headers。

设置连接超时的能力。

实验性的支持http1.1 response caching。

源代码基于Apache License 可免费获取。


使用步骤:

创建HttpClient对象

创建请求方法的实例,并指定请求URL。如果需要发送GET请求,创建HttpGet对象;如果需要发送POST请求,创建HttpPost对象

如果需要发送请求参数,可调用HttpGet、HttpPost共同的setParams(HttpParams params)方法来添加请求参数;对于HttpPost对象而言,也可调用setEntity(HttpEntity entity)方法来设置请求参数

调用HttpClient对象的execute(HttpUriRequest request)方法发送请求,该方法返回一个HttpResponse

调用HttpResponse的getAllHeaders()、getHeaders(String name)等方法可获取服务器的响应头;调用HttpResponse的getEntity()方法可获取HttpEntity对象,该对象包装了服务器的响应内容。程序可通过该对象获取服务器的响应内容

释放连接。无论执行方法是否成功,都必须释放连接

Get

public void get() {   
    CloseableHttpClient httpclient = HttpClients.createDefault();   
    try {   
        // 创建httpget.     
        HttpGet httpget = new HttpGet("http://www.baidu.com/");   
        logger.info("executing request " + httpget.getURI());   
        // 执行get请求.     
        CloseableHttpResponse response = httpclient.execute(httpget);   
        try {   
            // 获取响应实体     
            HttpEntity entity = response.getEntity();   
            System.out.println("--------------------------------------");   
            // 打印响应状态     
            System.out.println(response.getStatusLine());   
            if (entity != null) {   
                // 打印响应内容长度     
                System.out.println("Response content length: " + entity.getContentLength());   
                // 打印响应内容     
                System.out.println("Response content: " + EntityUtils.toString(entity));   
            }   
            System.out.println("------------------------------------");   
        } finally {   
            response.close();   
        }   
    } catch (ClientProtocolException e) {   
        e.printStackTrace();   
    } catch (ParseException e) {   
        e.printStackTrace();   
    } catch (IOException e) {   
        e.printStackTrace();   
    } finally {   
        // 关闭连接,释放资源     
        try {   
            httpclient.close();   
        } catch (IOException e) {   
            e.printStackTrace();   
        }   
    }   
}  

Post

/** 
 * 发送 post请求访问本地应用并根据传递参数不同返回不同结果 
 */   
public void post() {   
    // 创建默认的httpClient实例.     
    CloseableHttpClient httpclient = HttpClients.createDefault();   
    // 创建httppost     
    HttpPost httppost = new HttpPost("http://localhost:8080/myDemo/Ajax/serivceJ.action");   
    // 创建参数队列     
    List<NameValuePair> formparams = new ArrayList<NameValuePair>();   
    formparams.add(new BasicNameValuePair("type", "house"));   
    UrlEncodedFormEntity uefEntity;   
    try {   
        uefEntity = new UrlEncodedFormEntity(formparams, "UTF-8");   
        httppost.setEntity(uefEntity);   
        System.out.println("executing request " + httppost.getURI());   
        CloseableHttpResponse response = httpclient.execute(httppost);   
        try {   
            HttpEntity entity = response.getEntity();   
            if (entity != null) {   
                System.out.println("--------------------------------------");   
                System.out.println("Response content: " + EntityUtils.toString(entity, "UTF-8"));   
                System.out.println("--------------------------------------");   
            }   
        } finally {   
            response.close();   
        }   
    } catch (ClientProtocolException e) {   
        e.printStackTrace();   
    } catch (UnsupportedEncodingException e1) {   
        e1.printStackTrace();   
    } catch (IOException e) {   
        e.printStackTrace();   
    } finally {   
        // 关闭连接,释放资源     
        try {   
            httpclient.close();   
        } catch (IOException e) {   
            e.printStackTrace();   
        }   
    }   
}  

文件处理

上传文件

/** 
 * 发送 post请求访问本地应用并根据传递参数不同返回不同结果 
 */   
public void post() {   
    // 创建默认的httpClient实例.     
    CloseableHttpClient httpclient = HttpClients.createDefault();   
    // 创建httppost     
    HttpPost httppost = new HttpPost("http://localhost:8080/myDemo/Ajax/serivceJ.action");   
    // 创建参数队列     
    List<NameValuePair> formparams = new ArrayList<NameValuePair>();   
    formparams.add(new BasicNameValuePair("type", "house"));   
    UrlEncodedFormEntity uefEntity;   
    try {   
        uefEntity = new UrlEncodedFormEntity(formparams, "UTF-8");   
        httppost.setEntity(uefEntity);   
        System.out.println("executing request " + httppost.getURI());   
        CloseableHttpResponse response = httpclient.execute(httppost);   
        try {   
            HttpEntity entity = response.getEntity();   
            if (entity != null) {   
                System.out.println("--------------------------------------");   
                System.out.println("Response content: " + EntityUtils.toString(entity, "UTF-8"));   
                System.out.println("--------------------------------------");   
            }   
        } finally {   
            response.close();   
        }   
    } catch (ClientProtocolException e) {   
        e.printStackTrace();   
    } catch (UnsupportedEncodingException e1) {   
        e1.printStackTrace();   
    } catch (IOException e) {   
        e.printStackTrace();   
    } finally {   
        // 关闭连接,释放资源     
        try {   
            httpclient.close();   
        } catch (IOException e) {   
            e.printStackTrace();   
        }   
    }   
}

下载文件

public class FileFetchEngine {

    public String[] fetch(String url,String sysId, String fileType, String filepath, String date)
            throws Exception {
        String tmpfilepath = filepath ;
        CloseableHttpClient httpclient = HttpClients.createDefault();
        FileOutputStream out = null;
        try {
            HttpPost httppost = new HttpPost(url);
            HttpEntity reqEntity = MultipartEntityBuilder.create().build();
            httppost.setEntity(reqEntity);
            
            CloseableHttpResponse response = httpclient.execute(httppost);
            try {
                String ret = "" ;
                HttpEntity resEntity = response.getEntity();
                if (resEntity == null) {
                    return null ;
                }
                if(resEntity.getContentType()==null) {
                    return null ;
                }
                String contentType = resEntity.getContentType().getValue().split(";")[0] ;
                if("text/xml".equals(contentType)) {
                    byte[] content = new byte[(int) resEntity.getContentLength()];
                    resEntity.getContent().read(content);
                    ret = new String(content, "UTF-8");
                    
                    JSONObject retJson = JSONObject.fromObject(ret) ;
                    String retCode = retJson.getString("retCode") ;
                    String retMsg = retJson.getString("retMsg") ;
                    return new String[]{retCode,retMsg};
                } else {
                    File dir = new File(filepath);
                    if(!dir.exists()) {
                        dir.mkdirs() ;
                    }
                    
                    filepath += File.separator+fileType+"_"+date+".txt.gz" ;
                    out = new FileOutputStream(filepath);  
                    int c;
                    while ((c = resEntity.getContent().read()) != -1) {
                        out.write(c);
                    }
                }
                
                try {
                    TarAndGzUtil.unGz(filepath, tmpfilepath);
                } catch (Exception e) { 
                    throw new Exception("文件解压失败!"+e)  ;
                }
                
                EntityUtils.consume(resEntity);
                return new String[]{"0000","下载成功!",filepath};
            } finally {
                response.close();
            }
        } finally {
            try {  
                if (out != null)  
                    out.close();  
            } catch (IOException e) {  
                Enviroment.getEnv().getLogBean().error(e);  
            }
            httpclient.close();
        }
    }
    
}


参考资料

https://blog.csdn.net/w372426096/article/details/82713315

OkHttp

传送门

传送门:https://square.github.io/okhttp/

代码片段:https://square.github.io/okhttp/recipes/

说明

OkHttp是一个高效的HTTP客户端


特性:

支持HTTP/2,允许所有同一个主机地址的请求共享同一个socket连接

连接池减少请求延时

透明的GZIP压缩减少响应数据的大小

缓存响应内容,避免出现一些完全重复的请求


使用步骤:

创建OkHttpClient对象

创建Request对象

将Request 对象封装为Call

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

maven坐标

<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>3.12.13</version>
</dependency>

Get(同步)

为什么写异步的处理?因为我在实际业务开发中始终没有遇到异步HTTP处理的场景;

以下是官网的例子

private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url("https://publicobject.com/helloworld.txt")
        .build();

    try (Response response = client.newCall(request).execute()) {
      if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

      Headers responseHeaders = response.headers();
      for (int i = 0; i < responseHeaders.size(); i++) {
        System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
      }

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

Post(项目中用得最多的)

String url = "" ;
String content = "{}";
OkHttpClient client = new OkHttpClient.Builder()
        .connectTimeout(10, TimeUnit.SECONDS)
        .writeTimeout(10, TimeUnit.SECONDS)
        .readTimeout(30, TimeUnit.SECONDS)
        .build();
MediaType mediaType = MediaType.parse("application/json;charset=utf-8");
RequestBody body = RequestBody.create(mediaType, content.toString());
Request request = new Request.Builder()
        .url(url)
        .method("POST", body)
        .addHeader("Content-Type", "application/json;charset=utf-8")
        .build();
try {
    Response response = client.newCall(request).execute();
    String responseMsg = "";
    if (response.isSuccessful()) {
        responseMsg = response.body() == null ? "" : response.body().string();
        JsonObject respJsonObject = new JsonParser()
            .parse(responseMsg)
            .getAsJsonObject();
        // 业务处理
    } else {
        responseMsg = "交易端通讯失败!";
        // 错误处理
    }
    IOUtils.closeQuietly(response);
} catch (Throwable e) {
    // 异常处理
}

注意

  1. 每次response都必须关闭掉,否则会报警,告诉你太多响应没有关闭;
  2. OkHttpClient可以为单例模式,也可以不是;
  3. 非单例模式下,OkHttpClient会维护一个单独的线程池,高并发下会出现连接池线程增多;因此OkHttpClient可以采用同一个连接池的管理对象;或直接使用单例模式;
  4. OkHttpClient中的超时时间设置也很有必要,默认超时时间往往会影响业务效率;

文件处理

上传文件

  public static final MediaType MEDIA_TYPE_MARKDOWN
      = MediaType.parse("text/x-markdown; charset=utf-8");

  private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    File file = new File("README.md");

    Request request = new Request.Builder()
        .url("https://api.github.com/markdown/raw")
        .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))
        .build();

    try (Response response = client.newCall(request).execute()) {
      if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

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

下载文件

也可查看Get方式的处理,本身就是一个文件下载的例子;

在get的基础上,获取到response后将response返回的stream写到文件里面即可

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {
    Request request = new Request.Builder()
        .url("https://publicobject.com/helloworld.txt")
        .build();

    try (Response response = client.newCall(request).execute()) {
      if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

      File file = new File("");
      writeFile(file,response);
      
    }
}

private void writeFile(File file,Response response) {
    OutputStream outputStream = null;
    InputStream inputStream = response.body().byteStream();
    try {
        outputStream = new FileOutputStream(file);
        int len = 0;
        byte[] buffer = new byte[1024*10];
        while ((len = inputStream.read(buffer))!=-1){
            outputStream.write(buffer,0,len);
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }finally {
        try {
            if(inputStream != null)
                inputStream.close();
            if(outputStream != null)
                outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}


你看,奇怪的知识是不是又增加了!

最近发表
标签列表