Spring Cloud Feign - fallback service downgrade
1. Introduction to Feign
Feign
is a declarative HTTP
client that simplifies the process of writing code for inter-service communication based on REST
. In Spring Cloud
, Feign plays an important role, it has pluggable annotation support, including Feign annotations and JAX-RS annotations. Feign
Also supports pluggable 编码器
and 解码器
. Spring Cloud
Added support for Spring MVC
annotations and uses the HttpMessageConverters
used by default in Spring Web. Spring Cloud integrates Ribbon
and Eureka
or Nacos
registries to provide load balancing when using Feign
http client.
Feign feature introduction:
- Declarative REST client:
Feign
allows developers to define calls to remote REST services using interfaces and annotations without having to worry about Low-level HTTP request and response handling. In Feign, various operations of remote services can be described through the definition of interface methods, including URL, request method, parameters and other information. - Integrated Load Balancing:
By integrating theRibbon
load balancer,Feign
Can automatically implement load balancing calls to specified services. This allowsFeign
to select the appropriate service instance to call based on the load balancing policy when the service provider has multiple instances. - Supports multiple request methods:
Feign
Supports various HTTP request methods, such asGET
,, etc., thus making the call to remote services very flexible. ,POST
,PUT
DELETE
- Dynamic URL and parameter binding:
Using Feign, parameters can be bound to theURL
template , and pass dynamic data to the remote service. At the same time,Feign
also supports binding parameters into the request body forPOST
orPUT
requests. - Integrated Hystrix fault tolerance mechanism:
Combined with theHystrix
circuit breaker in Spring Cloud, Feign can provide remote The fault tolerance of service calls includes timeout, circuit breaker and other functions, thereby enhancing the reliability of service calls. - Easy to integrate:
In a Spring Cloud application,Feign
integrates with other components such as, etc.) are seamlessly integrated and can be quickly enabled through simple dependency configuration and annotations, without the need for additional cumbersome configuration.Eureka
,Zuul
In short, Spring Cloud Feign provides a concise and elegant way to define and call REST services. It simplifies the process of communication between services, improves development efficiency, and provides convenient support for building applications with a microservice architecture.
2. Basic use of Feign
Build a SpringCloud Demo, including producers and consumers. They are all ordinary web services, in which consumers have more dependencies on Feign than producers.
<!-- SpringCloud Openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
And add annotations to the startup class:@EnableFeignClients
If fallbackFactory
downgrade is configured, you also need to introduce dependencies:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
Then add annotations to the startup class:@EnableHystrix
, and add the configuration to enable service degradation:
feign:
hystrix:
enabled: true
2.1 Ordinary HTTP request
The producer provides an interface ofPOST
:/student/queryList
to query the data of the student table in the database.
@RestController
@RequestMapping("/student")
public class StudentController {
@Resource
private IStudentService studentService;
@GetMapping("/queryList")
public ApiR queryList(){
List<Student> studentList = studentService.list();
return ApiR.ok().data(studentList);
}
}
The consumer uses Feign to remotely call the interface of this query list:
First create a new @FeignClient
annotated interface
import com.lin.common.config.FeignSupportConfig;
import com.lin.common.constant.ServiceNameConstants;
import com.lin.common.vo.ApiR;
import com.lin.consumer.feign.fallback.RemoteStudentServiceFallback;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
/**
* @author linmengmeng
* @since 2023/11/12 14:12
*/
@FeignClient(contextId = "RemoteStudentService", name = ServiceNameConstants.PROVIDER_SERVICE
, configuration = FeignSupportConfig.class, fallbackFactory = RemoteStudentServiceFallback.class)
public interface RemoteStudentService {
@GetMapping ("/student/queryList")
ApiR queryList();
}
I have registered both the producer and the consumer toNacos
, so I can directly configure it herename = ServiceNameConstants.PROVIDER_SERVICE
and call it directly through the producer's service name. the other party’s interface.
and configured a downgrade callback fallbackFactory = RemoteStudentServiceFallback.class
, the downgrade operation implements the interface defined above and outputs specific exception information;
import com.lin.common.vo.ApiR;
import com.lin.consumer.feign.RemoteStudentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
/**
* @author linmengmeng
* @since 2023/11/12 14:22
*/
@Slf4j
@Component
public class RemoteStudentServiceFallback implements FallbackFactory<RemoteStudentService> {
@Override
public RemoteStudentService create(Throwable cause) {
log.error("RemoteStudentServiceFallback error : {}", cause.getMessage());
return () -> {
log.error("sayHello error 接口调用异常");
return ApiR.fail();
};
}
}
Add a test request interface and call the feign interface above:
import cn.hutool.json.JSONUtil;
import com.lin.common.vo.ApiR;
import com.lin.common.vo.R;
import com.lin.consumer.feign.RemoteStudentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* 测试
* @author linmengmeng
* @since 2023/11/8 14:18
*/
@Slf4j
@RestController
@RequestMapping("/test")
public class TestController {
@Resource
private RemoteStudentService remoteStudentService;
@GetMapping("/sayHello")
public R sayHello() {
log.info("sayHello。。。。。。");
ApiR apiR = remoteStudentService.queryList();
log.info("接口调用结果-apiR:{}", JSONUtil.toJsonStr(apiR));
return R.ok();
}
}
Start the consumer first, and then use Postman to call the consumer'ssayHello
interface. You can see that Feign's remote interface is called in this interface. At this time, the producer has not started and the service The interfacequeryList
will definitely be blocked. According to the original OkHTTP call, if the remote interface is abnormal, the exception will be passed to the interface caller. After the above configuration, let's call the interface and take a look:
You can see that the interface responded normally, and then look at the consumer's log:
2023-11-12 22:18:11.504 INFO [io-37002-exec-1] [] c.l.consumer.controller.TestController [29] : sayHello。。。。。。
2023-11-12 22:18:12.030 WARN [reakerFactory-1] [] o.s.c.l.core.RoundRobinLoadBalancer [97] : No servers available for service: cloud-feign-provider
2023-11-12 22:18:12.033 WARN [reakerFactory-1] [] .s.c.o.l.FeignBlockingLoadBalancerClient [103] : Load balancer does not contain an instance for the service cloud-feign-provider
2023-11-12 22:18:12.065 ERROR [reakerFactory-1] [] c.l.c.f.f.RemoteStudentServiceFallback [18] : RemoteStudentServiceFallback error : [503] during [POST] to [http://cloud-feign-provider/student/queryList] [RemoteStudentService#queryList()]: [Load balancer does not contain an instance for the service cloud-feign-provider]
2023-11-12 22:18:12.072 ERROR [reakerFactory-1] [] c.l.c.f.f.RemoteStudentServiceFallback [20] : sayHello error 接口调用异常
2023-11-12 22:18:12.204 INFO [io-37002-exec-1] [] c.l.consumer.controller.TestController [31] : 接口调用结果-apiR:{
"code":500,"message":"系统繁忙,请稍后再试!","data":{
"changeLog":"","detail":{
},"releaseTime":"2023-11-12 22:18:12"}}
It can be seen that after the interface call is successful, when the Feign remote interface is called, two lines of Warn logs are printed, prompting us that the producer service is not found, and then the error log in our downgrade service is printed, and the interface we requested using postman Responded normally.
This is different from the OkHttp remote call interface we used before. Although the upstream service is abnormal here, it does not affect the call on our side. For consumers, it has its own closed-loop logic and will not pass the exception to itself. , the results and logs of the downgraded service can be obtained normally in our business logic.
Then start the producer's services respectively and call the consumer's interface again:
2023-11-12 22:31:50.722 INFO [io-37002-exec-2] [] c.l.consumer.controller.TestController [29] : sayHello。。。。。。
2023-11-12 22:31:51.312 INFO [io-37002-exec-2] [] c.l.consumer.controller.TestController [31] : 接口调用结果-apiR:{
"code":200,"message":"成功","data":[{
"id":"001","name":"Tom","age":19,"createTime":"2023-11-12T21:19:15"},{
"id":"002","name":"Mary","age":18,"createTime":"2023-11-12T21:19:39"}]}
As you can see, the interface responded normally and the data was obtained.