标签:过程 with release market timertask null hub call condition

最近一个新项目在做后端HTTP库技术选型的时候对比了Spring WebClient,Spring RestTemplate,Retrofit,Feign,Okhttp。综合考虑最终选择了上层封装比较好的Feign,尽管我们的App没有加入微服务,但是时间下来Feign用着还是很香的。
我们的sytyale针对Feign的底层原理和源码进行了解析,最后用一个小例子总结怎么快速上手。
本文作者:sytyale,另外一个聪明好学的同事
Feign 是一个 Java 到 HTTP 的客户端绑定器,灵感来自于 Retrofit 和 JAXRS-2.0 以及 WebSocket。Feign 的第一个目标是降低将 Denominator 无变化的绑定到 HTTP APIs 的复杂性,而不考虑 ReSTfulness。
Feign 使用 Jersey 和 CXF 等工具为 ReST 或 SOAP 服务编写 java 客户端。此外,Feign 允许您在 Apache HC 等http 库之上编写自己的代码。Feign 以最小的开销将代码连接到 http APIs,并通过可定制的解码器和错误处理(可以写入任何基于文本的 http APIs)将代码连接到 http APIs。
Feign 通过将注解处理为模板化请求来工作。参数在输出之前直接应用于这些模板。尽管 Feign 仅限于支持基于文本的 APIs,但它极大地简化了系统方面,例如重放请求。此外,Feign 使得对转换进行单元测试变得简单。
Feign 10.x 及以上版本是在 Java 8上构建的,应该在 Java 9、10 和 11上工作。对于需要 JDK 6兼容性的用户,请使用 Feign 9.x
 
feign 在默认情况下使用 JDK 原生的 URLConnection 发送HTTP请求。(没有连接池,保持长连接) 。
可以通过修改 client 依赖换用底层的 client,不同的 http client 对请求的支持可能有差异。具体使用示例如下:
feign: 
  httpclient:
    enable: false
  okhttp:
    enable: true
AND
<!-- Support PATCH Method-->
<dependency>    
  <groupId>org.apache.httpcomponents</groupId>    
  <artifactId>httpclient</artifactId> 
</dependency>
      
<!-- Do not support PATCH Method -->
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-okhttp</artifactId>
</dependency>
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
public class OkHttpFeignConfiguration {
 private okhttp3.OkHttpClient okHttpClient;
  
 @Bean
 @ConditionalOnMissingBean(ConnectionPool.class)
 public ConnectionPool httpClientConnectionPool(
   FeignHttpClientProperties httpClientProperties,
   OkHttpClientConnectionPoolFactory connectionPoolFactory) {
  Integer maxTotalConnections = httpClientProperties.getMaxConnections();
  Long timeToLive = httpClientProperties.getTimeToLive();
  TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
  return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
 }
 @Bean
 public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory,
   ConnectionPool connectionPool,
   FeignHttpClientProperties httpClientProperties) {
  Boolean followRedirects = httpClientProperties.isFollowRedirects();
  Integer connectTimeout = httpClientProperties.getConnectionTimeout();
  this.okHttpClient = httpClientFactory
    .createBuilder(httpClientProperties.isDisableSslValidation())
    .connectTimeout(connectTimeout, TimeUnit.MILLISECONDS)
    .followRedirects(followRedirects).connectionPool(connectionPool).build();
  return this.okHttpClient;
 }
 @PreDestroy
 public void destroy() {
  if (this.okHttpClient != null) {
   this.okHttpClient.dispatcher().executorService().shutdown();
   this.okHttpClient.connectionPool().evictAll();
  }
 }
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(CloseableHttpClient.class)
public class HttpClientFeignConfiguration {
 private final Timer connectionManagerTimer = new Timer(
   "FeignApacheHttpClientConfiguration.connectionManagerTimer", true);
 private CloseableHttpClient httpClient;
 @Autowired(required = false)
 private RegistryBuilder registryBuilder;
 @Bean
 @ConditionalOnMissingBean(HttpClientConnectionManager.class)
 public HttpClientConnectionManager connectionManager(
   ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
   FeignHttpClientProperties httpClientProperties) {
  final HttpClientConnectionManager connectionManager = connectionManagerFactory
    .newConnectionManager(httpClientProperties.isDisableSslValidation(),
      httpClientProperties.getMaxConnections(),
      httpClientProperties.getMaxConnectionsPerRoute(),
      httpClientProperties.getTimeToLive(),
      httpClientProperties.getTimeToLiveUnit(), this.registryBuilder);
  this.connectionManagerTimer.schedule(new TimerTask() {
   @Override
   public void run() {
    connectionManager.closeExpiredConnections();
   }
  }, 30000, httpClientProperties.getConnectionTimerRepeat());
  return connectionManager;
 }
 @Bean
 @ConditionalOnProperty(value = "feign.compression.response.enabled",
   havingValue = "true")
 public CloseableHttpClient customHttpClient(
   HttpClientConnectionManager httpClientConnectionManager,
   FeignHttpClientProperties httpClientProperties) {
  HttpClientBuilder builder = HttpClientBuilder.create().disableCookieManagement()
    .useSystemProperties();
  this.httpClient = createClient(builder, httpClientConnectionManager,
    httpClientProperties);
  return this.httpClient;
 }
 @Bean
 @ConditionalOnProperty(value = "feign.compression.response.enabled",
   havingValue = "false", matchIfMissing = true)
 public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory,
   HttpClientConnectionManager httpClientConnectionManager,
   FeignHttpClientProperties httpClientProperties) {
  this.httpClient = createClient(httpClientFactory.createBuilder(),
    httpClientConnectionManager, httpClientProperties);
  return this.httpClient;
 }
 private CloseableHttpClient createClient(HttpClientBuilder builder,
   HttpClientConnectionManager httpClientConnectionManager,
   FeignHttpClientProperties httpClientProperties) {
  RequestConfig defaultRequestConfig = RequestConfig.custom()
    .setConnectTimeout(httpClientProperties.getConnectionTimeout())
    .setRedirectsEnabled(httpClientProperties.isFollowRedirects()).build();
  CloseableHttpClient httpClient = builder
    .setDefaultRequestConfig(defaultRequestConfig)
    .setConnectionManager(httpClientConnectionManager).build();
  return httpClient;
 }
 @PreDestroy
 public void destroy() throws Exception {
  this.connectionManagerTimer.cancel();
  if (this.httpClient != null) {
   this.httpClient.close();
  }
 }
}
@ConfigurationProperties(prefix = "feign.httpclient")
public class FeignHttpClientProperties {
 /**
  * Default value for disabling SSL validation.
  */
 public static final boolean DEFAULT_DISABLE_SSL_VALIDATION = false;
 /**
  * Default value for max number od connections.
  */
 public static final int DEFAULT_MAX_CONNECTIONS = 200;
 /**
  * Default value for max number od connections per route.
  */
 public static final int DEFAULT_MAX_CONNECTIONS_PER_ROUTE = 50;
 /**
  * Default value for time to live.
  */
 public static final long DEFAULT_TIME_TO_LIVE = 900L;
 /**
  * Default time to live unit.
  */
 public static final TimeUnit DEFAULT_TIME_TO_LIVE_UNIT = TimeUnit.SECONDS;
 /**
  * Default value for following redirects.
  */
 public static final boolean DEFAULT_FOLLOW_REDIRECTS = true;
 /**
  * Default value for connection timeout.
  */
 public static final int DEFAULT_CONNECTION_TIMEOUT = 2000;
 /**
  * Default value for connection timer repeat.
  */
 public static final int DEFAULT_CONNECTION_TIMER_REPEAT = 3000;
 private boolean disableSslValidation = DEFAULT_DISABLE_SSL_VALIDATION;
 private int maxConnections = DEFAULT_MAX_CONNECTIONS;
 private int maxConnectionsPerRoute = DEFAULT_MAX_CONNECTIONS_PER_ROUTE;
 private long timeToLive = DEFAULT_TIME_TO_LIVE;
 private TimeUnit timeToLiveUnit = DEFAULT_TIME_TO_LIVE_UNIT;
 private boolean followRedirects = DEFAULT_FOLLOW_REDIRECTS;
 private int connectionTimeout = DEFAULT_CONNECTION_TIMEOUT;
 private int connectionTimerRepeat = DEFAULT_CONNECTION_TIMER_REPEAT;
 //省略 setter 和 getter 方法
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FeignClient {
  // 忽略了过时的属性
  
 /**
  * The name of the service with optional protocol prefix. Synonym for {@link #name()
  * name}. A name must be specified for all clients, whether or not a url is provided.
  * Can be specified as property key, eg: ${propertyKey}.
  * @return the name of the service with optional protocol prefix
  */
 @AliasFor("name")
 String value() default "";
 /**
  * This will be used as the bean name instead of name if present, but will not be used
  * as a service id.
  * @return bean name instead of name if present
  */
 String contextId() default "";
 /**
  * @return The service id with optional protocol prefix. Synonym for {@link #value()
  * value}.
  */
 @AliasFor("value")
 String name() default "";
 /**
  * @return the <code>@Qualifier</code> value for the feign client.
  */
 String qualifier() default "";
 /**
  * @return an absolute URL or resolvable hostname (the protocol is optional).
  */
 String url() default "";
 /**
  * @return whether 404s should be decoded instead of throwing FeignExceptions
  */
 boolean decode404() default false;
 /**
  * A custom configuration class for the feign client. Can contain override
  * <code>@Bean</code> definition for the pieces that make up the client, for instance
  * {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.
  *
  * @see FeignClientsConfiguration for the defaults
  * @return list of configurations for feign client
  */
 Class<?>[] configuration() default {};
 /**
  * Fallback class for the specified Feign client interface. The fallback class must
  * implement the interface annotated by this annotation and be a valid spring bean.
  * @return fallback class for the specified Feign client interface
  */
 Class<?> fallback() default void.class;
 /**
  * Define a fallback factory for the specified Feign client interface. The fallback
  * factory must produce instances of fallback classes that implement the interface
  * annotated by {@link FeignClient}. The fallback factory must be a valid spring bean.
  *
  * @see feign.hystrix.FallbackFactory for details.
  * @return fallback factory for the specified Feign client interface
  */
 Class<?> fallbackFactory() default void.class;
 /**
  * @return path prefix to be used by all method-level mappings. Can be used with or
  * without <code>@RibbonClient</code>.
  */
 String path() default "";
 /**
  * @return whether to mark the feign proxy as a primary bean. Defaults to true.
  */
 boolean primary() default true;
}
 /**
  * Feign client configuration.
  */
 public static class FeignClientConfiguration {
  private Logger.Level loggerLevel;
  private Integer connectTimeout;
  private Integer readTimeout;
  private Class<Retryer> retryer;
  private Class<ErrorDecoder> errorDecoder;
  private List<Class<RequestInterceptor>> requestInterceptors;
  private Boolean decode404;
  private Class<Decoder> decoder;
  private Class<Encoder> encoder;
  private Class<Contract> contract;
  private ExceptionPropagationPolicy exceptionPropagationPolicy;
    //省略setter 和 getter
 }
EnableFeignClients 注解header 或者 token 等标签:过程 with release market timertask null hub call condition
原文地址:https://www.cnblogs.com/shoshana-kong/p/14928308.html