[Java]_[中级]_[使用okhttp3和HttpClient代理访问外部网络]

场景

  1. Javahttp库常用的有HttpClientOkhttp3, 如果公司有限制网络访问,需要代理才可以访问外网,那么如何使用代理Proxy
<dependency>
	<groupId>org.apache.httpcomponents</groupId>
	<artifactId>httpclient</artifactId>
	<version>4.5.13</version>
</dependency>

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

说明

  1. 从性能方面来说,Okhttp3更出色一点。因为从我本机运行两个库的访问, Okhttp3会快一些。

  2. 从易用性来说,Ohttp3更好。OkHttpClient只需要在Builder里调用proxy(Proxy)传递一个Proxy对象之后即可。之后访问其他外网,就直接创建外网的Request即可,非常方便。 代理和非代理都用统一的方法。

    OkHttpClient.Builder builder = client.newBuilder();
    builder.proxy(new Proxy(Proxy.Type.HTTP, 
    	new InetSocketAddress(hostName, Integer.parseInt(hostPort))));
    
    ...
    Response response = getClient().newCall(request).execute()
    
  3. HttpClient访问[1]则需要创建两个对象: 需要访问的主机HttpHost和URL里的请求路径和查询参数的HttpGet对象。 每次请求都需要传递这两个参数。而如果非代理的话,就需要调用另外的重载方法。

      URL url = RequestUtils.getUrlOrNull(hostUrl);
    
      HttpHost target = new HttpHost(url.getHost(),getPort(url),url.getProtocol());
      HttpGet request = new HttpGet("/user?name=xxx");
    //  HttpGet httpGet = new HttpGet(hostUrl); // 非代理
    
    ...
    httpClient.execute(target, request) // 代理
    // httpClient.execute(httpGet); // 非代理
    
  4. 注意,出于创建Client比较废资源,okhttp3还是HttpClient官方都是推荐创建单例的OkHttpClientHttpClient对象来使用。对于CloseableHttpClient在使用完之后还需要调用close()关闭。OkHttpClient官方例子也没有调用销毁方法。

  5. 从源码设计上来说,Okhttp3在发送数据时,使用了自带的线程池,我们想用自己的线程池代替都不行。这个对某些线程数有严格限制的程序(Linux)非常不友好,甚至不能用,因为它的线程不受宿主程序控制。

    // 在类 RealConnectionPool里有这段代码
    static {
        executor = new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue(), Util.threadFactory("OkHttp ConnectionPool", true));
    }
    

例子

  1. 以下是使用okhttp3HttpClient的目前最佳做法.

OkHttpClientCaller


import Pair;
import StringUtils;
import HttpHelper;
import okhttp3.*;
import org.apache.log4j.Logger;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.Map;

public class OkHttpClientCaller {

    private static Logger logger = Logger.getLogger(OkHttpClientCaller.class);

    private static OkHttpClient client;

    public static void newClient(){
        OkHttpClient.Builder builder = new OkHttpClient().newBuilder();
        client = builder.cookieJar(new LocalCookieJar()).build();
    }

    public static void setProxy(String hostName,String hostPort){

        Proxy proxy = client.proxy();
        if(StringUtils.isEmpty(hostName,hostPort)){
            if(proxy != null) {
                client.newBuilder().proxy(null).cookieJar(new LocalCookieJar()).build();
            }
        }else{
            if(proxy == null){
                OkHttpClient.Builder builder = client.newBuilder();
                builder.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(hostName, Integer.parseInt(hostPort))));
                client = builder.cookieJar(new LocalCookieJar()).build();
            }
        }

    }

    public static OkHttpClient getClient(){
        return client;
    }

    public static Pair<Boolean,String> doGet(Map<String,String> hostHeaders,String hostUrl){
        Request.Builder builder = new Request.Builder().url(hostUrl);
        HttpHelper.buildRequestHeader(builder,hostHeaders);
        Request request = builder.get().build();

        try (Response response = getClient().newCall(request).execute()) {
            return new Pair<>((response.code() == 200), response.body().string());
        } catch (Exception e) {
            logger.error(e.getMessage());
        }

        return new Pair<>(false,"");
    }

    /**
     *
     * @param apiUrl
     * @param apiHeaders
     * @param contentType "application/x-www-form-urlencoded" "application/json"
     * @param content
     * @return
     */
    public static Pair<Boolean,String> doPost(String apiUrl,
                                              Map<String,String> apiHeaders,
                                              String contentType,String content){
        // Header
        Request.Builder builder = new Request.Builder().url(apiUrl);
        HttpHelper.buildRequestHeader(builder,apiHeaders);

        MediaType mediaType = MediaType.parse(contentType);
        RequestBody body = RequestBody.create(mediaType,content);

        // Post
        try(Response response = getClient().newCall(builder.post(body).build()).execute()){
            return new Pair<>((response.code() == 200), response.body().string());
        } catch (IOException e) {
            logger.error(e.getMessage());
        }

        return new Pair<>(false,"");
    }

    public static Pair<Boolean,String> doPostFormData(String apiUrl,
                                              Map<String,String> apiHeaders,
                                              String content){
        return doPost(apiUrl,apiHeaders,"application/x-www-form-urlencoded",content);
    }

    public static Pair<Boolean,String> doPostJsonData(String apiUrl,
                                                      Map<String,String> apiHeaders,
                                                      String content){
        return doPost(apiUrl,apiHeaders,"application/json",content);
    }
}

HttpClientCaller


import Pair;
import StringUtils;
import RequestUtils;
import HttpHelper;
import org.apache.http.HttpHost;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.protocol.HttpContext;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;

import static RequestUtils.getPathAndQueryParam;
import static RequestUtils.getPort;

public class HttpClientCaller {

    private static Logger logger = Logger.getLogger(HttpClientCaller.class);

    private static CloseableHttpClient httpClient;
    private static RequestConfig requestConfig;

    //传输超时时间, 10秒
    private static int socketTimeout = 10000;

    //建立连接超时时间,默认10秒
    private static int connectTimeout = 5000;

    //获取连接超时时间,10秒
    private static int connectRequest = 5000;

    private static HttpHost proxy = null;

    private static HttpRequestRetryHandler myRetryHandler = new HttpRequestRetryHandler() {
        @Override
        public boolean retryRequest(IOException exception,
                                    int executionCount, HttpContext context) {
            return false;
        }
    };

    public static CloseableHttpClient createClientAttr(HttpClientBuilder builder){
        return builder.setRetryHandler(myRetryHandler).setMaxConnPerRoute(2).setMaxConnTotal(2).build();
    }

    public static void newClient() {

        // setMaxConnTotal是连接池中可用的总最大连接数。setMaxConnPerRoute是限制到单个端口或url的连接总数。
        httpClient = createClientAttr(HttpClients.custom());

        //根据默认超时限制初始化requestConfig
        requestConfig = getCommonRequestConfigBuilder().build();

    }

    public static RequestConfig.Builder getCommonRequestConfigBuilder(){

        RequestConfig.Builder builder = RequestConfig.custom().setConnectionRequestTimeout(connectRequest)
                .setSocketTimeout(socketTimeout)
                .setConnectTimeout(connectTimeout);
        return builder;
    }

    /**
     * 不行: https://blog.csdn.net/woshirongshaolin/article/details/126992654
     * @return
     */
    public static SSLConnectionSocketFactory createSSLFactory(){

        //使用 loadTrustMaterial() 方法实现一个信任策略,信任所有证书
        try {
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
                // 信任所有
                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    return true;
                }
            }).build();

            //NoopHostnameVerifier类:  作为主机名验证工具,实质上关闭了主机名验证,它接受任何
            //有效的SSL会话并匹配到目标主机。
            HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
            return sslsf;
        } catch (NoSuchAlgorithmException e) {
            logger.error(e.getMessage());
        } catch (KeyManagementException e) {
            logger.error(e.getMessage());
        } catch (KeyStoreException e) {
            logger.error(e.getMessage());
        }

        return null;
    }

    /**
     *
     * @param hostName
     * @param port
     */
    public static void setProxy(String hostName,String port){

        // 不支持
        // https://github.com/apache/httpcomponents-client/blob/4.5.x/httpclient/src/examples/org/apache/http/examples/client/ClientExecuteProxy.java
        if(StringUtils.isEmpty(hostName,port)){
            if(proxy != null)
                requestConfig = getCommonRequestConfigBuilder().setProxy(null).build();
        }else{
            if(proxy == null){
                closeClient();
                SSLConnectionSocketFactory sslFactory = createSSLFactory();
                if(sslFactory != null)
                    httpClient = createClientAttr(HttpClients.custom().setSSLSocketFactory(sslFactory));

                proxy = new HttpHost(hostName, Integer.parseInt(port));
                requestConfig = getCommonRequestConfigBuilder().setProxy(proxy).build();
            }
        }
    }

    public static void closeClient(){
        try {
            if(httpClient != null)
                httpClient.close();

            httpClient = null;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static RequestConfig getRequestConfig(){
        return requestConfig;
    }

    public static CloseableHttpClient getClient(){
        return httpClient;
    }

    public static Pair<Boolean,String> doGet(Map<String,String> hostHeaders, String hostUrl){

        if(proxy != null){

            URL url = RequestUtils.getUrlOrNull(hostUrl);

            HttpHost target = new HttpHost(url.getHost(), getPort(url),url.getProtocol());
            HttpGet request = new HttpGet(getPathAndQueryParam(url));
            request.setConfig(requestConfig);

            HttpHelper.buildRequestHeader(request,hostHeaders);
            try(CloseableHttpResponse response = httpClient.execute(target, request);){
                return new Pair<>((response.getStatusLine().getStatusCode() == 200),
                        EntityUtils.toString(response.getEntity(), "utf-8"));
            }catch ( IOException e){
                e.printStackTrace();
            }

        }else{
            HttpGet httpGet = new HttpGet(hostUrl);
            httpGet.setConfig(requestConfig);

            HttpHelper.buildRequestHeader(httpGet,hostHeaders);
            try(CloseableHttpResponse response = httpClient.execute(httpGet);){
                return new Pair<>((response.getStatusLine().getStatusCode() == 200),
                        EntityUtils.toString(response.getEntity(), "utf-8"));
            }catch ( IOException e){
                e.printStackTrace();
            }
        }

        return new Pair<>(false,"");
    }

    public static Pair<Boolean,String> doPost(String apiUrl,
                                              Map<String,String> apiHeaders,
                                              String contentType,String content) {
        if(proxy != null){

            URL url = RequestUtils.getUrlOrNull(apiUrl);
            HttpHost target = new HttpHost(url.getHost(), getPort(url),url.getProtocol());
            HttpPost post = new HttpPost(getPathAndQueryParam(url));

            HttpHelper.buildRequestHeader(post,apiHeaders);
            if(post.getFirstHeader("Content-Type") == null)
                post.addHeader("Content-Type",contentType);

            StringEntity postEntity = new StringEntity(content, "UTF-8");
            post.setEntity(postEntity);
            post.setConfig(requestConfig);
            try(CloseableHttpResponse response = httpClient.execute(target,post)){
                return new Pair<>((response.getStatusLine().getStatusCode() == 200),
                        EntityUtils.toString(response.getEntity(), "utf-8"));
            } catch (IOException e) {
                logger.warn(e.getMessage());
            }

        }else{
            HttpPost post = new HttpPost(apiUrl);
            HttpHelper.buildRequestHeader(post,apiHeaders);
            if(post.getFirstHeader("Content-Type") == null)
                post.addHeader("Content-Type",contentType);

            StringEntity postEntity = new StringEntity(content, "UTF-8");
            post.setEntity(postEntity);
            post.setConfig(requestConfig);
            try(CloseableHttpResponse response = httpClient.execute(post)){
                return new Pair<>((response.getStatusLine().getStatusCode() == 200),
                        EntityUtils.toString(response.getEntity(), "utf-8"));
            } catch (IOException e) {
                logger.warn(e.getMessage());
            }
        }


        return  new Pair<>(false,"");
    }

    public static Pair<Boolean,String> doPostFormData(String apiUrl,
                                                      Map<String,String> apiHeaders,
                                                      String content){
        return doPost(apiUrl,apiHeaders,"application/x-www-form-urlencoded; charset=UTF-8",content);
    }

    public static Pair<Boolean,String> doPostJsonData(String apiUrl,
                                                      Map<String,String> apiHeaders,
                                                      String content){
        return doPost(apiUrl,apiHeaders,"application/json; charset=UTF-8",content);
    }
}

HttpHelper


import okhttp3.Request;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.message.AbstractHttpMessage;

import java.util.Map;

public class HttpHelper {

    public static void buildRequestHeader(AbstractHttpMessage builder, Map<String,String> values){
        for (Map.Entry<String,String> keyValue:values.entrySet()) {
            builder.addHeader(keyValue.getKey(),keyValue.getValue());
        }
    }

    public static void buildRequestHeader(Request.Builder builder,Map<String,String> values){
        for (Map.Entry<String,String> keyValue:values.entrySet()) {
            builder.addHeader(keyValue.getKey(),keyValue.getValue());
        }
    }

    public static String toFormData(Map<String,String> params){
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String,String> keyValue: params.entrySet())
            sb.append(keyValue.getKey()).append("=").append(keyValue.getValue()).append("&");

        if(sb.length() > 0)
            return sb.substring(0,sb.length()-1);

        return "";
    }
}

RequestUtils

import java.net.MalformedURLException;
import java.net.URL;

public class RequestUtils {

    /**
     *
     * @param url
     * @return URL or null
     */
    public static URL getUrlOrNull(String url){
        try {
            return new URL(url);
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }

        return null;
    }

    public static int getPort(URL url){
        int port = url.getPort();
        return (port == -1)?url.getDefaultPort():port;
    }

    public static String getPathAndQueryParam(URL url){
        String path = url.getPath();
        String query = url.getQuery();
        if(query == null)
            return path;

        return String.format("%s?%s",path,query);
    }
}

运行

OkHttpClientCaller.newClient();
OkHttpClientCaller.setProxy("127.0.0.1","1283");
Pair<Boolean, String> pp = OkHttpClientCaller.doGet(headers, "https://www.bing.com");
if(pp.k)
    logger.info(pp.v);

OkHttpClientCaller.closeClient();

参考

  1. httpcomponents-client/httpclient/src/examples/org/apache/http/examples/client/ClientExecuteProxy.java

  2. Httpclient3.1指定tlsv1.2协议访问https

  3. java http请求设置代理 Proxy_java

  4. RestTemplate发起HTTPS请求Unsupported or unrecognized SSL message

  5. HttpClient访问不安全的https链接报错:SSLHandshakeException

  6. httpclient信任所有证书解决SSLException:Unrecognized SSL message

  7. Java URI getQuery()方法及示例

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Peter(阿斯拉达)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值