• Okhttp配置 重试 自动切换代理


    配置:

    import com.**.interceptor.OkHttpInterceptor;
    import com.**.interceptor.SwitchProxySelector;
    import okhttp3.ConnectionPool;
    import okhttp3.OkHttpClient;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.SSLSocketFactory;
    import javax.net.ssl.TrustManager;
    import javax.net.ssl.X509TrustManager;
    import java.security.KeyManagementException;
    import java.security.NoSuchAlgorithmException;
    import java.security.SecureRandom;
    import java.security.cert.CertificateException;
    import java.security.cert.X509Certificate;
    import java.util.concurrent.TimeUnit;
    
    /**
     * OKHttp配置
     *
     * @author qhong
     */
    @Configuration
    public class OkHttpConfig {
    
        @Bean
        public X509TrustManager x509TrustManager() {
            return new X509TrustManager() {
                @Override
                public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                }
    
                @Override
                public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                }
    
                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return new X509Certificate[0];
                }
            };
        }
    
        @Bean
        public SSLSocketFactory sslSocketFactory() {
            try {
                //信任任何链接
                SSLContext sslContext = SSLContext.getInstance("TLS");
                sslContext.init(null, new TrustManager[]{x509TrustManager()}, new SecureRandom());
                return sslContext.getSocketFactory();
            } catch (NoSuchAlgorithmException e) {
                //e.printStackTrace();
            } catch (KeyManagementException e) {
                //e.printStackTrace();
            }
            return null;
        }
    
        /**
         * Create a new connection pool with tuning parameters appropriate for a single-user application.
         * The tuning parameters in this pool are subject to change in future OkHttp releases. Currently
         */
        @Bean
        public ConnectionPool pool() {
            return new ConnectionPool(200, 5, TimeUnit.MINUTES);
        }
    
        @Bean
        public OkHttpClient okHttpClient() {
            return new OkHttpClient.Builder()
                    .sslSocketFactory(sslSocketFactory(), x509TrustManager())
                    //不自动重连
                    .retryOnConnectionFailure(false)
                    //连接池
                    .connectionPool(pool())
                    .connectTimeout(60L, TimeUnit.SECONDS)
                    .readTimeout(60L, TimeUnit.SECONDS)
                    //代理
    				.proxySelector(new SwitchProxySelector())
                    //增加3次重试次数
                    .addInterceptor(new OkHttpInterceptor())
                    .build();
        }
    }
    

    重试机制:

    import lombok.extern.slf4j.Slf4j;
    import okhttp3.Interceptor;
    import okhttp3.Request;
    import okhttp3.Response;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Value;
    
    import javax.validation.constraints.NotNull;
    import java.io.IOException;
    
    /**
     * OKHttp拦截器
     *
     * @author qhong
     */
    @Slf4j
    public class OkHttpInterceptor implements Interceptor {
    
        public static final String metaProxy = "meta.proxy";
    
        /**
         * 最大重试次数
         */
        @Value("${max.retry:3}")
        private Integer maxRetry = 3;
    
    
        @Override
        public Response intercept(@NotNull Chain chain) throws IOException {
            /* 递归 2次下发请求,如果仍然失败 则返回 null ,但是 intercept must not return null.
             * 返回 null 会报 IllegalStateException 异常
             * */
            return retry(chain, 0);
        }
    
    
        /**
         * okhttp retry
         *
         * @param chain
         * @param retryCent
         * @return
         */
        Response retry(Chain chain, int retryCent) {
            Response response = null;
            try {
                log.info("第" + (retryCent + 1) + "次执行下发请求.");
                response = proxy(chain);
            } catch (Exception e) {
                if (maxRetry > retryCent) {
                    return retry(chain, retryCent + 1);
                }
            } finally {
                return response;
            }
        }
    
        /**
         * okhttp proxy
         *
         * @param chain
         * @return
         * @throws IOException
         */
        private Response proxy(Chain chain) throws IOException {
            if (StringUtils.isNoneBlank(chain.request().header(metaProxy))) {
                String proxyHeader = chain.request().header(metaProxy);
                if (StringUtils.isNoneBlank(proxyHeader)) {
                    log.info("url:{}, add proxy header : {}", chain.request().url().toString(), proxyHeader);
                    SwitchProxySelector.proxyCache.put(chain.request().url().host(), SwitchProxySelector.getProxy(proxyHeader));
                    Request newRequest = chain.request().newBuilder().removeHeader(metaProxy).build();
                    return chain.proceed(newRequest);
                }
            }
            return chain.proceed(chain.request());
        }
    
    }
    

    自动切换代理:

    import com.google.common.cache.Cache;
    import com.google.common.cache.CacheBuilder;
    import lombok.extern.slf4j.Slf4j;
    
    import java.io.IOException;
    import java.net.*;
    import java.util.Collections;
    import java.util.List;
    import java.util.concurrent.TimeUnit;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    /**
     * @author qhong
     * @date 2022/5/13 21:34
     **/
    @Slf4j
    public class SwitchProxySelector extends ProxySelector {
    
        /**
         * 根据request返回
         */
        @Override
        public List<Proxy> select(URI uri) {
            Proxy proxy = SwitchProxySelector.proxyCache.getIfPresent(uri.getHost());
            if (proxy == null || proxy.address() == null) {
                proxy = Proxy.NO_PROXY;
            }
            log.info("{} use proxy {}:{}", uri.toString(), proxy.type().name(), proxy.address());
            return Collections.singletonList(proxy);
        }
    
    
        @Override
        public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
        }
    
        public static Cache<String, Proxy> proxyCache = CacheBuilder.newBuilder()
                .maximumSize(2000)
                .expireAfterWrite(60, TimeUnit.MINUTES)
                .build();
    
        /**
         * proxy 模式
         */
        private static final Pattern PROXY_PATTERN = Pattern.compile("(socket|http):(.*):(.*)");
    
        /**
         * 工厂方法 获取Proxy
         *
         * @param proxyString meta.proxy 中的 proxy 字符串  header("meta.proxy", "socket:192.168.0.63:1080")   header("meta.proxy", "http:192.168.0.63:8118")
         * @return Proxy
         */
        static Proxy getProxy(String proxyString) {
            if (proxyString == null || "".equals(proxyString)) {
                return Proxy.NO_PROXY;
            }
            Matcher matcher = PROXY_PATTERN.matcher(proxyString);
            if (matcher.matches()) {
    
                switch (matcher.group(1)) {
                    case "socket":
                        return new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(matcher.group(2), Integer.parseInt(matcher.group(3))));
                    case "http":
                        return new Proxy(Proxy.Type.HTTP, new InetSocketAddress(matcher.group(2), Integer.parseInt(matcher.group(3))));
                    default:
                        return Proxy.NO_PROXY;
                }
    
            }
            return Proxy.NO_PROXY;
        }
    
    }
    

    总结:

    后续如果某个请求需要代理转发,那么只要在该请求header中新增meta.proxy代理配置即可。

    例如:

    header("meta.proxy", "socket:192.168.0.63:1080")  
    
    header("meta.proxy", "http:192.168.0.63:8118")
    

    参考:

    OkHttp 自动切换代理

  • 相关阅读:
    Apriori算法原理总结
    FP Tree算法原理总结
    用Spark学习FP Tree算法和PrefixSpan算法
    《万历十五年》段落摘抄
    DPDK mempool
    DPDK PCIe 与 包处理
    《汇编语言》-- 控制执行流程
    《黑客攻防技术-系统实战》第二章--栈溢出4
    DPDK报文转发
    DPDK同步互斥机制
  • 原文地址:https://www.cnblogs.com/hongdada/p/16826357.html
Copyright © 2020-2023  润新知