• Spring-Cloud之Feign


    原文:https://blog.vchar.top/java/1621167133.html

    Feign的引入可以让我们通过接口注解的形式实现服务间的调用,让调用者无需再关心接口地址这些配置,同时对于接口需要的参数能够更清晰地了解,简化了调用的流程。下面我们通过一些Feign的示例带你快速了解如何使用它。示例使用的Spring-Cloud的版本是Hoxton.SR8,Spring-Boot的版本是2.3.4.RELEASE。示例项目的源代码

    添加相关的依赖

    feign中已经对ribbon进行集成支持,在spring最新版本已经开始弃用ribbon了,而推荐使用Cloud LoadBalancer(feign也支持)。

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

    使用示例

    feign其实是一个伪RPC的组件;我们之前在服务间调用需要的使用者去手动设置请求地址,在加入ribbon后,虽然省略了具体的服务器地址和端口,但是接口地址还是需要手动编写;而引入feign后就可以解决这个问题,由接口的开发者提供给调用者一个接口类的方法的jar包,调用者直接依赖该包,然后注入该接口类即可像调用方法一样调用其他的服务。同时feign提供了在方法调用失败时的处理策略。这样如果接口地址发生改变了,那么只需要更新jar包即可;同时调用者在使用的时候也会更加清晰具体的参数。

    要使feign生效,我们还需要在启动类上添加如下注解来启用feign:@EnableFeignClients ;下面对该注解主要的属性进行说明:

    • value和name:服务名称;这个2个随意选择一个配置即可;
    • path:请求路径的前缀;当多个接口地址有相同的前缀时,可以将前缀配置到path中;
    • fallback和fallbackFactory:这2个都是对接口熔断降级处理,也就是异常处理,只能选择其中一个;

    简单示例

    定义一个接口,在里面编写需要调用的某个服务的restful接口,然后在类上添加 @FeignClient ,在上面配置要调用的服务名称,也就是说feign会帮助我们拼接这个请求地址(也就是我们在ribbon那里做的事情),因此必须正确才可以。

    @FeignClient(name = "feign-server-b") // 服务名称,必须匹配,否则待会将无法替换为真实的地址
    public interface GoodsFeignClientDemo1 {
    
        /**
         * 这里的路径必须和对应要调用的接口地址对应
         */
        @GetMapping("/goods")
        Goods findById(@RequestParam(value = "id")Long id);
    }
    

    在业务代码中使用

    @Service
    public class OrderServiceImpl implements OrderService {
        
        // 注入该接口的feign接口
        @Autowired
        private GoodsFeignClientDemo1 goodsFeignClientDemo1;
    
        @Override
        public void demo1() {
            Goods goods = goodsFeignClientDemo1.findById(1L);
            System.out.println("示例1:"+goods);
        }
    }
    

    编写feign客户端接口的参数注解示例

    由于feign最终是要根据我们编写的接口方法来组装请求的参数和方式的,因此为了让feign能够正常识别我们的参数,需要加上相关注解才可行;比如GET请求的参数加上@RequestParam注解,如果是个对象就需要加上@SpringQueryMap注解。下面是一些常用的示例。

    • GET请求参数中有多个参数
    @GetMapping("/goods/demo2")
    Goods findById(@RequestParam(value = "id") Long id, @RequestParam(value = "goodsName")String goodsName);
    
    • GET请求参数是一个自定义对象
    @GetMapping("/goods/demo3")
    Goods findById(@SpringQueryMap GoodsParams params);
    
    • GET请求参数是一个自定义对象和普通类型的组合
    @GetMapping("/goods/demo4")
    Goods findById(@SpringQueryMap GoodsParams params, @RequestParam(value = "id") Long id);
    
    • GET请求参数在URL路径上
    @GetMapping("/goods/demo5/{id}")
    Goods findGoods(@PathVariable(value = "id") Long id);
    
    • 请求参数在body里面
    @PostMapping("/goods/demo6")
    String checkGoods(@RequestBody GoodsParams params);
    

    为feign添加接口异常处理

    可以通过配置@EnableFeignClients注解的fallback或者是fallbackFactory的属性来实现在请求接口异常时返回默认的数据。注意需要在配置中打开feign的hystrix配置:

    feign:
      hystrix:
        enabled: true
    

    使用fallback

    注意:使用fallback无法打印具体的异常信息

    @FeignClient(name = "feign-server-b", fallback = GoodsFeignClientFallback.class)
    public interface GoodsFeignClientDemo3 {
    
        @GetMapping("/goods")
        Goods findById(@RequestParam(value = "id")Long id);
    }
    

    异常处理实现类

    @Component
    public class GoodsFeignClientFallback implements GoodsFeignClientDemo3 {
    
        @Override
        public Goods findById(Long id) {
            System.out.println(id+"请求接口异常");
            return null;
        }
    }
    

    使用fallbackFactory

    @FeignClient(name = "feign-server-b", fallbackFactory = GoodsFeignClientFallbackFactory.class)
    public interface GoodsFeignClientDemo4 {
        
        @GetMapping("/goods")
        Goods findById(@RequestParam(value = "id")Long id);
    }
    

    异常处理实现类

    @Component
    public class GoodsFeignClientFallbackFactory implements FallbackFactory<GoodsFeignClientDemo4> {
        @Override
        public GoodsFeignClientDemo4 create(Throwable throwable) {
            // 这里通过匿名实现
            return new GoodsFeignClientDemo4() {
                @Override
                public Goods findById(Long id) {
                    // 这里可以进行异常的处理
                    System.out.println(id+"请求接口异常: "+throwable.getMessage());
                    return null;
                }
            };
        }
    }
    

    通过feign自定义拦截器实现请求统一参数添加

    当我们需要在请求中添加一些默认的统一参数,那么可以通过添加自定义拦截器来实现。首先需要实现RequestInterceptor接口

    public class CustomRequestInterceptor implements RequestInterceptor {
    
        @Override
        public void apply(RequestTemplate requestTemplate) {
            System.out.println("拦截请求...做统一处理");
            requestTemplate.header("uid", "9527");
        }
    }
    

    定义bean配置类

    public class FeignConfiguration {
    
        @Bean
        public RequestInterceptor requestInterceptor(){
            return new CustomRequestInterceptor();
        }
    }
    

    在feignClient上配置

    @FeignClient(name = "feign-server-b", configuration = FeignConfiguration.class)
    public interface GoodsFeignClientDemo5 {
    
        @GetMapping("/goods")
        Goods findById(@RequestParam(value = "id") Long id);
    }
    

    一些可能遇见的问题

    当一个服务需要暴露的接口较多时,通常我们会选择分模块分包分类来处理,此时对一个服务来说就要定义多个feign client接口;由于最终bean的名称是根据服务名来生成,因此会出现同名的bean导致启动失败。也就是会出现如下提示:

    2021-05-17 17:02:49.325 ERROR 39740 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   : 
    
    ***************************
    APPLICATION FAILED TO START
    ***************************
    
    Description:
    
    The bean 'feign-server-b.FeignClientSpecification' could not be registered. A bean with that name has already been defined and overriding is disabled.
    
    Action:
    
    Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
    

    这样只需要在配置文件里面加上如下配置即可:spring.main.allow-bean-definition-overriding=true

    同时项目中接口地址和feign中地址必须保持唯一(也就是RequestMapping中配置的值),否则将可能会出现问题。

    feign配置

    对请求参数和响应的数据进行压缩,默认是没有开启的。

    feign:
      compression:
        # 请求和响应压缩配置
        request:
          enabled: true
          mime-types: text/xml,application/xml,application/json
          min-request-size: 2048
        response:
          enabled: true
    

    对服务进行个性化配置

    feign:
      client:
        config:
          # 全局配置
          default:
            connectTimeout: 1000
            readTimeout: 1000
            loggerLevel: BASIC
          # 单独某个服务配置
          feign-server-b:
            connectTimeout: 5000
            readTimeout: 5000
            loggerLevel: full
    

    在开发测试环境打开feign的请求日志打印

    feign:
      client:
        config:
          # 单独某个服务配置
          feign-server-b:
            connectTimeout: 5000
            readTimeout: 5000
            loggerLevel: full
    # 将feign客户端请求类的日志级别调整为 DEBUG  
    logging.level.top.vchar.feign.GoodsFeignClientDemo5: DEBUG
    

    加上之后控制台会打印如下日志:

    ...
    2021-05-18 10:10:15.551 DEBUG 41344 --- [eign-server-b-1] top.vchar.feign.GoodsFeignClientDemo5    : [GoodsFeignClientDemo5#findById] ---> GET http://feign-server-b/goods?id=1 HTTP/1.1
    2021-05-18 10:10:15.551 DEBUG 41344 --- [eign-server-b-1] top.vchar.feign.GoodsFeignClientDemo5    : [GoodsFeignClientDemo5#findById] Accept-Encoding: gzip
    2021-05-18 10:10:15.551 DEBUG 41344 --- [eign-server-b-1] top.vchar.feign.GoodsFeignClientDemo5    : [GoodsFeignClientDemo5#findById] Accept-Encoding: deflate
    2021-05-18 10:10:15.551 DEBUG 41344 --- [eign-server-b-1] top.vchar.feign.GoodsFeignClientDemo5    : [GoodsFeignClientDemo5#findById] uid: 9527
    2021-05-18 10:10:15.551 DEBUG 41344 --- [eign-server-b-1] top.vchar.feign.GoodsFeignClientDemo5    : [GoodsFeignClientDemo5#findById] ---> END HTTP (0-byte body)
    ...
    

  • 相关阅读:
    不负时光,不负自己
    理解无偏估计(unbiased estimation)
    Latex Error:‘acmart.cls’ not found 解决方案:
    Dark theme for Texstudio
    马尔可夫毯(Markov Blanket)
    时间复杂度和空间复杂度的简单讲解
    应用层级时空记忆模型(HTM)实现对实时异常流时序数据检测
    ElasticSearch集群状态查看命令大全
    ElasticSearch API 之 UPDATE
    ElasticSearch API 之 DELETE
  • 原文地址:https://www.cnblogs.com/vchar/p/14778044.html
Copyright © 2020-2023  润新知