OpenFeign can be used in conjunction with Hystrix and Sentinel to implement downgrades and circuit breakers.
OpenFeign combined with Hystrix
To use OpenFeign, you need to introduce OpenFeign dependencies:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
The dependencies introduced by spring-cloud-starter-openfeign
are as follows:
[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
The dependency of hystrix has been automatically introduced by default, and there is no need to introduce hystrix separately.
Use fallback to implement downgrade method
The class of the downgrade method needs to implement the FeignClient interface, and this class needs to be injected into the Spring container:
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(); } }
Specify the fallback attribute in the @FeignClient annotation:
@FeignClient(value = "order-service", path = "/order", fallback = OrderFeignService.class) public interface OrderClient {<!-- --> ... ...
Enable hystrix in the configuration file:
feign: hystrix: enabled: true
Use fallbackFactory to implement the downgrade method
Using fallback to implement the downgrade method cannot obtain exception information, but using fallbackFactory to implement the downgrade method can obtain exception information.
The factory class of the fallback method needs to implement the FallbackFactory interface, and this class needs to be injected into the Spring container:
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(); } } } }
Specify the fallback attribute in the @FeignClient annotation:
@FeignClient(value = "order-service", path = "/order", fallbackFactory = OrderFeignFactory.class) public interface OrderClient {<!-- --> ... ...
Enable hystrix in the configuration file:
feign: hystrix: enabled: true
Using Hystrix fuse
The Hystrix circuit breaker function is enabled by default, and the commandKey is Class name#Method name (parameter type)
. For example, the commandKey corresponding to the above method is OrderClient#findOrderByUserId(Long)
.
You can set some parameters of circuit breaker according to commandKey in the configuration file:
hystrix: command: OrderClient#findOrderByUserId(Long): circuitBreaker: enabled: false requestVolumeThreshold: 2 execution: timeout: enabled: true isolation: thread: #Set the request timeout, the default is 1 second. After the specified time is exceeded, the service circuit breaker is triggered. timeoutInMilliseconds: 10000
OpenFeign combined with Sentinel
Introduce Sentinel dependencies:
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
And enable Sentinel’s circuit breaker and downgrade function:
spring: sentinel: transport: dashboard: 127.0.0.1:8080 Feign: sentinel: enabled: true
The use of downgrade is still the same as before, using fallback and fallbackFactory.
The use of Sentinel fuse
Although the fusing function is enabled, the fusing rules need to be configured. Without the configuration rules, the fusing will not be triggered.
Configure the following downgrade rules to test circuit breaker:
[ {<!-- --> "count": 0.5, "grade": 1, "limitApp": "default", "minRequestAmount": 5, "resource": "GET:http://order-service/order/findOrderByUserId", "slowRatioThreshold": 1, "statIntervalMs": 5000, "timeWindow": 5000 } ]
Customized global exception
OpenFeign can configure a global exception to wrap other exceptions in the request process, so that what is obtained in the fallbackFactory is a custom global exception instead of the original exception.
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("Custom Feign error message error: {0}", e.getMessage())); } finally {<!-- --> if (null != reader) {<!-- --> try {<!-- --> reader.close(); } catch (IOException e) {<!-- --> e.printStackTrace(); } } } } }