文章目录
实战
环境
Nacos
Nacos Version
:2.0.3
Demo Application:
System
: macOS 10.14.6
JDK
: 1.8.0_251
Spring Boot
:2.2.13.RELEASE
Spring Cloud
:Hoxton.SR12
Spring Cloud Alibaba
: 2.2.5.RELEASE
添加依赖
<!-- feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
添加注解
启动类添加@EnableFeignClients
。
关键代码
定义feign client
good-service
服务的feign client
@FeignClient(name = "good-service")
public interface GoodServiceClient {
@GetMapping("/good/{id}")
Result queryGood(@PathVariable("id") String id);
}
定义接口
接口方法WfbiFeignApplication::getGoodByID
@GetMapping("/{id}")
public Result getGoodByID(@PathVariable("id") String id) {
return goodServiceClient.queryGood(id);
}
feign脱离ribbon使用
@FeignClient(name = "aservice", url = "${aservice.url}")
public interface AServiceClient {
... ...
}
可单独配置服务aservice的api地址url进行远程调用。
feign拦截器
拦截器可以为每个HTTP请求/响应执行从身份验证到日志记录的各种隐式任务。
定义拦截器FeignHeaderInterceptor
@Slf4j
public class FeignHeaderInterceptor implements RequestInterceptor {
/**
* 调用链传递token
*
* @param requestTemplate
*/
@Override
public void apply(RequestTemplate requestTemplate) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String accessToken = request == null ? StringUtils.EMPTY : request.getHeader(HttpHeaders.AUTHORIZATION);
log.info("=================Feign Interceptor AccessToken: " + accessToken);
requestTemplate.header(HttpHeaders.AUTHORIZATION, accessToken);
}
}
注意 不能添加@Component注解,否则会全局生效。
添加配置加入调用链
# feign interceptor
feign.client.config.good-service.request-interceptors[0]=com.wfbi.wfbifeign.FeignHeaderInterceptor
good-service可用deault代替,代表对所有服务适用该拦截器。
feign 整合 sentinel
添加依赖
<!-- sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
添加配置
# feign整合sentinel
feign.sentinel.enabled=true
# sentinel config
spring.cloud.sentinel.transport.dashboard=localhost:10000
spring.cloud.sentinel.transport.port=8719
spring.cloud.sentinel.web-context-unify=false
断路代码
方式一 fallback方式
GoodServiceFallBack实现GoodServiceClient
@Component
public class GoodServiceFallBack implements GoodServiceClient{
@Override
public Result queryGood(String id) {
return new Result().setCode(1).setMessage("断路message:" + id);
}
}
GoodServiceClient配置fallback
@FeignClient(name = "good-service", fallback = GoodServiceFallBack.class)
public interface GoodServiceClient {
@GetMapping("/good/{id}")
Result queryGood(@PathVariable("id") String id);
}
方式二 fallbackFactory方式
GoodServiceFactory实现FallbackFactory
@Slf4j
@Component
public class GoodServiceFactory implements FallbackFactory<GoodServiceClient> {
@Override
public GoodServiceClient create(Throwable throwable) {
return new GoodServiceClient() {
@Override
public Result queryGood(String id) {
log.error("GoodServiceClient::queryGood error:", throwable);
return new Result().setCode(1).setMessage("fallbackFactory断路message:" + id);
}
};
}
}
GoodServiceClient配置fallbackFactory
@FeignClient(name = "good-service", fallbackFactory = GoodServiceFactory.class)
public interface GoodServiceClient {
@GetMapping("/good/{id}")
Result queryGood(@PathVariable("id") String id);
}
配置sentinel熔断规则
为方便演示,直接将最大RT设置为1ms。请求查看效果如下:
推荐fallbackFactory方式,可以捕获异常并输出日志
遇到的问题
Requested bean is currently in creation: Is there an unresolvable circular reference
解决方案:spring-cloud 从Hoxton.SR12降级为Hoxton.SR9。
参考
配置项
配置项 | 作用 |
---|---|
Logger.Level | 指定日志级别 |
Retryer | 指定重试策略 |
ErrorDecoder | 指定错误解码器 |
Request.Options | 超时时间 |
Collection<RequestInterceptor> | 拦截器 |
SetterFactory | 用于设置Hystrix的配置属性,Fgien整合Hystrix才会用 |
支持配置方式
代码配置和属性配置(优先选择)。
代码配置日志级别
Feign有四种类型:NONE(默认)、BASIC、HEADERS、FULL。
NONE
: 不记录任何日志信息,这是默认值。BASIC
:仅记录请求的方法,URL以及响应状态码和执行时间HEADERS
:在BASIC的基础上,额外记录了请求和响应的头信息FULL
:记录所有请求和响应的明细,包括头信息、请求体、元数据
参数配置类FeignConfiguration
public class FeignConfiguration {
@Bean
public Logger.Level level() {
// 打印所有细节
return Logger.Level.FULL;
}
}
在类上面加了@Configuration注解的话,就必须将该类放到@ComponentScan能扫描的包以外,否则这个类的配置将会被所有的FeignClient共享,就无法实现针对服务的细粒度配置了,详见JAVA父子上下文。所以这里建议不加。
FeignClient配置类GoodServiceClient
@FeignClient(name = "good-service", configuration = FeignConfiguration.class)
public interface GoodServiceClient {
@GetMapping("/good/{id}")
Result queryGood(@PathVariable("id") String id);
}
属性配置日志级别
# 配置good-service日志输出级别
feign.client.config.good-service.loggerLevel=full
常用配置
源码配置类FeignClientProperties
public static class FeignClientConfiguration {
private Level loggerLevel;
private Integer connectTimeout;
private Integer readTimeout;
private Class<Retryer> retryer;
private Class<ErrorDecoder> errorDecoder;
private List<Class<RequestInterceptor>> requestInterceptors;
private Map<String, Collection<String>> defaultRequestHeaders;
private Map<String, Collection<String>> defaultQueryParameters;
private Boolean decode404;
private Class<Decoder> decoder;
private Class<Encoder> encoder;
private Class<Contract> contract;
private ExceptionPropagationPolicy exceptionPropagationPolicy;
private Boolean followRedirects;
... ...
启用默认hystrix断路器
openfeign集成了hystrix断路器,但默认关闭的,可通过如下配置开启
feign.hystrix.enabled=true
请求超时配置
# Feign的连接建立超时时间,默认为10秒
feign.client.config.default.connectTimeout=10000
# Feign的请求处理超时时间,默认60s
feign.client.config.default.readTimeout=60000
# Feign使用默认的超时配置,在该类源码中可见,默认单次请求最大时长1秒,重试5次
feign.client.config.default.retryer=feign.Retryer.Default
注意:
- ribbon&feign同时配置,则feign优先级高生效,推荐ribbon配置(有争议,个人参考的)
- default可以改成服务名,针对具体服务生效
与hystrix结合需配置熔断超时时间
feign.hystrix.enabled=true
# 熔断超时设置,默认为 1 秒
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000
# 强制打开熔断器,如果该属性设置为 true,强制断路器进入打开状态,将会拒绝所有的请求。 默认 false 关闭的
hystrix.command.defautl.circuitBreaker.forceOpen=false
# 触发熔断错误比例阈值,默认值 50%
hystrix.command.defautl.circuitBreaker.errorThresholdPercentage=50
# 熔断后休眠时长,默认值 5 秒
hystrix.command.defautl.circuitBreaker.sleepWindowInMilliseconds=3000
# 熔断触发最小请求次数,默认值是 20
hystrix.command.defautl.circuitBreaker.requestVolumeThreshold=10
注意:
default可以改为服务名,甚至具体方法,
hystrix.command.GoodServiceClient#queryGood(String).execution.isolation.thread.timeoutInMilliseconds=1500
超时重试
因为ribbon的重试机制和Feign的重试机制有冲突,所以源码中默认关闭Feign的重试机制(默认的简单实现Retryer.Default)。推荐ribbon重试。
注意
需配置hystrix的timeoutInMillisecond大于ribbon的 ( ConnectTimeout + ReadTimeout ) × 2。否则还没来得及重试就走断路了。
压缩
支持对请求和响应进行GZIP压缩,以提高通信效率,通过下面的参数即可开启请求与响应的压缩功能:
# 配置请求GZIP压缩
feign.compression.request.enabled=true
# 配置响应GZIP压缩
feign.compression.response.enabled=true
# 配置压缩支持的MIME TYPE
feign.compression.request.mime-types=text/xml,application/xml,application/json
# 配置压缩数据阈值,默认2048B
feign.compression.request.min-request-size=2048
# 对于除OkHttpClient外的http客户端,可以启用默认gzip解码器以UTF-8编码解码gzip响应:
feign.compression.response.useGzipDecoder=true
编码解码
Encoder&Decoder
使用场景
- 第三方需要特定的参数格式,如xml、json
- 第三方返回的格式需要做本地化转换,以便进行业务处理
- 为了追求性能,进行数据编码,如
protobuf
编码。
注意 在使用中发现方法签名为method(Object)
这种格式才会进入对应的编解码器。
ErrorDecoder
发生错误、异常情况时使用的解码器,允许你对异常进行特殊处理。
使用场景
- 需要对固定错误码进行处理,比如返回400错误码时抛出指定异常。
定义返回码为400时返回固定异常
class IllegalArgumentExceptionDecoder implements ErrorDecoder {
@Override
public Exception decode(String methodKey, Response response) {
if (response.status() == 400)
throw new IllegalArgumentException("bad zone name");
// 错误码如果不是400,会交给Default进行解码
// 改进:这里使用单例即可,Default不用每次都去new
return new ErrorDecoder.Default().decode(methodKey, response);
}
}
注意 若设置decode404=true
,那么它交给的是Decoder去解析,而非ErrorDecoder
Feign简介
Feign是Spring Cloud提供的一个声明式的伪Http客户端, 它使得调用远程服务就像调用本地服务一样简单, 只需要创建一个接口并添加一个注解即可。
Nacos很好的兼容了Feign, Feign默认集成了 Ribbon, 所以在Nacos下使用Fegin默认就实现了负载均衡的效果。