【微服务】SpringCloud(SpringCloud Alibaba)微服务秒杀项目-05:OpenFeign服务接口调用

SpringCloud-05: OpenFeign服务接口调用

项目Github地址

Spring Cloud OpenFeign 官方文档

微服务服务接口调用

  • 之前的调用方式
    • restTemplate.getForObject(PAYMENT_URL + "/payment/get/" + id, Result.class);
  • 引入Feign/OpenFeign后的调用方式
    • paymentFeignService.getPayment(id);

引入Feign后,调用远程微服务方法就跟调用本地方法一样,相当于由Feign包装了一层。简化了微服务的调用。

Feign与OpenFeign

简单理解:OpenFeign = 加强版 Feign,且Feign已停止维护,故选择OpenFeign。

集成OpenFeign

  • 添加依赖

    				<!--   OpenFeign   -->
            <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-openfeign -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-openfeign</artifactId>
            </dependency>
    
  • 创建@FeignClient接口

    package cc.seckill.springcloud.service;
    
    import cc.seckill.springcloud.entities.Result;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.stereotype.Component;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    
    /**
     * description: PaymentFeignService <br>
     * date: 2022/4/13 10:53 <br>
     * author: hq <br>
     * version: 1.0 <br>
     */
    @Component
    @FeignClient(value = "CLOUD-PAYMENT-SERVICE")
    public interface PaymentFeignService {
    
        @GetMapping(value = "/payment/get/{id}")
        public Result getPayment(@PathVariable("id") Long id);
    }
    

    接口里这个方法就是服务方方法的签名,这样的话OpenFeign就会根据我们配置的服务名(@FeignClient(value = “CLOUD-PAYMENT-SERVICE”))以及对应的方法,帮我们去调用

    对比原来直接调用RestTemplate实例来访问的方式,这种面向接口编程,相当于直接调用对面方法的这种形式比原来拼接url来调用的形式确实要更优雅,可重用性更好,也方便更好的维护。

    @GetMapping("/consumer/payment/get/{id}")
        public Result getPayment(@PathVariable("id") Long id) {
            return restTemplate.getForObject(PAYMENT_URL + "/payment/get/" + id,
                    Result.class);
        }
    
  • 创建Controller,直接调用本地FeignClient接口。

    @RestController
    @Slf4j
    public class OrderFeignController {
        @Resource
        private PaymentFeignService paymentFeignService;
    
        @GetMapping(value = "/consumer/feign/payment/get/{id}")
        public Result getPaymentById(@PathVariable("id") Long id) {
            return paymentFeignService.getPayment(id);
        }
    }
    
  • 主启动类开启FeignClient

    @SpringBootApplication
    @EnableEurekaClient
    @EnableFeignClients
    public class OrderMain {
        public static void main(String[] args) {
            SpringApplication.run(OrderMain.class, args);
        }
    }
    
  • 修改自定义负载均衡算法,主要是进行了日志的打印,打印每次选择了哪个服务实例

    @Slf4j
    public class CustomRandomLoadBalancerClient implements ReactorServiceInstanceLoadBalancer {
    
        // 服务列表
        private final ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
    
        public CustomRandomLoadBalancerClient(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider) {
            this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
        }
    
        @Override
        public Mono<Response<ServiceInstance>> choose(Request request) {
            ServiceInstanceListSupplier supplier =
                    serviceInstanceListSupplierProvider.getIfAvailable();
            return supplier.get().next().map(this::getInstanceResponse);
        }
    
        /**
         * 使用随机数获取服务
         *
         * @param instances
         * @return
         */
        private Response<ServiceInstance> getInstanceResponse(
                List<ServiceInstance> instances) {
    //        System.out.println("进来了");
            log.info("调用自定义负载均衡算法");
            if (instances.isEmpty()) {
                return new EmptyResponse();
            }
    
            // 随机算法
            int size = instances.size();
            Random random = new Random();
            ServiceInstance instance = instances.get(random.nextInt(size));
            log.info("随机选取的服务实例为 :{}", instance);
    
            return new DefaultResponse(instance);
        }
    }
    
  • 启动项目

  • 测试,多次刷新。

    image-20220413123559502
  • 查看日志,发现我们自定义的客户端负载均衡算法同样生效。

    image-20220413123710318

OpenFeign超时控制

  • 模仿业务超时

    在服务提供者payment8001的Controller添加

    		@GetMapping(value = "/payment/feign/timeout")
        public String paymentFeignTimeout() {
            try {
                // 模仿超时
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return serverPort;
        }
    
  • 在消费者order80的FeignClient添加Feign方法调用

    @Component
    @FeignClient(value = "CLOUD-PAYMENT-SERVICE")
    public interface PaymentFeignService {
    
        @GetMapping(value = "/payment/get/{id}")
        public Result getPayment(@PathVariable("id") Long id);
    
        @GetMapping(value = "/payment/feign/timeout")
        public String paymentFeignTimeout();
    }
    
  • 配置OpenFeign超时时间,这里选择的是yml配置方式,配置类方式请查看官方文档。

    在服务消费者的application.yml添加OpenFeign配置,下面的配置是全局的默认配置。

    feign:
      client:
        config:
          default: 
            connectTimeout: 1000
            readTimeout: 1000
            loggerLevel: basic
    

    单独为某个FeignClient配置

    feign:
        client:
            config:
                feignName:
                    connectTimeout: 5000
                    readTimeout: 5000
                    loggerLevel: full
                    errorDecoder: com.example.SimpleErrorDecoder
                    retryer: com.example.SimpleRetryer
                    defaultQueryParameters:
                        query: queryValue
                    defaultRequestHeaders:
                        header: headerValue
                    requestInterceptors:
                        - com.example.FooRequestInterceptor
                        - com.example.BarRequestInterceptor
                    decode404: false
                    encoder: com.example.SimpleEncoder
                    decoder: com.example.SimpleDecoder
                    contract: com.example.SimpleContract
                    capabilities:
                        - com.example.FooCapability
                        - com.example.BarCapability
                    queryMapEncoder: com.example.SimpleQueryMapEncoder
                    metrics.enabled: false
    

    其中feignName为@FeignClient(value = "CLOUD-PAYMENT-SERVICE")注解中的值。或者value改为name是一样的,下面是@FeignClient注解的源码,可以看到value其实是name的别名。

    		@AliasFor("name")
        String value() default "";
    		@AliasFor("value")
        String name() default "";
    
  • 这里我们先只按默认的配置来做,启动主启动类后,调用,可以看到报超时错误了,因为我们默认设置的调用超时时间是1s

    image-20220413134136780

    image-20220413134330438

  • 继续修改application.yml配置超时时间

    feign:
      client:
        config:
          default: # OpenFeign默认配置
            connectTimeout: 1000  # 默认建立连接的超时时间
            readTimeout: 1000     # 默认方法调用超时时间
            loggerLevel: basic    # 日志打印级别
          CLOUD-PAYMENT-SERVICE:
            connectTimeout: 1000  # 默认建立连接的超时时间
            readTimeout: 5000     # 默认方法调用超时时间
            loggerLevel: basic
    
  • 等热加载完成后,继续调用,等待几秒后,返回了结果,说明我们的配置生效了。

    image-20220413134408096

OpenFeign日志增强

通过调整OpenFeign的日志打印级别,可以修改OpenFeign打印Http请求的细节。

OpenFeign日志级别有:

  • NONE:默认的,不显示任何日志
  • BASIC:仅记录请求方法、URL、响应状态码及执行时间
  • HEADERS:除了BASIC中定义的信息之外,还有请求和相应的头信息
  • FULL:除了HEADERS中定义的信息外,还有请求和响应的正文及元数据

同样可以使用配置类或者yml来配置,因为SpringBoot默认的日志级别是info,因此我们还需要配置日志级别。

feign:
  client:
    config:
      default: # OpenFeign默认配置
        connectTimeout: 1000  # 默认建立连接的超时时间
        readTimeout: 1000     # 默认方法调用超时时间
        loggerLevel: BASIC    # 日志打印级别
      CLOUD-PAYMENT-SERVICE:
        connectTimeout: 1000  # 默认建立连接的超时时间
        readTimeout: 5000     # 默认方法调用超时时间
        loggerLevel: BASIC
        
logging:
  level:
    root: info
    cc.seckill.springcloud.service.PaymentFeignService: debug

日志框架日志级别有(级别由低到高)

  • TRACE:最低级别的日志,一般不会使用
  • DEBUG:打印调试信息
  • INFO:粒度主要强调程序的运行过程中的一些信息,可以用于生产环境中输出程序运行的一些重要信息。
  • WARN:打印警告⚠️日志,表示潜在的可能出问题的地方
  • ERROR:出错啦,但是又没完全错,程序还能跑
  • FATAL:程序寄了,很高级别的日志了,发生这种日志的打印说明出现重大错误了,直接停止程序纠错吧。
  • OFF:最高等级的了,就是关闭所有日志。😄?

选择日志打印级别后,只会打印级别大于等于配置级别的日志,比如设置的级别是INFO,那么就不会打印DEBUG信息和TRACE信息。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值