OpenFeign集成OkHttp

OpenFeign本质是HTTP来进行服务调用的,也就是需要集成一个Http客户端。

使用的是Client接口来进行请求的

public interface Client {

 // request是封装的请求方式、参数、返回值类型
  // options 是连接超时、读取超时等的配置项
  Response execute(Request request, Options options) throws IOException;
}

默认是HttpURLConnection方式,也就是jdk中提供的最原始的那个

public static class Default implements Client {

  @Override
  public Response execute(Request request, Options options) throws IOException {
    HttpURLConnection connection = convertAndSend(request, options);
    return convertResponse(connection).toBuilder().request(request).build();
  }
}

<!-- more -->

HTTP连接需要进行TCP三次握手,是一个比较耗时的操作,一般我们不直接使用HttpURLConnection,而是使用HttpClient/okHttp等支持连接池的客户端工具,以Feign集成OkHttp为例

添加依赖

        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-okhttp</artifactId>
        </dependency>

其包内有一个Client的实现类OkHttpClient,

public final class OkHttpClient implements Client {

  @Override
  public feign.Response execute(feign.Request input, feign.Request.Options options)
      throws IOException {
    okhttp3.OkHttpClient requestScoped;
    if (delegate.connectTimeoutMillis() != options.connectTimeoutMillis()
        || delegate.readTimeoutMillis() != options.readTimeoutMillis()) {
      requestScoped = delegate.newBuilder()
          .connectTimeout(options.connectTimeoutMillis(), TimeUnit.MILLISECONDS)
          .readTimeout(options.readTimeoutMillis(), TimeUnit.MILLISECONDS)
          .followRedirects(options.isFollowRedirects())
          .build();
    } else {
      requestScoped = delegate;
    }
    Request request = toOkHttpRequest(input);
    Response response = requestScoped.newCall(request).execute();
    return toFeignResponse(response, input).toBuilder().request(input).build();
  }
}

配置连接池

import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.net.ssl.*;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;

@Configuration
public class OkHttpConfig {
    /**
     * OkHttp 客户端配置
     *
     * @return OkHttp 客户端配
     */
    @Bean
    public OkHttpClient okHttpClient() {
        return new OkHttpClient.Builder()
                .sslSocketFactory(sslSocketFactory(), x509TrustManager())
                .hostnameVerifier(hostnameVerifier())
                .retryOnConnectionFailure(false)    //是否开启缓存
                .connectionPool(pool())             //连接池
                .connectTimeout(15L, TimeUnit.SECONDS) // 连接超时时间
                .readTimeout(15L, TimeUnit.SECONDS) // 读取超时时间
                .followRedirects(true) // 是否允许重定向
                .build();
    }

    /**
     * 忽略证书校验
     *
     * @return 证书信任管理器
     */
    @Bean
    public X509TrustManager x509TrustManager() {
        return new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] x509Certificates, String s) {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] x509Certificates, String s) {
            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }
        };
    }

    /**
     * 信任所有 SSL 证书
     *
     * @return
     */
    @Bean
    public SSLSocketFactory sslSocketFactory() {
        try {
            TrustManager[] trustManagers = new TrustManager[]{x509TrustManager()};
            SSLContext sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, trustManagers, new SecureRandom());
            return sslContext.getSocketFactory();
        } catch (NoSuchAlgorithmException | KeyManagementException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 连接池配置
     *
     * @return 连接池
     */
    @Bean
    public ConnectionPool pool() {
        // 最大连接数、连接存活时间、存活时间单位(分钟)
        return new ConnectionPool(200, 5, TimeUnit.MINUTES);
    }

    /**
     * 信任所有主机名
     *
     * @return 主机名校验
     */
    @Bean
    public HostnameVerifier hostnameVerifier() {
        return (s, sslSession) -> true;
    }
}

yml配置

要开启OkHttp ,还需要在YML 中添加开启配置项,默认是关闭的

feign:
  okhttp:
    enabled: true

至于为什么需要配这个,看一下FeignAutoConfiguration中装配OkHttp的条件

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(OkHttpClient.class)
@ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
@ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
@ConditionalOnProperty("feign.okhttp.enabled")
protected static class OkHttpFeignConfiguration

参考文献


bug生产者
20 声望0 粉丝