OpenFeign可以与Hystrix和Sentinel结合使用,实现降级和熔断。
使用OpenFeign需要引入OpenFeign的依赖:
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
spring-cloud-starter-openfeign
引入的依赖如下:
[INFO] +- org.springframework.cloud:spring-cloud-starter-openfeign:jar:2.2.6.RELEASE:compile
[INFO] | +- org.springframework.cloud:spring-cloud-openfeign-core:jar:2.2.6.RELEASE:compile
[INFO] | | +- org.springframework.boot:spring-boot-starter-aop:jar:2.3.2.RELEASE:compile
[INFO] | | | \- org.aspectj:aspectjweaver:jar:1.9.6:compile
[INFO] | | \- io.github.openfeign.form:feign-form-spring:jar:3.8.0:compile
[INFO] | | +- io.github.openfeign.form:feign-form:jar:3.8.0:compile
[INFO] | | \- commons-fileupload:commons-fileupload:jar:1.4:compile
[INFO] | +- io.github.openfeign:feign-core:jar:10.10.1:compile
[INFO] | +- io.github.openfeign:feign-slf4j:jar:10.10.1:compile
[INFO] | \- io.github.openfeign:feign-hystrix:jar:10.10.1:compile
[INFO] | +- com.netflix.archaius:archaius-core:jar:0.7.6:compile
[INFO] | \- com.netflix.hystrix:hystrix-core:jar:1.5.18:compile
[INFO] | \- org.hdrhistogram:HdrHistogram:jar:2.1.9:compile
默认已经自动引入了hystrix的依赖,不再需要单独再引入hystrix了。
降级方法的类需要实现FeignClient的接口,同时这个类需要注入到Spring容器中:
package com.morris.user.client;
import com.morris.user.entity.Order;
import com.morris.user.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@Service
@Slf4j
public class OrderFeignService implements OrderClient {
@Override
public List<Order> findOrderByUserId(Long userId) {
log.error("findOrderByUserIdFall error {}", userId);
return Collections.emptyList();
}
}
@FeignClient注解中指定fallback属性:
@FeignClient(value = "order-service", path = "/order", fallback = OrderFeignService.class)
public interface OrderClient {
... ...
在配置文件中开启hystrix:
feign:
hystrix:
enabled: true
使用fallback实现降级方法无法获取到异常信息,而使用fallbackFactory实现降级方法可以获取到异常信息。
降级方法的工厂类需要实现FallbackFactory的接口,同时这个类需要注入到Spring容器中:
package com.morris.user.client;
import com.morris.user.entity.Order;
import com.morris.user.entity.User;
import feign.hystrix.FallbackFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@Slf4j
@Service
public class OrderFeignFactory implements FallbackFactory {
@Override
public Object create(Throwable throwable) {
log.error("OrderFeignFactory ", throwable);
return new OrderClient() {
@Override
public List<Order> findOrderByUserId(Long userId) {
return Collections.emptyList();
}
}
}
}
@FeignClient注解中指定fallback属性:
@FeignClient(value = "order-service", path = "/order", fallbackFactory = OrderFeignFactory.class)
public interface OrderClient {
... ...
在配置文件中开启hystrix:
feign:
hystrix:
enabled: true
Hystrix熔断的功能是默认开启的,commandKey为类名#方法名(参数类型)
,例如上面的方法对应的commandKey为OrderClient#findOrderByUserId(Long)
。
可以在配置文件中根据commandKey对熔断的一些参数进行设置:
hystrix:
command:
OrderClient#findOrderByUserId(Long):
circuitBreaker:
enabled: false
requestVolumeThreshold: 2
execution:
timeout:
enabled: true
isolation:
thread:
#设置请求超时时间,默认1秒,超过指定的时间后,触发服务熔断
timeoutInMilliseconds: 10000
引入Sentinel的依赖:
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
dependency>
并开启Sentinel的熔断降级功能:
spring:
sentinel:
transport:
dashboard: 127.0.0.1:8080
feign:
sentinel:
enabled: true
降级的使用还是跟前面的一样,使用fallback和fallbackFactory实现。
虽然熔断的功能开启了,但是需要配置熔断规则,没有配置规则是不会触发熔断的。
配置如下降级规则测试熔断:
[
{
"count": 0.5,
"grade": 1,
"limitApp": "default",
"minRequestAmount": 5,
"resource": "GET:http://order-service/order/findOrderByUserId",
"slowRatioThreshold": 1,
"statIntervalMs": 5000,
"timeWindow": 5000
}
]
OpenFeign可以配置一个全局异常,来对请求过程中的其他异常进行包装,这样在fallbackFactory中获取到的是自定义的全局异常,而不是原始的异常。
package com.morris.user.config;
import feign.Response;
import feign.Util;
import feign.codec.ErrorDecoder;
import feign.form.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;
import java.io.Reader;
import java.text.MessageFormat;
@Configuration
@Slf4j
public class FeignErrorDecoder implements ErrorDecoder {
@Override
public Exception decode(String methodKey, Response response) {
Reader reader = null;
try {
reader = response.body().asReader(CharsetUtil.UTF_8);
String errMsg = Util.toString(reader);
log.error("FeignErrorDecoder {}", errMsg);
return new RuntimeException(errMsg);
} catch (IOException e) {
return new RuntimeException(MessageFormat.format("自定义Feign错误信息出错:{0}", e.getMessage()));
} finally {
if (null != reader) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}