
com.taotao.cloud.openfeign.configuration.FeignAutoConfiguration Maven / Gradle / Ivy
Show all versions of taotao-cloud-starter-openfeign Show documentation
/*
* Copyright (c) 2020-2030, Shuigedeng ([email protected] & https://blog.taotaocloud.top/).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.taotao.cloud.openfeign.configuration;
import com.taotao.cloud.common.constant.StarterName;
import com.taotao.cloud.common.utils.log.LogUtils;
import com.taotao.cloud.openfeign.aspect.FeignRetryAspect;
import com.taotao.cloud.openfeign.endpoint.FeignClientEndpoint;
import com.taotao.cloud.openfeign.feign.FeignErrorDecoder;
import com.taotao.cloud.openfeign.feign.FeignInnerContract;
import com.taotao.cloud.openfeign.feign.FeignRequestInterceptor;
import com.taotao.cloud.openfeign.feign.FeignResponseEntityDecoder;
import com.taotao.cloud.openfeign.formatter.DateFormatRegister;
import com.taotao.cloud.openfeign.properties.FeignProperties;
import com.taotao.cloud.openfeign.properties.LoadbalancerProperties;
import feign.*;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.form.spring.SpringFormEncoder;
import feign.optionals.OptionalDecoder;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.support.HttpMessageConverterCustomizer;
import org.springframework.cloud.openfeign.support.SpringDecoder;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
/**
* Feign 自动配置
*
* @author shuigedeng
* @version 2023.07
* @since 2023-07-06 13:42:21
*/
@EnableFeignClients(basePackages = {"com.taotao.cloud.*.api.feign"})
// @AutoConfiguration(before = SentinelFeignAutoConfiguration.class)
@EnableConfigurationProperties({LoadbalancerProperties.class, FeignProperties.class})
@ConditionalOnProperty(prefix = FeignProperties.PREFIX, name = "enabled", havingValue = "true", matchIfMissing = true)
public class FeignAutoConfiguration implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
LogUtils.started(FeignAutoConfiguration.class, StarterName.OPENFEIGN_STARTER);
}
@Bean
@ConditionalOnMissingBean
public Contract contract() {
return new FeignInnerContract();
}
/**
* Feign 客户端的日志记录,默认级别为NONE Logger.Level 的具体级别如下: NONE:不记录任何信息 BASIC:仅记录请求方法、URL以及响应状态码和执行时间
* HEADERS:除了记录 BASIC级别的信息外,还会记录请求和响应的头信息 FULL:记录所有请求与响应的明细,包括头信息、请求体、元数据
*/
@Bean
public Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
// @Bean
// @Profile({"dev", "test"})
// public Logger.Level devFeignLoggerLevel() {
// return Logger.Level.FULL;
// }
// @Bean
// @Profile({"docker", "pre", "prod"})
// public Logger.Level prodFeignLoggerLevel() {
// return Logger.Level.BASIC;
// }
@Bean
public DateFormatRegister dateFormatRegister() {
return new DateFormatRegister();
}
/**
* 使用feign默认的重试机制
*/
@Bean
public Retryer retryer() {
return new Retryer.Default();
}
/**
* 使用自定义注解重试机制
*/
@Bean
public FeignRetryAspect feignRetryAspect() {
return new FeignRetryAspect();
}
/**
* Feign支持文件上传
*/
@Bean
@Primary
@Scope("prototype")
public Encoder multipartFormEncoder(ObjectFactory messageConverters) {
return new SpringFormEncoder(new SpringEncoder(messageConverters));
}
// @Bean
// public Encoder feignEncoder() {
// return new SpringEncoder(feignHttpMessageConverter());
// }
// @Bean("starterFeignDecoder")
// public Decoder feignDecoder(ObjectProvider customizers) {
// return new OptionalDecoder(
// new ResultDecode(new SpringDecoder(feignHttpMessageConverter(), customizers)));
// }
@Bean
public Decoder feignDecoder(
ObjectFactory messageConverters,
ObjectProvider customizers) {
return new OptionalDecoder(new FeignResponseEntityDecoder(new SpringDecoder(messageConverters, customizers)));
}
@Bean
public FeignErrorDecoder feignErrorDecoder() {
return new FeignErrorDecoder();
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnAvailableEndpoint
public FeignClientEndpoint feignClientEndpoint(ApplicationContext context) {
return new FeignClientEndpoint(context);
}
/**
* 使用feign client访问别的微服务时,将上游传过来的access_token、username、roles等信息放入header传递给下一个服务
*/
@Bean
@ConditionalOnMissingBean(FeignRequestInterceptor.class)
public RequestInterceptor feignRequestInterceptor() {
LogUtils.trace(" Bean [Feign Request Interceptor] Auto Configure.");
return new FeignRequestInterceptor();
}
/**
* FeignClient超时设置
* feign超时设置有3种方式:配置文件直接配置FeignClient、自定义Request.Options及配置文件配置Ribbon,优先级从高到低如下。
* 1、配置文件里对特定FeignClient配置属性: feign.client.config.demo.connectTimeout=1000,feign.client.config.demo.readTimeout=2000 ;
* 2、自定义对特定FeignClient生效的Request.Options类型的Bean;
* 3、配置文件里对所有FeienClient属性的配置:feign.client.config.default.connectTimeout=1000,feign.client.config.default.readTimeout=5000
* 4、对全体FeignClient生效的Request.Options类型的Bean;
* 5、特定服务的ribbon配置:demo.ribbon.ConnectTimeout=1000,demo.ribbon.ReadTimeout=5000
* 6、全体服务的ribbon配置:ribbon.ConnectTimeout=1000,ribbon.ReadTimeout=5000
* 7、Ribbon默认配置:默认连接超时和读取超时都是1000,即1秒
*
* 总结一下:
* 1、FeignClient的直接配置高于Ribbon的配置
* 2、特定服务的配置高于全体服务的配置
* 3、配置文件的配置高于自定义Request.Options
* 4、如果有特定服务的Options和全体服务的配置文件配置,遵循第二条规则,以特定服务的Options为准;
* 5、如果有特性服务的Ribbon配置和全体服务的FeignClient配置,遵循第一条规则,以FeingClient的配置为准
*
* 最佳实践:
* 1、不要采用Ribbon配置而要直接配置FeignClient,即配置feign.client.xx
* 2、配置文件配置全体FeignClient的超时设置,同时对特定服务有特殊设置的,也在配置文件里配置
*
*
* @see
*/
@Bean
public Request.Options options() {
return new Request.Options(10, TimeUnit.SECONDS, 60, TimeUnit.SECONDS, true);
}
/** 设置解码器为fastjson */
// private ObjectFactory feignHttpMessageConverter() {
// final HttpMessageConverters httpMessageConverters = new HttpMessageConverters(
// this.getFastJsonConverter());
// return () -> httpMessageConverters;
// }
//
// private FastJsonHttpMessageConverter getFastJsonConverter() {
// FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
//
// List supportedMediaTypes = new ArrayList<>();
// MediaType mediaTypeJson = MediaType.valueOf(MediaType.APPLICATION_JSON_VALUE);
// supportedMediaTypes.add(mediaTypeJson);
// converter.setSupportedMediaTypes(supportedMediaTypes);
// FastJsonConfig config = new FastJsonConfig();
// config.getSerializeConfig().put(JSON.class, new SwaggerJsonSerializer());
// config.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect);
// converter.setFastJsonConfig(config);
//
// return converter;
// }
/// **
// * RestTemplate 相关的配置
// */
// @Configuration
// @ConditionalOnClass(okhttp3.OkHttpClient.class)
// public static class RestTemplateConfiguration implements InitializingBean {
// @Override
// public void afterPropertiesSet() throws Exception {
// LogUtil.started(RestTemplateConfiguration.class, StarterName.FEIGN_STARTER);
// }
//
// private static final Charset UTF_8 = StandardCharsets.UTF_8;
// private final ObjectMapper objectMapper = JsonUtil.MAPPER;
//
// @Bean
// @ConditionalOnMissingBean(FeignLoggerFactory.class)
// public FeignLoggerFactory getInfoFeignLoggerFactory() {
// return new InfoFeignLoggerFactory();
// }
//
//
// /**
// * 配置OkHttpClient
// *
// * @param httpClientFactory httpClient 工厂
// * @param connectionPool 链接池配置
// * @param httpClientProperties httpClient配置
// * @return OkHttpClient
// */
// @Bean
// @ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
// public okhttp3.OkHttpClient okHttp3Client(
// OkHttpClientFactory httpClientFactory,
// okhttp3.ConnectionPool connectionPool,
// FeignHttpClientProperties httpClientProperties) {
// return httpClientFactory.createBuilder(httpClientProperties.isDisableSslValidation())
// .followRedirects(httpClientProperties.isFollowRedirects())
// .writeTimeout(Duration.ofSeconds(30))
// .readTimeout(Duration.ofSeconds(30))
// .connectTimeout(Duration.ofMillis(httpClientProperties.getConnectionTimeout()))
// .connectionPool(connectionPool)
// .build();
// }
//
// /**
// * okhttp3 链接池配置
// *
// * @param connectionPoolFactory 链接池配置
// * @param hcp httpClient配置
// * @return okhttp3.ConnectionPool
// */
// @Bean
// @ConditionalOnMissingBean(okhttp3.ConnectionPool.class)
// public okhttp3.ConnectionPool okHttp3ConnectionPool(FeignHttpClientProperties hcp,
// OkHttpClientConnectionPoolFactory connectionPoolFactory) {
// return connectionPoolFactory.create(hcp.getMaxConnections(), hcp.getTimeToLive(),
// hcp.getTimeToLiveUnit());
// }
//
// /**
// * 解决 RestTemplate 传递Request header
// */
// @Bean
// public RestTemplateHeaderInterceptor requestHeaderInterceptor() {
// return new RestTemplateHeaderInterceptor();
// }
//
// /**
// * 支持负载均衡的 LbRestTemplate, 传递请求头,一般用于内部 http 调用
// *
// * @param httpClient OkHttpClient
// * @param interceptor RestTemplateHeaderInterceptor
// * @return LbRestTemplate
// */
// @Bean("lbRestTemplate")
// @LoadBalanced
// @SentinelRestTemplate
// @ConditionalOnMissingBean(RestTemplate.class)
// public RestTemplate lbRestTemplate(okhttp3.OkHttpClient httpClient,
// RestTemplateHeaderInterceptor interceptor) {
// RestTemplate lbRestTemplate = new RestTemplate(
// new OkHttp3ClientHttpRequestFactory(httpClient));
// lbRestTemplate.setInterceptors(Collections.singletonList(interceptor));
// this.configMessageConverters(lbRestTemplate.getMessageConverters());
// return lbRestTemplate;
// }
//
// /**
// * 普通的 RestTemplate,不透传请求头,一般只做外部 http 调用
// *
// * @param httpClient OkHttpClient
// * @return RestTemplate
// */
// @Bean
// @SentinelRestTemplate
// public RestTemplate restTemplate(okhttp3.OkHttpClient httpClient) {
// RestTemplate restTemplate = new RestTemplate(
// new OkHttp3ClientHttpRequestFactory(httpClient));
// this.configMessageConverters(restTemplate.getMessageConverters());
// return restTemplate;
// }
//
// private void configMessageConverters(List> converters) {
// converters.removeIf(c -> c instanceof StringHttpMessageConverter
// || c instanceof MappingJackson2HttpMessageConverter);
// converters.add(new StringHttpMessageConverter(UTF_8));
// converters.add(new MappingJackson2HttpMessageConverter(this.objectMapper));
// }
// }
}