Feign--Fallback 蔚落 2023-03-01 13:36 31阅读 0赞 原文网址:[Feign--Fallback\_IT利刃出鞘的博客-CSDN博客][Feign--Fallback_IT_-CSDN] # 其他网址 # > [Spring Cloud Feign(第四篇)之Fallback\_孙平平的博客-CSDN博客\_fallback][Spring Cloud Feign_Fallback_-CSDN_fallback] > [SpringCloud(三):Feign-单独使用及对hystrix的支持配置 - 掘金][SpringCloud_Feign-_hystrix_ -] # 简介 # **为什么要用fallback** > Feign默认认为非2XX都是异常。对于404这种非常容易抛出的业务异常来说,没两下就circuit break了。 > > 可降级设计中,希望调用某个服务失败的时候能够让流程走下去而非抛异常,但是又不希望每个调用的地方加try catch,这个时候可以用Feign的Fallback。 > > 但是,这种情况很少见。一般直接全局异常处理的。 **hystrix配置** > fallback是hystrix的功能,所以必须开启hystrix(默认是关闭的)。 **共存问题** > fallback比fallbackfactory优先级高。若都存在,会走fallback调用。 **防止hystrix直接调用fallback** 有时候可能feign直接到了fallback中,原因如下: > Hystrix默认的超时时间是1秒,如果超过这个时间尚未响应,将会进入fallback代码。而首次请求往往会比较慢(由于Ribbon是懒加载的,在首次请求时,才会开始初始化相关类),这个响应时间可能就大于1秒了。 解决方法 法1:Ribbon配置饥饿加载(最佳推荐) > 饥饿加载:即在启动的时候便加载所有配置项的应用程序上下文。 > ribbon: > # 饥饿加载 > eager-load: > # 是否开启饥饿加载 > enabled: true > # 饥饿加载的服务 > clients: demo-goods,demo-product 法2:调用端配置 > eureka.client.fetch-registy: true # Fallback # **说明** > Fallback只能覆写方法,但无法捕获异常(获取不到HTTP请求错误状态码和信息)。 > HystrixTargeter.targetWithFallback方法实现了@FeignClient.fallback处理逻辑,通过源码可以知道UserFeignFallback回调类是从Spring容器中获取的,所以UserFeignFallback由spring创建。 **UserFeignClient** > package com.example.product.feign; > > import com.example.product.entity.User; > import org.springframework.cloud.openfeign.FeignClient; > import org.springframework.web.bind.annotation.GetMapping; > import org.springframework.web.bind.annotation.PathVariable; > > @FeignClient(name = "user", fallback = UserFeignClientFallback.class) > public interface UserFeignClient { > @GetMapping("/user/{id}") > public User getUser(@PathVariable("id") Long id); > } **UserFeignFallback** > package com.example.feign; > > import org.springframework.stereotype.Component; > > @Component > public class UserFeignClientFallback implements UserFeignClient { > > @Override > public User getUserByID(String id) { > User user = new User(); > user.setId(-1); > return user; > } > } **ProductController** > package com.example.product.controller; > > import com.example.product.entity.User; > import com.example.product.feign.UserFeignClient; > import org.springframework.beans.factory.annotation.Autowired; > import org.springframework.web.bind.annotation.GetMapping; > import org.springframework.web.bind.annotation.RequestMapping; > import org.springframework.web.bind.annotation.RestController; > > @RestController > @RequestMapping("/product") > public class ProductController { > @Autowired > UserFeignClient userFeignService; > > @GetMapping("/feign") > public User testFeign(){ > User user = userFeignService.getUser(1L);; > return user; > } > } **UserController** > package com.example.user.controller; > > import com.example.user.entity.User; > import org.springframework.beans.factory.annotation.Autowired; > import org.springframework.cloud.client.ServiceInstance; > import org.springframework.cloud.client.discovery.DiscoveryClient; > import org.springframework.web.bind.annotation.GetMapping; > import org.springframework.web.bind.annotation.PathVariable; > import org.springframework.web.bind.annotation.RequestMapping; > import org.springframework.web.bind.annotation.RestController; > > import java.util.logging.Logger; > > @RestController > @RequestMapping("/user") > public class UserController { > @GetMapping("/{id}") > public User getUser(@PathVariable("id") Long id) { > User user = new User(); > user.setId(id); > user.setUserName("user_name_" + id); > user.setAge(20); > return user; > } > } postman访问: [http://localhost:9001/product/feign][http_localhost_9001_product_feign] 正常结果: > { > "id": 1, > "userName": "user_name_1", > "age": 20 > } 将User服务直接关掉:结果 > postman结果: > > { > "id": -1, > "userName": null, > "age": null > } # FallbackFactory # **简介** > 上面的实现方式简单,但无法捕获异常(获取不到HTTP请求错误状态码和信息) ,这时可用工厂模式来实现Fallback **UserFeignClient** > package com.example.product.feign; > > import com.example.product.entity.User; > import org.springframework.cloud.openfeign.FeignClient; > import org.springframework.web.bind.annotation.GetMapping; > import org.springframework.web.bind.annotation.PathVariable; > > @FeignClient(name = "user", fallbackFactory = UserFeignClientFallbackFactory.class) > public interface UserFeignClient { > @GetMapping("/user/{id}") > public User getUser(@PathVariable("id") Long id); > } **UserFeignClientFallbackFactory** > package com.example.product.feign; > > import com.example.product.entity.User; > import feign.hystrix.FallbackFactory; > import org.springframework.stereotype.Component; > > @Component > public class UserFeignClientFallbackFactory implements FallbackFactory<UserFeignClient> { > @Override > public UserFeignClient create(Throwable throwable) { > System.out.println("fallback throwable: " + throwable); > System.out.println("fallback message: " + throwable.getMessage()); > > return new UserFeignClient() { > @Override > public User getUser(Long id) { > User user = new User(); > user.setId(-1L); > return user; > } > }; > } > } **ProductController** > package com.example.product.controller; > > import com.example.product.entity.User; > import com.example.product.feign.UserFeignClient; > import org.springframework.beans.factory.annotation.Autowired; > import org.springframework.web.bind.annotation.GetMapping; > import org.springframework.web.bind.annotation.RequestMapping; > import org.springframework.web.bind.annotation.RestController; > > @RestController > @RequestMapping("/product") > public class ProductController { > @Autowired > UserFeignClient userFeignService; > > @GetMapping("/feign") > public User testFeign(){ > User user = userFeignService.getUser(1L);; > return user; > } > } **UserController** > package com.example.user.controller; > > import com.example.user.entity.User; > import org.springframework.beans.factory.annotation.Autowired; > import org.springframework.cloud.client.ServiceInstance; > import org.springframework.cloud.client.discovery.DiscoveryClient; > import org.springframework.web.bind.annotation.GetMapping; > import org.springframework.web.bind.annotation.PathVariable; > import org.springframework.web.bind.annotation.RequestMapping; > import org.springframework.web.bind.annotation.RestController; > > import java.util.logging.Logger; > > @RestController > @RequestMapping("/user") > public class UserController { > @GetMapping("/{id}") > public User getUser(@PathVariable("id") Long id) { > User user = new User(); > user.setId(id); > user.setUserName("user_name_" + id); > user.setAge(20); > return user; > } > } postman访问: [http://localhost:9001/product/feign][http_localhost_9001_product_feign] 正常结果: > { > "id": 1, > "userName": "user_name_1", > "age": 20 > } 将User服务直接关掉:结果 > postman结果: > > { > "id": -1, > "userName": null, > "age": null > } > 后端打印 > > fallback throwable: java.lang.RuntimeException: com.netflix.client.ClientException: Load balancer does not have available server for client: user > fallback message: com.netflix.client.ClientException: Load balancer does not have available server for client: user ErrorDecoder接口处理请求错误信息,ErrorDecoder.Default这个默认实现抛出FeignException异常。 > FeignException.status 方法返回HTTP状态码,FallbackFactory.create默认情况下可以强制转换成FeignException异常这样就可以获取到HTTP状态码了。 # 自定义ErrorDecoder # **其他网址** > [3.Feign 服务异常不进入熔断][3.Feign] > [Feign 自定义 ErrorDecoder (捕获 Feign 服务端异常) - VictorBu - 博客园][Feign _ ErrorDecoder _ Feign _ - VictorBu -] > ErrorDecoder与fallback的执行顺序:先走ErrorDecoder拦截器,再走熔断的fallback。 > @FeignClient加上decode404 = true这一个参数,Feign对于2XX和404 ,都不会走Fallback了。 > 排除404,已经基本上够用了,如果想把409、400等status也加到例外中,可以重写一下Feign的errorDecoder。 **常用处理** > **1.解析错误** > > package com.example.product.config; > > import feign.Response; > import feign.Util; > import feign.codec.ErrorDecoder; > import org.springframework.context.annotation.Configuration; > import org.springframework.http.HttpStatus; > > import java.io.IOException; > > @Component > public class KeepErrMsgConfiguration implements ErrorDecoder { > @Override > public Exception decode(String methodKey, Response response) { > System.out.println(response.status()); > if (response.status() != HttpStatus.OK.value()) { > try { > // 原始的返回内容 > String json = Util.toString(response.body().asReader()); > System.out.println(json); > return new RuntimeException("try中的异常"); > } catch (IOException e) { > return new RuntimeException("cat中的异常"); > } > } > return new RuntimeException("if外的异常"); > } > } > **2.不进入熔断逻辑,只是把异常原样往外抛。** > > package com.example.product.config; > > import com.netflix.hystrix.exception.HystrixBadRequestException; > import feign.Response; > import feign.Util; > import feign.codec.ErrorDecoder; > import org.springframework.context.annotation.Configuration; > import org.springframework.http.HttpStatus; > > import java.io.IOException; > > @Component > public class NotBreakerConfiguration implements ErrorDecoder { > @Override > public Exception decode(String methodKey, Response response) { > System.out.println(response.status()); > Exception exception = null; > if (response.status() != HttpStatus.OK.value()) { > try { > // 获取原始的返回内容(来自GlobalExceptionHandler类中的返回信息) > String json = Util.toString(response.body().asReader()); > System.out.println(json); > exception = new HystrixBadRequestException("if中的"); > } catch (IOException e) { > exception = new HystrixBadRequestException(e.getMessage()); > } > } > return exception; > } > } **配置方法** > **第一种:@Configuration** > > 如上边所示,直接加此注解即可。 > **第二种:@EnableFeignClients** > > 全局配置,@EnableFeignClients.defaultConfiguration注解。 > 此时,对于Decoder的配置类,就像上边那样,去掉@Configuration即可。 > > package com.example; > > import com.example.feign.FeignClientsConfig; > import org.springframework.boot.SpringApplication; > import org.springframework.boot.autoconfigure.SpringBootApplication; > import org.springframework.cloud.netflix.feign.EnableFeignClients; > > @EnableFeignClients( > defaultConfiguration = NotBreakerConfiguration.class > ) > > @SpringBootApplication > public class FeignApplication { > public static void main(String[] args) { > SpringApplication.run(FeignApplication.class, args); > } > } > **第三种:@FeignClient** > > @FeignClient.configuration 注解。作用范围是Feign接口,优先级要高于上面两种。 > 此时,对于Decoder的配置类,就像上边那样,去掉@Configuration即可。 > > package com.example.feign; > > import org.springframework.cloud.netflix.feign.FeignClient; > import org.springframework.web.bind.annotation.GetMapping; > import org.springframework.web.bind.annotation.PathVariable; > import org.springframework.web.bind.annotation.PostMapping; > > import java.util.List; > > @FeignClient(name = "user", url = "${user.url}", > decode404 = true, > fallbackFactory = UserFeignFactory.class, > configuration = NotBreakerConfiguration.class > ) > > public interface UserFeign { > @PostMapping > void save(User user); > > @GetMapping("/{id}") > User getUserByID(@PathVariable("id") String id); > } > **第四种** > > 使用@Configuration > > @Configuration > public class KeepErrMsgConfiguration{ > @Bean > public ErrorDecoder errorDecoder() { > return new MyErrorDecoder(); > } > > public static class MyErrorDecoder implements ErrorDecoder { > > @Override > public Exception decode(String methodKey, Response response) { > > System.out.println(response.status()); > if (response.status() != HttpStatus.OK.value()) { > try { > // 原始的返回内容 > String json = Util.toString(response.body().asReader()); > System.out.println(json); > return new RuntimeException("try中的异常"); > } catch (IOException e) { > return new RuntimeException("cat中的异常"); > } > } > return new RuntimeException("if外的异常"); > } > } > } [Feign--Fallback_IT_-CSDN]: https://knife.blog.csdn.net/article/details/107600124 [Spring Cloud Feign_Fallback_-CSDN_fallback]: https://blog.csdn.net/sun_shaoping/article/details/82079287 [SpringCloud_Feign-_hystrix_ -]: https://juejin.im/post/5cfdbd745188252dd239922d [http_localhost_9001_product_feign]: http://localhost:9001/product/feign [3.Feign]: http://luoma.pro/Content/Detail/458?parentId=5 [Feign _ ErrorDecoder _ Feign _ - VictorBu -]: https://www.cnblogs.com/victorbu/p/12787702.html
还没有评论,来说两句吧...