美文网首页springcloudSpringCloud
5. SpringCloud之Feign使用介绍

5. SpringCloud之Feign使用介绍

作者: 天还下着毛毛雨 | 来源:发表于2022-04-04 21:57 被阅读0次
    image.png

    1. 简介

    Feign Feign是一个声明式的Web服务客户端, 通过代理的方式,封装了对服务提供方的调用过程。
    同时集成了Ribbon用于 对相同服务名称的多台实例进行调用时的负载均衡,集成了Hystrix应对 可能在调用过程中出现的异常 进行服务降级和熔断。

    2. 简单使用

    2.1. 引入依赖

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    

    2.2 启动类加上@EnableFeignClients(clients = OrderService.class)

    @SpringBootApplication
    // 声明OrderService为 Feign客户端
    @EnableFeignClients(clients = OrderService.class)
    public class MicroUserApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(MicroUserApplication.class);
        }
    
    }
    

    2.3. Feign客户端接口

    在类上的@FeignClient 里指定 要调用的服务

    // 指定调用 micro-order服务
    @FeignClient(name = "micro-order")
    public interface OrderService {
    
        // 该方法 对应micro-order服务的 /order/{id} 接口,url以及传参方式要一致
        @GetMapping("/order/{id}")
        // 参数列表以及返回值要一值
        String getOrderById(@PathVariable Integer id);
    
    }
    

    这个接口里每个方法,与micro-order 的接口 要一一对应,url, 参数列表和返回值以及传参方式都要一样。

    micro-order/order/{id} 接口

    @GetMapping("/{id}")
    public String getOrder(@PathVariable("id") Integer id) {
        int i = 1/id;
        return Thread.currentThread().getName() + ",该服务端口号:" + port + ",这是用户 :" + id + " 所有的订单信息";
    }
    

    2.4 发起远程调用

    在micro-user服务的接口里,直接注入Feign客户端 OrderService实例,调用对应方法即可 调用到到对应服务的对应接口上。

    由于 Feign集成了Ribbon,所以根据 服务名的调用 是会对 该名称的所有可用实例 进行负载均衡的。

    @GetMapping("/getOrderById/{id}")
    public String getOrderById(@PathVariable("id") Integer id) {
        return orderService.getOrderById(id);
    }
    
    image.png

    3. Feign 的服务隔离

    在对Feign客户端的代理对象里,又封装了 Hystrix的 服务隔离,和服务降级,以及服务熔断等功能。

    3.1. 开启Hystrix

    首先需要在application.properties文件开启 Feign中Hystrix

    feign.hystrix.enabled=true
    

    Hystrix 对服务隔离策略 有线程池隔离和 信号量隔离两种策略,默认是线程池,且线程池核心线程数为 10

    3.2. 如何在Feign中配置 Hystrix的线程隔离策略呢?

    3.2.1. 直接使用 Hystrix 的配置方式 :

    要么在代码里的@HystrixCommand 里配置单独的

    @HystrixCommand(
            commandKey = "getOrder",
            groupKey = "getGroup-one",
            // 指定降级的方法
            fallbackMethod = "getOrderListFallbackMethod",
            threadPoolKey = "getOrderThreadPool",
            commandProperties = {
                    // 将隔离策略为线程池
                    @HystrixProperty(name = "execution.isolation.strategy", value = "THREAD"),
            },
            threadPoolProperties = {@HystrixProperty(name = "coreSize",value = "100")}
    )
    
    

    或者在 application.properties文件里对每个commandKey进行配置

    # 配置隔离策略为 信号量,最大并发数为1
    hystrix.command.getOrder.execution.isolation.strategy=SEMAPHORE
    hystrix.command.getOrder.execution.isolation.semaphore.maxConcurrentRequests=1
    

    3.2.2. Feign中 Hystrix的配置方式

    由于我们没有 直接导入 Hystrix的依赖,而是用Feign中 集成的Hystrix,所以找不到 @HystrixCommand 这个注解,只能在application.properties中配置

    最大的问题就是 Feign服务端方法的 commandKey该配置啥?

    # 配置隔离策略为 信号量,最大并发数为1
    hystrix.command.commandKey.execution.isolation.strategy=SEMAPHORE
    hystrix.command.commandKey.execution.isolation.semaphore.maxConcurrentRequests=1
    

    我们直接debugger到SetterFactory.Default.create这个类,可以看到这个commandKey 由Feign生成, 格式是 '接口名 #方法名(参数类型列表)'

    知道commandKey之后, 其他配置和单独用hystrix的配置方式一样 :

    feign.hystrix.enabled=true
    
    hystrix.command.orderService#getOrderById(Integer).execution.isolation.strategy=SEMAPHORE
    hystrix.command.orderService#getOrderById(Integer).execution.isolation.semaphore.maxConcurrentRequests=1
    

    4. Feign 的服务降级

    又是封装的Hystrix的功能,用法略微有点不同,Feign中直接在@FeignClient 中配置。

    4.1 代码使用

    4.1.1. 第一种:fallback + Feign客户端类的实现类

    //OrderServiceFallback 要重写 OrderService 的所有方法,实现降级的具体逻辑
    @FeignClient(name = "micro-order",fallback = OrderServiceFallback.class);
    public interface OrderService {
    
        @GetMapping("/order/{id}")
        String getOrderById(@PathVariable Integer id);
    
    }
    
    @Component
    public class OrderServiceFallback implements OrderService {
        @Override
        public String getOrderById(Integer id) {
            return "降级方法";
        }
    }
    

    4.1.2. 第二种:fallbackFactory

    注册一个FallbackFactory的实现类,重写create 方法,返回实现Feign客户端接口 的 子类实例,重写Feign客户端接口的方法,实现降级的具体逻辑

    @FeignClient(name = "micro-order",fallbackFactory = OrderServiceFallbackFactory.class)
    public interface OrderService {
    
        @GetMapping("/order/{id}")
        String getOrderById(@PathVariable Integer id);
    
    }
    
    @Component
    public class OrderServiceFallbackFactory implements FallbackFactory<OrderService> {
        @Override
        public OrderService create(Throwable throwable) {
           return new OrderService() {
               @Override
               public String getOrderById(Integer id) {
                   return "服务降级";
               }
           };
        }
    }
    

    5. Feign 的 服务熔断

    又是封装的Hystrix的功能,功能与概念 都一摸一样,这里就不再赘述了。

    6. Feign 的异常过滤

    这个过滤器是对异常信息的再封装,把 feign 的异常信息封装成我们系统的通用异常对象。

    过滤器把异常返回后,feign 前面定义的降级方法就会调到 create 方法。

    @Configuration
    public class FeignErrMessageFilter {
    @Bean
    public ErrorDecoder errorDecoder() {
        return new FeignErrorDecoder();
    }
    
    /*
    * 当调用服务时,如果服务返回的状态码不是200,就会进入到Feign的ErrorDecoder中
    * {"timestamp":"2020-02-17T14:01:18.080+0000","status":500,"error":"Internal Server Error","message":"/ by zero","path":"/feign/student/errorMessage"}
    * 只有这种方式才能获取所有的被feign包装过的异常信息
    *
    * 这里如果创建的Exception是HystrixBadRequestException
    * 则不会走熔断逻辑,不记住熔断统计
    * */
    class FeignErrorDecoder implements ErrorDecoder {
    
        private Logger logger = LoggerFactory.getLogger(FeignErrorDecoder.class);
    
        @Override
        public Exception decode(String s, Response response) {
    
            RuntimeException runtimeException = null;
    
            try {
                String retMsg = Util.toString(response.body().asReader());
                logger.info(retMsg);
    
                runtimeException = new RuntimeException(retMsg);
    
            } catch (IOException e) {
                e.printStackTrace();
            }
            return runtimeException;
        }
    }
    

    相关文章

      网友评论

        本文标题:5. SpringCloud之Feign使用介绍

        本文链接:https://www.haomeiwen.com/subject/lrrhsrtx.html