• Java中常见的HttpClient调用方式


    1. 背景

    在Java应用开发中,服务暴露接口的方式分为rpc和http两种方式。优雅的httpClient封装不仅能让程序work得更好,也能在一定程度上有性能优化。Java项目中调用第三方接口的方式有:

    • 通过JDK网络类Java.net.HttpURLConnection
    • 通过common封装HttpClient
    • Springboot提供的RestTemplate.

    各种方式的优缺点和适用范式,下面一一道来。

    2. HttpClient的封装调用范式

    2.1 HttpURLConnection

    缺点:不能直接使用池化技术,需要自行处理输入输出流

    1. package com.book.xw.web.util;
    2. import org.springframework.lang.Nullable;
    3. import java.io.*;
    4. import java.net.HttpURLConnection;
    5. import java.net.MalformedURLException;
    6. import java.net.URL;
    7. import java.net.URLConnection;
    8. public class HttpClientUtil {
    9. /**
    10. * http get请求,参数拼接在URL上
    11. */
    12. public static String doGet(String httpUrl){
    13. HttpURLConnection connection = null;
    14. InputStream is = null;
    15. BufferedReader br = null;
    16. StringBuffer result = new StringBuffer();
    17. try {
    18. //创建连接
    19. URL url = new URL(httpUrl);
    20. connection = (HttpURLConnection) url.openConnection();
    21. //设置请求方式
    22. connection.setRequestMethod("GET");
    23. //设置连接超时时间
    24. connection.setReadTimeout(15000);
    25. //开始连接
    26. connection.connect();
    27. //获取响应数据
    28. if (connection.getResponseCode() == 200) {
    29. //获取返回的数据
    30. is = connection.getInputStream();
    31. if (null != is) {
    32. br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
    33. String temp = null;
    34. while (null != (temp = br.readLine())) {
    35. result.append(temp);
    36. }
    37. }
    38. }
    39. } catch (Exception e) {
    40. e.printStackTrace();
    41. } finally {
    42. if (null != br) {
    43. try {
    44. br.close();
    45. } catch (IOException e) {
    46. e.printStackTrace();
    47. }
    48. }
    49. if (null != is) {
    50. try {
    51. is.close();
    52. } catch (IOException e) {
    53. e.printStackTrace();
    54. }
    55. }
    56. //关闭远程连接
    57. connection.disconnect();
    58. }
    59. return result.toString();
    60. }
    61. /**
    62. * Http post请求
    63. */
    64. public static String doPost(String httpUrl, String param) {
    65. StringBuffer result = new StringBuffer();
    66. //连接
    67. HttpURLConnection connection = null;
    68. OutputStream os = null;
    69. InputStream is = null;
    70. BufferedReader br = null;
    71. try {
    72. //创建连接对象
    73. URL url = new URL(httpUrl);
    74. //创建连接
    75. connection = (HttpURLConnection) url.openConnection();
    76. //设置请求方法
    77. connection.setRequestMethod("POST");
    78. //设置连接超时时间
    79. connection.setConnectTimeout(15000);
    80. //设置读取超时时间
    81. connection.setReadTimeout(15000);
    82. //DoOutput设置是否向httpUrlConnection输出,DoInput设置是否从httpUrlConnection读入,此外发送post请求必须设置这两个
    83. //设置是否可读取
    84. connection.setDoOutput(true);
    85. connection.setDoInput(true);
    86. //设置通用的请求属性
    87. connection.setRequestProperty("accept", "*/*");
    88. connection.setRequestProperty("connection", "Keep-Alive");
    89. connection.setRequestProperty("Content-Type","application/json;charset=utf-8");
    90. //拼装参数
    91. if (null != param && !param.equals("")) {
    92. //设置参数
    93. os = connection.getOutputStream();
    94. //拼装参数
    95. os.write(param.getBytes("UTF-8"));
    96. }
    97. //connection.connect();
    98. //读取响应
    99. if (connection.getResponseCode() == 200) {
    100. is = connection.getInputStream();
    101. if (null != is) {
    102. br = new BufferedReader(new InputStreamReader(is, "GBK"));
    103. String temp = null;
    104. while (null != (temp = br.readLine())) {
    105. result.append(temp);
    106. result.append("\r\n");
    107. }
    108. }
    109. }
    110. } catch (MalformedURLException e) {
    111. e.printStackTrace();
    112. } catch (Exception e) {
    113. e.printStackTrace();
    114. } finally {
    115. //关闭连接
    116. if(br!=null){
    117. try {
    118. br.close();
    119. } catch (IOException e) {
    120. e.printStackTrace();
    121. }
    122. }
    123. if(os!=null){
    124. try {
    125. os.close();
    126. } catch (IOException e) {
    127. e.printStackTrace();
    128. }
    129. }
    130. if(is!=null){
    131. try {
    132. is.close();
    133. } catch (IOException e) {
    134. e.printStackTrace();
    135. }
    136. }
    137. //关闭连接
    138. connection.disconnect();
    139. }
    140. return result.toString();
    141. }
    142. }

    2.2 apache common封装HttpClient

    引入Apache的commons jar包:


        commons-httpclient
        commons-httpclient
        3.1

    每次调用会创建一个httpClient对象,并且需要手动释放连接。 

    1. public class HttpClientUtil {
    2. public static String doGet(String url, String charset) {
    3. //1.生成HttpClient对象并设置参数
    4. HttpClient httpClient = new HttpClient();
    5. //设置Http连接超时为5秒
    6. httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(5000);
    7. //2.生成GetMethod对象并设置参数
    8. GetMethod getMethod = new GetMethod(url);
    9. //设置get请求超时为5秒
    10. getMethod.getParams().setParameter(HttpMethodParams.SO_TIMEOUT, 5000);
    11. //设置请求重试处理,用的是默认的重试处理:请求三次
    12. getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler());
    13. String response = "";
    14. //3.执行HTTP GET 请求
    15. try {
    16. int statusCode = httpClient.executeMethod(getMethod);
    17. //4.判断访问的状态码
    18. if (statusCode != HttpStatus.SC_OK) {
    19. System.err.println("请求出错:" + getMethod.getStatusLine());
    20. }
    21. //5.处理HTTP响应内容
    22. //HTTP响应头部信息,这里简单打印
    23. Header[] headers = getMethod.getResponseHeaders();
    24. for(Header h : headers) {
    25. System.out.println(h.getName() + "---------------" + h.getValue());
    26. }
    27. //读取HTTP响应内容,这里简单打印网页内容
    28. //读取为字节数组
    29. byte[] responseBody = getMethod.getResponseBody();
    30. response = new String(responseBody, charset);
    31. System.out.println("-----------response:" + response);
    32. //读取为InputStream,在网页内容数据量大时候推荐使用
    33. //InputStream response = getMethod.getResponseBodyAsStream();
    34. } catch (HttpException e) {
    35. e.printStackTrace();
    36. } catch (IOException e) {
    37. //发生网络异常
    38. System.out.println("发生网络异常!");
    39. } finally {
    40. //6.释放连接
    41. getMethod.releaseConnection();
    42. }
    43. return response;
    44. }
    45. public static String doPost(String url, JSONObject json){
    46. HttpClient httpClient = new HttpClient();
    47. PostMethod postMethod = new PostMethod(url);
    48. postMethod.addRequestHeader("accept", "*/*");
    49. postMethod.addRequestHeader("connection", "Keep-Alive");
    50. //设置json格式传送
    51. postMethod.addRequestHeader("Content-Type", "application/json;charset=GBK");
    52. //添加请求参数
    53. postMethod.addParameter("xxx", json.getString("xxx"));
    54. String res = "";
    55. try {
    56. int code = httpClient.executeMethod(postMethod);
    57. if (code == 200){
    58. res = postMethod.getResponseBodyAsString();
    59. System.out.println(res);
    60. }
    61. } catch (IOException e) {
    62. e.printStackTrace();
    63. }finally {
    64. postMethod.releaseConnection();
    65. }
    66. return res;
    67. }
    68. }

    2.3 CloseableHttpClient

    相比较HttpClient而言,可以使用连接池保持连接,并且过期自动释放。引入jar包


        org.apache.httpcomponents
        httpclient
        4.5.2

    1. package com.book.xw.common.util.utils;
    2. import com.alibaba.fastjson.JSON;
    3. import org.apache.http.Header;
    4. import org.apache.http.HttpStatus;
    5. import org.apache.http.client.config.RequestConfig;
    6. import org.apache.http.client.methods.CloseableHttpResponse;
    7. import org.apache.http.client.methods.HttpGet;
    8. import org.apache.http.client.methods.HttpPost;
    9. import org.apache.http.config.Registry;
    10. import org.apache.http.config.RegistryBuilder;
    11. import org.apache.http.conn.socket.ConnectionSocketFactory;
    12. import org.apache.http.conn.socket.PlainConnectionSocketFactory;
    13. import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
    14. import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
    15. import org.apache.http.entity.StringEntity;
    16. import org.apache.http.impl.client.CloseableHttpClient;
    17. import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
    18. import org.apache.http.impl.client.HttpClients;
    19. import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
    20. import org.apache.http.ssl.SSLContextBuilder;
    21. import org.apache.http.util.EntityUtils;
    22. import java.io.IOException;
    23. import java.security.KeyManagementException;
    24. import java.security.KeyStoreException;
    25. import java.security.NoSuchAlgorithmException;
    26. public class HttpCloseableClient {
    27. // 池化管理
    28. private static PoolingHttpClientConnectionManager poolConnManager = null;
    29. // 它是线程安全的,所有的线程都可以使用它一起发送http请求
    30. private static CloseableHttpClient httpClient;
    31. static {
    32. try {
    33. SSLContextBuilder builder = new SSLContextBuilder();
    34. builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
    35. SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build());
    36. // 配置同时支持 HTTP 和 HTPPS
    37. Registry socketFactoryRegistry = RegistryBuilder.create().register("http", PlainConnectionSocketFactory.getSocketFactory()).register("https", sslsf).build();
    38. // 初始化连接管理器
    39. poolConnManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
    40. // 同时最多连接数
    41. poolConnManager.setMaxTotal(640);
    42. // 设置最大路由
    43. poolConnManager.setDefaultMaxPerRoute(320);
    44. // 初始化httpClient
    45. httpClient = getConnection();
    46. } catch (NoSuchAlgorithmException e) {
    47. e.printStackTrace();
    48. } catch (KeyStoreException e) {
    49. e.printStackTrace();
    50. } catch (KeyManagementException e) {
    51. e.printStackTrace();
    52. }
    53. }
    54. public static CloseableHttpClient getConnection() {
    55. RequestConfig config = RequestConfig.custom().setConnectTimeout(5000)
    56. .setConnectionRequestTimeout(5000).setSocketTimeout(5000).build();
    57. CloseableHttpClient httpClient = HttpClients.custom()
    58. // 设置连接池管理
    59. .setConnectionManager(poolConnManager)
    60. .setDefaultRequestConfig(config)
    61. // 设置重试次数
    62. .setRetryHandler(new DefaultHttpRequestRetryHandler(2, false)).build();
    63. return httpClient;
    64. }
    65. public static String httpGet(String url) {
    66. HttpGet httpGet = new HttpGet(url);
    67. CloseableHttpResponse response = null;
    68. try {
    69. response = httpClient.execute(httpGet);
    70. String result = EntityUtils.toString(response.getEntity());
    71. int code = response.getStatusLine().getStatusCode();
    72. if (code == HttpStatus.SC_OK) {
    73. return result;
    74. } else {
    75. return null;
    76. }
    77. } catch (IOException e) {
    78. e.printStackTrace();
    79. } finally {
    80. try {
    81. if (response != null)
    82. response.close();
    83. } catch (IOException e) {
    84. e.printStackTrace();
    85. }
    86. }
    87. return null;
    88. }
    89. public static String post(String uri, Object params, Header... heads) {
    90. HttpPost httpPost = new HttpPost(uri);
    91. CloseableHttpResponse response = null;
    92. try {
    93. StringEntity paramEntity = new StringEntity(JSON.toJSONString(params));
    94. paramEntity.setContentEncoding("UTF-8");
    95. paramEntity.setContentType("application/json");
    96. httpPost.setEntity(paramEntity);
    97. if (heads != null) {
    98. httpPost.setHeaders(heads);
    99. }
    100. response = httpClient.execute(httpPost);
    101. int code = response.getStatusLine().getStatusCode();
    102. String result = EntityUtils.toString(response.getEntity());
    103. if (code == HttpStatus.SC_OK) {
    104. return result;
    105. } else {
    106. return null;
    107. }
    108. } catch (IOException e) {
    109. e.printStackTrace();
    110. } finally {
    111. try {
    112. if(response != null) {
    113. response.close();
    114. }
    115. } catch (IOException e) {
    116. e.printStackTrace();
    117. }
    118. }
    119. return null;
    120. }
    121. }

     2.4 OkHttp3

    pom文件引入依赖包


        com.squareup.okhttp3
        okhttp
        3.10.0

    1. @Slf4j
    2. public class OkHttpClient {
    3. private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
    4. private volatile static okhttp3.OkHttpClient client;
    5. private static final int MAX_IDLE_CONNECTION = Integer
    6. .parseInt(ConfigManager.get("httpclient.max_idle_connection"));
    7. private static final long KEEP_ALIVE_DURATION = Long
    8. .parseLong(ConfigManager.get("httpclient.keep_alive_duration"));
    9. private static final long CONNECT_TIMEOUT = Long.parseLong(ConfigManager.get("httpclient.connectTimeout"));
    10. private static final long READ_TIMEOUT = Long.parseLong(ConfigManager.get("httpclient. "));
    11. /**
    12. * 单例模式(双重检查模式)
    13. */
    14. private static okhttp3.OkHttpClient getInstance() {
    15. if (client == null) {
    16. synchronized (okhttp3.OkHttpClient.class) {
    17. if (client == null) {
    18. client = new okhttp3.OkHttpClient.Builder()
    19. .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
    20. .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
    21. .connectionPool(new ConnectionPool(MAX_IDLE_CONNECTION, KEEP_ALIVE_DURATION, TimeUnit.MINUTES))
    22. .build();
    23. }
    24. }
    25. }
    26. return client;
    27. }
    28. public static String doPost(String url, String json) throws IOException {
    29. RequestBody body = RequestBody.create(JSON, json);
    30. Request request = new Request.Builder()
    31. .url(url)
    32. .post(body)
    33. .build();
    34. try {
    35. Response response = OkHttpClient.getInstance().newCall(request).execute();
    36. if (response.isSuccessful()) {
    37. String result = response.body().string();
    38. log.info("syncPost response = {}, responseBody= {}", response, result);
    39. return result;
    40. }
    41. String result = response.body().string();
    42. log.info("syncPost response = {}, responseBody= {}", response, result);
    43. throw new IOException("三方接口返回http状态码为" + response.code());
    44. } catch (Exception e) {
    45. log.error("syncPost() url:{} have a ecxeption {}", url, e);
    46. throw new RuntimeException("syncPost() have a ecxeption {}" + e.getMessage());
    47. }
    48. }
    49. public static String doGet(String url, Map headParamsMap) throws IOException {
    50. Request request;
    51. final Request.Builder builder = new Request.Builder().url(url);
    52. try {
    53. if (!CollectionUtils.isEmpty(headParamsMap)) {
    54. final Iterator> iterator = headParamsMap.entrySet()
    55. .iterator();
    56. while (iterator.hasNext()) {
    57. final Map.Entry entry = iterator.next();
    58. builder.addHeader(entry.getKey(), (String) entry.getValue());
    59. }
    60. }
    61. request = builder.build();
    62. Response response = OkHttpClient.getInstance().newCall(request).execute();
    63. String result = response.body().string();
    64. log.info("syncGet response = {},responseBody= {}", response, result);
    65. if (!response.isSuccessful()) {
    66. throw new IOException("三方接口返回http状态码为" + response.code());
    67. }
    68. return result;
    69. } catch (Exception e) {
    70. log.error("remote interface url:{} have a ecxeption {}", url, e);
    71. throw new RuntimeException("三方接口返回异常");
    72. }
    73. }
    74. }

     2.5 RestTemplate

    1. // 先声明bean
    2. @Configuration
    3. public class RestTemplateConfig {
    4. @Bean
    5. public RestTemplate restTemplate(ClientHttpRequestFactory factory){
    6. return new RestTemplate(factory);
    7. }
    8. @Bean
    9. public ClientHttpRequestFactory simpleClientHttpRequestFactory(){
    10. SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
    11. factory.setConnectTimeout(15000);
    12. factory.setReadTimeout(5000);
    13. return factory;
    14. }
    15. }

    其余地方直接使用API即可:

    image-20220415205801633

  • 相关阅读:
    【Linux】翻山越岭——进程地址空间
    牛客月赛55 至至子的长链剖分
    Codeforces Round #800 (Div. 2)
    给爷肝!Alibaba架构师纯手工打造神仙级“2022版Java面试手册”
    linux 性能分析perf
    供应链和物流的自动化新时代
    Protocol Buffers 3 学习
    python 中内置函数ord()返回字符串的ASCII数值
    京东运营数据分析:2023年8月京东宠物主粮行业品牌销售排行榜
    第一章 基础算法(三)
  • 原文地址:https://blog.csdn.net/chenwiehuang/article/details/126735175