3-SpringCloud-LoadBalancer-OpenFeign服务调用与负载均衡

1.3 服务负载均衡与调用

1.3.1 LoadBalancer 微服务组件

LB负载均衡(Load Balance)是什么

简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的 HA(高可用),常见的负载均衡有软件 Nginx,LVS,硬件 F5 等

spring-cloud-starter-loadbalancer 组件是什么

Spring Cloud LoadBalancer 是由 SpringCloud 官方提供的一个开源的、简单易用的客户端负载均衡器,它包含在 SpringCloud-common s中用它来替换了以前的 Ribbon 组件。相比较于Ribbon,SpringCloud LoadBalancer 不仅能够支持 RestTemplate,还支持WebClient(WeClient是Spring Web Flux中提供的功能,可以实现响应式异步请求)。

Spring Cloud LoadBalancer 官网地址:https://docs.spring.io/spring-cloud-commons/reference/4.2/spring-cloud-commons/loadbalancer.html,如下图所示。

客户端负载 VS 服务端负载

loadbalancer本地负载均衡客户端 VS Nginx服务端负载均衡区别

  • Nginx是服务器负载均衡,客户端所有请求都会交给nginx,然后由nginx实现转发请求,即负载均衡是由服务端实现的。
  • loadbalancer本地负载均衡,在调用微服务接口时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用技术。
1.3.1.1 负载均衡案例

架构说明:订单微服务80通过轮询负载均衡访问支付微服务8001/8002,架构说明图如下图所示。

LoadBalancer 在工作时分成两步:

第一步,先选择ConsulServer从服务端查询并拉取服务列表,知道了它有多个服务(上图3个服务),这3个实现是完全一样的,

默认轮询调用谁都可以正常执行。类似生活中求医挂号,某个科室今日出诊的全部医生,客户端你自己选一个。

第二步 ,按照指定的负载均衡策略从server取到的服务注册列表中由客户端自己选择一个地址,所以LoadBalancer是一个客户端的负载均衡器。

  1. 新建支付微服务8002,将8001微服务的内容拷贝给8002服务。

  2. Consul 持久化配置

    • 在安装的注册中心 Consul 文件目录内(存有 consul.exe文件)新建目录 mydata,本人在D:\opt\SpringCloud-Components\consul_1.21.4_windows_386\目录下新建文件夹 mydata。然后继续在该目录下新建文件consul_start.bat,并输入以下内容。最后保存退出。
    cmd 复制代码
    @echo.服务启动......  
    @echo off  
    @sc create Consul binpath= "D:\opt\SpringCloud-Components\consul_1.21.4_windows_386\consul.exe agent -server -ui -bind=127.0.0.1 -client=0.0.0.0 -bootstrap-expect  1  -data-dir D:\opt\SpringCloud-Components\consul_1.21.4_windows_386\mydata   "
    @net start Consul
    @sc config Consul start= AUTO  
    @echo.Consul start is OK......success
    @pause
    • 右击文件consul_start.bat以管理员身份运行,如出现下图信息表示运行成功。

      再检查以下任务管理器的详细信息是否出现 consul.exe 服务,若出现表示注册中心的持久化配置成功配置。

  3. 要在订单服务80的 pom.xml 文件和 controller 文件中添加如下信息。

    pom.xml

    xml 复制代码
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-loadbalancer</artifactId>
    </dependency>

    OrderController

    java 复制代码
    @GetMapping("/get/info")
        public String getInfoByLoadBalancer(){
            return restTemplate.getForObject(PAYMENTSRV_URL+"/pay/get/info",String.class);
        }
  4. 然后在注册中心的 k/V键值存储中配置目录和文件,并在文件中添加系统级别的配置信息。配置方式与<1.2.3.1>的第4步配置方式类似,此处不在详细赘述。

  5. 测试,启动80、8001和8002微服务。注册中心 Consul 的服务如下所示。

    首先通过网址http://localhost:8001/pay/get/infohttp://localhost:8002/pay/get/info测试两个支付微服务是否能够成功入驻注册中心 Consul 和本地负载均衡,若能够成功入驻和负载均衡,在测试网址http://localhost/consumer/pay/get/info能否入驻注册中心和负载均衡。若能够成功入驻和负载均衡,表示 LoadBalancer 测试案例成功。测试结果如下图类似信息表示服务搭建成功。

小结

编码使用 DiscoveryClient 动态获取所有的线上服务列表。具体信息可参考官网(网址:https://docs.spring.io/spring-cloud-consul/reference/discovery.html#using-the-discoveryclient)。

在订单微服务80的 controller 中添加如下代码。

java 复制代码
@Resource
private DiscoveryClient discoveryClient;
@GetMapping("/discovery")
public String discovery()
{
    List<String> services = discoveryClient.getServices();
    for (String element : services) {
        System.out.println(element);
    }
    System.out.println("===================================");
    List<ServiceInstance> instances = discoveryClient.getInstances("cloud-payment-service");
    for (ServiceInstance element : instances) {
        System.out.println(element.getServiceId()+"\t"+element.getHost()+"\t"+element.getPort()+"\t"+element.getUri());
    }
    return instances.get(0).getServiceId()+":"+instances.get(0).getPort();
}

然后访问网址http://localhost/consumer/pay/discovery后网页和控制台返回如下信息,表示成功获取了线上所有服务列表信息。

轮询负载均衡算法:rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标 ,每次服务重启动后rest接口计数从1开始。List instances = discoveryClient.getInstances("cloud-payment-service");

如: List [0] instances = 127.0.0.1:8002

List [1] instances = 127.0.0.1:8001

8001+ 8002 组合成为集群,它们共计2台机器,集群总数为2, 按照轮询算法原理:

当总请求数为1时: 1 % 2 =1 对应下标位置为1 ,则获得服务地址为127.0.0.1:8001

当总请求数位2时: 2 % 2 =0 对应下标位置为0 ,则获得服务地址为127.0.0.1:8002

当总请求数位3时: 3 % 2 =1 对应下标位置为1 ,则获得服务地址为127.0.0.1:8001

当总请求数位4时: 4 % 2 =0 对应下标位置为0 ,则获得服务地址为127.0.0.1:8002

如此类推...

1.3.1.2 负载均衡算法原理

负载均衡算法有两种,分别是轮询负载均衡算法原理和随机负载均衡算法原理,默认采用轮询负载均衡算法原理。详细信息请参考网址https://docs.spring.io/spring-cloud-commons/reference/4.2/spring-cloud-commons/loadbalancer.html#switching-between-the-load-balancing-algorithms

将默认的轮询负载均衡算法修改为随机的负载均衡算法,具体操作只需要修改配置文件 RestTemplateConfig.class 文件,具体信息如下所示。

java 复制代码
@Configuration
@LoadBalancerClient(
        //下面的value值大小写一定要和consul里面的名字一样,必须一样
        value = "cloud-payment-service",configuration = RestTemplateConfig.class)
public class RestTemplateConfig
{
    @Bean
    @LoadBalanced //使用@LoadBalanced注解赋予RestTemplate负载均衡的能力
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

    @Bean
    ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
                                                            LoadBalancerClientFactory loadBalancerClientFactory) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);

        return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
    }
}

然后测试网址http://localhost/consumer/pay/get/info

1.3.2 OpenFeign 服务接口调用

OpenFeign 服务接口调用主要包含两个方面的功能,分别是服,务调用和服务负载均衡。所以在使用服务负载均衡微服务组件时通常选择该组件。OpenFeign 的功能比 LoadBalacer 的功能更加强大,二者都属于客户端组件。OpenFeign 的具体理论信息可参考网址https://docs.spring.io/spring-cloud-openfeign/reference/spring-cloud-openfeign.html,源码信息可参考网址:https://github.com/spring-cloud/spring-cloud-openfeign/

1.3.2.1 OpenFeign 概述

Feign是一个声明性web服务客户端。它使编写web服务客户端变得更容易。使用Feign创建一个接口并对其进行注释。它具有可插入的注释支持,包括Feign注释和JAX-RS注释。Feign还支持可插拔编码器和解码器。Spring Cloud添加了对Spring MVC注释的支持,以及对使用Spring Web中默认使用的HttpMessageConverter的支持。Spring Cloud集成了Eureka、Spring Cloud CircuitBreaker以及Spring Cloud LoadBalancer,以便在使用Feign时提供负载平衡的http客户端。

总之,OpenFeign 是一个声明式的 Web 服务客户端,只需要创建一个 Rest 接口并在接口上添加注解@FeignClient即可。OpenFeign 基本是当前微服务之间调用的事实标准。

OpenFeign 服务调用的功能

OpenFeign能干什么

前面在使用SpringCloud LoadBalancer+RestTemplate时,利用RestTemplate对http请求的封装处理形成了一套模版化的调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。所以,OpenFeign在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义。在OpenFeign的实现下,我们只需创建一个接口并使用注解的方式来配置它(在一个微服务接口上面标注一个**@FeignClient**注解即可),即可完成对服务提供方的接口绑定,统一对外暴露可以被调用的接口方法,大大简化和降低了调用客户端的开发量,也即由服务提供者给出调用接口清单,消费者直接通过OpenFeign调用即可,O(∩_∩)O。

OpenFeign同时还集成SpringCloud LoadBalancer

可以在使用OpenFeign时提供Http客户端的负载均衡,也可以集成阿里巴巴Sentinel来提供熔断、降级等功能。而与SpringCloud LoadBalancer不同的是,通过OpenFeign只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用。

  • 可插拔的注解支持,包括 Feign 注解和 JAX-RS 注解。
  • 支持可插拔的 HTTP 解码器和编码器。
  • 支持 Sentinel 和它的 Fallback。
  • 支持 Spring Cloud LoadBalancer 负载均衡。
  • 支持 HTTP 的请求和响应压缩。

1.3.2 OpenFeign 项目实战

首先创建一个模块 cloud-consumer-feign-order80,某块结构如下图所示。

服务消费者80 → 调用含有@FeignClient注解的Api服务接口 → 服务提供者(8001/8002)
  1. 创建 Module 模块,名为 cloud-consumer-feign-order80。请按照下图信息进行配置。

  2. 改 pom.xml 文件

    因为这里是 OpenFeign 的实战,所以要引入依赖org.springframework.cloud.spring-cloud-starter-openfeign。具体信息可到官网查看。

    xml 复制代码
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>com.atguigu</groupId>
            <artifactId>cloud2024</artifactId>
            <version>1.0-SNAPSHOT</version>
        </parent>
    
        <artifactId>cloud-consumer-feign-order80</artifactId>
    
        <properties>
            <maven.compiler.source>17</maven.compiler.source>
            <maven.compiler.target>17</maven.compiler.target>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        </properties>
    
    
        <dependencies>
            <!--openfeign-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-openfeign</artifactId>
            </dependency>
            <!--SpringCloud consul discovery-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-consul-discovery</artifactId>
            </dependency>
            <!-- 引入自己定义的api通用包 -->
            <dependency>
                <groupId>com.atguigu</groupId>
                <artifactId>cloud-api-commons</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
            <!--web + actuator-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
            <!--lombok-->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <!--hutool-all-->
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
            </dependency>
            <!--fastjson2-->
            <dependency>
                <groupId>com.alibaba.fastjson2</groupId>
                <artifactId>fastjson2</artifactId>
            </dependency>
            <!-- swagger3 调用方式 http://你的主机IP地址:5555/swagger-ui/index.html -->
            <dependency>
                <groupId>org.springdoc</groupId>
                <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    </project>
  3. 写 application.yml 文件

    yml 复制代码
    server:
      port: 80
    
    spring:
      application:
        name: cloud-consumer-openfeign-order
      ####Spring Cloud Consul for Service Discovery
      cloud:
        consul:
          host: localhost
          port: 8500
          discovery:
            prefer-ip-address: true #优先使用服务ip进行注册
            service-name: ${spring.application.name}
  4. 主启动类

    更改主启动类名为 MainOpenFeign80,然后添加注解@SpringBootApplication,@EnableDiscoveryClient,@EnableFeignClients。具体代码如下所示。

    java 复制代码
    package com.atguigu;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.cloud.openfeign.EnableFeignClients;
    
    @SpringBootApplication
    @EnableFeignClients
    @EnableDiscoveryClient
    public class MainOpenFeign80 {
        public static void main(String[] args) {
            SpringApplication.run(MainOpenFeign80.class, args);
        }
    }
  5. 业务类

    • 订单模块要去调用支付模块,订单和支付两个微服务,需要通过Api接口解耦,一般不要在订单模块写非订单相关的业务,自己的业务自己做+其它模块走FeignApi接口调用。具体内容与下图信息类似。

      修改 cloud-api-commons 通用模块。

      • 改 pom.xml 文件。在文件中添加如下依赖。

        xml 复制代码
        <!--openfeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
      • 新建接口 com.atguigu.apis.PayFeignApi,并在接口上方添加注解@FeignClient,并指明该接口要绑定的微服务名称。具体代码如下所示。

        java 复制代码
        package com.atguigu.apis;
        
        import com.atguigu.entities.PayDTO;
        import com.atguigu.resp.ResultData;
        import org.springframework.cloud.openfeign.FeignClient;
        import org.springframework.web.bind.annotation.GetMapping;
        import org.springframework.web.bind.annotation.PathVariable;
        import org.springframework.web.bind.annotation.PostMapping;
        import org.springframework.web.bind.annotation.RequestBody;
        
        @FeignClient("cloud-payment-service")
        public interface PayFeignApi {
            @PostMapping("/pay/add")
            public ResultData addPay(@RequestBody PayDTO payDTO);
        
            @GetMapping("/pay/get/{id}")
            public ResultData getPayInfo(@PathVariable("id") String id);
        
            @GetMapping("/pay/get/info")
            public String mylb();
        }
    • 将订单微服务80的 controller 复制到模块 cloud-consumer-feign-order80 中,然后删除与接口 PayFeignApi 无关的方法。具体代码如下所示。

      java 复制代码
      package com.atguigu.controller;
      
      import com.atguigu.apis.PayFeignApi;
      import com.atguigu.entities.PayDTO;
      import com.atguigu.resp.ResultData;
      import com.atguigu.resp.ReturnCodeEnum;
      import jakarta.annotation.Resource;
      import lombok.extern.slf4j.Slf4j;
      import org.springframework.cloud.client.ServiceInstance;
      import org.springframework.cloud.client.discovery.DiscoveryClient;
      import org.springframework.web.bind.annotation.*;
      import org.springframework.web.client.RestTemplate;
      
      import java.util.List;
      
      @RestController
      @Slf4j
      @RequestMapping("/feign/pay")
      public class OrderController {
      
          @Resource
          private PayFeignApi payFeignApi;
      
          @PostMapping("/add")
          public ResultData addOrder(@RequestBody PayDTO payDTO) {
              System.out.println("第一步:模拟本地addOrder新增订单成功(省略sql操作),第二步:再开启addPay支付微服务远程调用");
              ResultData resultData = payFeignApi.addPay(payDTO);
              return resultData;
          }
      
          @GetMapping("/get/{id}")
          public ResultData getPayInfo(@PathVariable("id") String id) {
              System.out.println("-------支付微服务远程调用,按照id查询订单支付流水信息");
              ResultData resultData = payFeignApi.getPayInfo(id);
              return resultData;
          }
      
          @GetMapping("/get/info")
          public String mylb(){
              return payFeignApi.mylb();
          }
      }
    • 测试

      先启动注册中心 Consul,再启动支付微服务8001,再启动 cloud-consumer-feign-order80,然后使用网址http://localhost/feign/pay/add,http://localhost/feign/pay/get/1进行测试插入功能和查询功能。插入功能需要传入的 json 输入如下所示。

      json 复制代码
      {
          "payNo":1213,
          "orderNo":1213,
          "userId":7,
          "account":6.66
      }

      然后启动微服务8002,然后输入网址http://localhost/feign/pay/get/info测试 OpenFeign 的负载均衡功能。

1.3.3 OpenFeign 的高级特新

1.3.3.1 OpenFeign 的超时控制

由于大部分公司使用 OpenFeign 配置服务调用,一些简单的业务使用OpenFeign 的默认配置没什么问题,但是一些复杂的业务使用默认的配置可能会产生 runtimeout 异常,这时候就需要定制化的配置。具体信息请参考官网https://docs.spring.io/spring-cloud-openfeign/reference/spring-cloud-openfeign.html#timeout-handling

在Spring Cloud微服务架构中,大部分公司都是利用OpenFeign进行服务间的调用,而比较简单的业务使用默认配置是不会有多大问题的,但是如果是业务比较复杂,服务要进行比较繁杂的业务计算,那后台很有可能会出现Read Timeout这个异常,因此定制化配置超时时间就有必要了。

为了测试超时控制这一特性,故意设置超时 BUG ,具体工作如下。

  1. 在支付微服务8001中故意设置超时62s ,具体代码如下所示。

    java 复制代码
     @GetMapping("/get/{id}")
        public ResultData<Pay> getPay(@PathVariable("id") Integer id) {
            if(id==-4) throw new RuntimeException("id不能为负数");
            try {
                TimeUnit.SECONDS.sleep(62);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            return ResultData.success(payService.getById(id));
        }
  2. 在 cloud-consumer-feign-order80 服务中计算程序从开始到报 timeout exception 错误的时间。具体代码如下所示。

    java 复制代码
     @GetMapping("/get/{id}")
        public ResultData<Pay> getPay(@PathVariable("id") Integer id) {
            if(id==-4) throw new RuntimeException("id不能为负数");
            try {
                TimeUnit.SECONDS.sleep(62);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            return ResultData.success(payService.getById(id));
        }
  3. 测试网址http://localhost/feign/pay/get/1,在控制台可看到错误信息如下所示。

默认OpenFeign客户端等待60秒钟,但是服务端处理超过规定时间会导致Feign客户端返回报错。为了避免这样的情况,有时候我们需要设置Feign客户端的超时控制,默认60秒太长或者业务时间太短都不好。这是后我们需要在yml文件中开启配置connectTimeout(连接超时时间)、readTimeout(请求处理超时时间)。具体信息可参考网址https://docs.spring.io/spring-cloud-openfeign/reference/spring-cloud-openfeign.html#spring-cloud-feign-overriding-defaults

修改 cloud-consumer-feign-order80 的 application.yml 文件配置,设置超时设置的方式有两种,分别是全局配置和指定配置。

  1. 全部配置,指服务调用对所有的服务的超时设置都一样。具体操作如下所示。

    • 在 application.yml 配置文件中添加如下配置。

      yml 复制代码
      spring:
        cloud:
          openfeign:
            client:
              config:
                default:
                  #连接超时时间
                           connectTimeout: 3000
                  #读取超时时间
                           readTimeout: 3000
    • 测试,测试网址为http://localhost/feign/pay/get/1,错误信息一致,只是等待时间变化了从默认的60s 变成了3s。

  2. 指定配置,指精细化设置,服务调用不同的服务可以设置不同的超时设置,具体操作如下所示。

    • 在 application.yml 配置文件中添加如下配置。

      yml 复制代码
      spring:
        cloud:
          openfeign:
            client:
              config:
                cloud-payment-service:
                  #连接超时时间
                            connectTimeout: 5000
                  #读取超时时间
                            readTimeout: 5000
    • 测试,测试网址为http://localhost/feign/pay/get/1,错误信息一致,只是等待时间变化了从默认的60s 变成了5s。

1.3.3.2 OpenFeign 的重试机制

OpenFeign 的重试机制默认关闭,默认情况下会自动创建Retryer.NERVER_RETRY 类型为 Retryer 的 Bean,这会禁用重试机制,若想开启充实机制需要新增配置类 FeignConfig 并修改 Retryer 配置。具体操作如下所示。

java 复制代码
package com.atguigu.config;

import feign.Retryer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FeignConfig {
    @Bean
    public Retryer myRetryer() {
//        return Retryer.NEVER_RETRY;
        return new Retryer.Default(100,1,3);
    }
            
}

重启 cloud-consumer-feign-order80 服务,测试网址http://localhost/feign/pay/get/1,错误信息一致,只是等待时间变化了从不重试的5s 变成了重试3次 的15s。控制台显示结果如下图所示。

1.3.3.3 OpenFeign 默认 HttpClient 的修改

修改 OpenFeign 默认的 HTTPClient 为 Apache 的 HttpClient5,因为 OpenFeign 使用 JDK 自带的 HttpClient 效果不好,而 Apache 提过的 HttpClient 性能更好。具体信息可参考官网网址https://docs.spring.io/spring-cloud-openfeign/reference/spring-cloud-openfeign.html#spring-cloud-feign-overriding-defaults

OpenFeign 中 httpclient 如果不做特殊配置,OpenFeign默认使用 JDK 自带的 HttpURLConnection 发送 HTTP 请求,由于默认HttpURLConnection 没有连接池、性能和效率比较低,如果采用默认,性能上不是最牛B的,所以加到最大。

修改微服务 cloud-consumer-feign-order80 的设置,使其 OpenFeign 默认的 HttpClient 修改为 Apache的 HttpClient。是微服务性能更好。具体操作如下所示。

  1. 首先,将 OpenFeign 的重试机制修改为默认的关闭状态,避免多次重试而等待更多时间。

    java 复制代码
    package com.atguigu.config;
    
    import feign.Retryer;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class FeignConfig {
        @Bean
        public Retryer myRetryer() {
            return Retryer.NEVER_RETRY;
    //        return new Retryer.Default(100,1,3);
        }
    
    }
  2. 修改 pom.xml 文件按,引入 Apache HttpClient 的相关依赖。

    xml 复制代码
    <!-- httpclient5-->
    <dependency>
        <groupId>org.apache.httpcomponents.client5</groupId>
        <artifactId>httpclient5</artifactId>
        <version>5.3</version>
    </dependency>
    <!-- feign-hc5-->
    <dependency>
        <groupId>io.github.openfeign</groupId>
        <artifactId>feign-hc5</artifactId>
        <version>13.1</version>
    </dependency>
  3. 更改 application.yml 文件,添加如下配置。

    yml 复制代码
    #  Apache HttpClient5 配置开启
    spring:
      cloud:
        openfeign:
          httpclient:
            hc5:
              enabled: true
  4. 测试网址http://localhost/feign/pay/get/1,错误信息如下所示。

    • 替换 HttpClient 之前

    • 替换 HttpClient 之后

1.3.3.4 OpenFeign 的请求与响应压缩

对请求和响应进行 GZIP 压缩

Spring Cloud OpenFeign 支持对请求和响应进行GZIP压缩,以减少通信过程中的性能损耗。通过下面的两个参数设置,就能开启请求与相应的压缩功能:

properties 复制代码
spring.cloud.openfeign.compression.request.enabled=true
spring.cloud.openfeign.compression.response.enabled=true

细粒度化设置

对请求压缩做一些更细致的设置,比如下面的配置内容指定压缩的请求数据类型并设置了请求压缩的大小下限,只有超过这个大小的请求才会进行压缩:

properties 复制代码
spring.cloud.openfeign.compression.request.enabled=true
spring.cloud.openfeign.compression.request.mime-types=text/xml,application/xml,application/json #触发压缩数据类型
spring.cloud.openfeign.compression.request.min-request-size=2048 #最小触发压缩的大小

具体信息可参考官网网址https://docs.spring.io/spring-cloud-openfeign/reference/spring-cloud-openfeign.html#feign-request-response-compression

配置 application.yml 文件,在该文件中配置如下信息。

yml 复制代码
spring:
	cloud:
		openfeign:
			compression:
        		request:
          			enabled: true
          			min-request-size: 2048 #最小触发压缩的大小
          			mime-types: text/xml,application/xml,application/json #触发压缩数据类型
        		response:
          			enabled: true

测试与日志打印功能一起测试,此处不测试。

1.3.3.5 OpenFeign 的日志打印功能

Feign 提供了日志打印功能,我们可以通过配置来调整日志级别,从而了解 Feign 中 Http 请求的细节,说白了就是对Feign接口的调用情况进行监控和输出。

日志级别

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

配置日志打印功能的主要操作如下所示。

  1. 配置日志 Bean。

    java 复制代码
    package com.atguigu.config;
    
    import feign.Logger;
    import feign.Retryer;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class FeignConfig {
        @Bean
        public Retryer myRetryer() {
            return Retryer.NEVER_RETRY;
    //        return new Retryer.Default(100,1,3);
        }
        @Bean
        public Logger.Level feignLoggerLevel() {
            return Logger.Level.FULL;
        }
    
    }
  2. 在 application.yml 文件中开启日志的 Feign 客户端。主要添加如下配置。公式(三段):logging.level + 含有@FeignClient注解的完整带包名的接口名+debug。

    yml 复制代码
    # feign日志以什么级别监控哪个接口
    logging:
      level:
        com:
          atguigu:
             apis:
               PayFeignApi: debug 
  3. 测试网址http://localhost/feign/pay/get/1,错误信息如下所示。若出现 gzip 表明请求被压缩,否则就是未被压缩。具体结果如下图所示。

    • 带着压缩调用
    • 去掉压缩调用

      将 yml 配置文件的配置修改为如下图所示的信息。将值原先为 true 的修改为 false。

      yml 复制代码
            compression:
              request:
                enabled: false
                min-request-size: 2048 #最小触发压缩的大小
                mime-types: text/xml,application/xml,application/json #触发压缩数据类型
              response:
                enabled: false

      再次测试,控制台信息如下所示。

  4. 补充测试1.3.3.4

    • 调整 FeignConfig 配置的重试策略为3次,代码如下所示。

      java 复制代码
      package com.atguigu.config;
      
      import feign.Logger;
      import feign.Retryer;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      
      @Configuration
      public class FeignConfig {
          @Bean
          public Retryer myRetryer() {
      //        return Retryer.NEVER_RETRY;
              return new Retryer.Default(100,1,3);
          }
          @Bean
          public Logger.Level feignLoggerLevel() {
              return Logger.Level.FULL;
          }
      
      }
    • 修改 yml 文件的配置,将等待时间设置为2s,具体操作如下所示。

      yml 复制代码
      spring:
        application:
          name: cloud-consumer-openfeign-order
        ####Spring Cloud Consul for Service Discovery
        cloud:
          consul:
            host: localhost
            port: 8500
            discovery:
              prefer-ip-address: true #优先使用服务ip进行注册
              service-name: ${spring.application.name}
          openfeign:
            client:
              config:
                default:
                  connect-timeout: 2000
                  read-timeout: 2000
      #  Apache HttpClient5 配置开启
            httpclient:
              hc5:
                enabled: true
            compression:
              request:
                enabled: true
                min-request-size: 2048 #最小触发压缩的大小
                mime-types: text/xml,application/xml,application/json #触发压缩数据类型
              response:
                enabled: true
    • 测试网址http://localhost/feign/pay/get/1,日志部分信息如下所示。共打印日志3次。

      txt 复制代码
      -------支付微服务远程调用,按照id查询订单支付流水信息
      ---调用开始:2025-09-17 22:17:47
      2025-09-17T22:17:47.534+08:00 DEBUG 15560 --- [cloud-consumer-openfeign-order] [p-nio-80-exec-2] com.atguigu.apis.PayFeignApi             : [PayFeignApi#getPayInfo] ---> GET http://cloud-payment-service/pay/get/1 HTTP/1.1
      2025-09-17T22:17:47.535+08:00 DEBUG 15560 --- [cloud-consumer-openfeign-order] [p-nio-80-exec-2] com.atguigu.apis.PayFeignApi             : [PayFeignApi#getPayInfo] Accept-Encoding: gzip
      2025-09-17T22:17:47.535+08:00 DEBUG 15560 --- [cloud-consumer-openfeign-order] [p-nio-80-exec-2] com.atguigu.apis.PayFeignApi             : [PayFeignApi#getPayInfo] Accept-Encoding: deflate
      2025-09-17T22:17:47.535+08:00 DEBUG 15560 --- [cloud-consumer-openfeign-order] [p-nio-80-exec-2] com.atguigu.apis.PayFeignApi             : [PayFeignApi#getPayInfo] ---> END HTTP (0-byte body)
      2025-09-17T22:17:49.730+08:00 DEBUG 15560 --- [cloud-consumer-openfeign-order] [p-nio-80-exec-2] com.atguigu.apis.PayFeignApi             : [PayFeignApi#getPayInfo] <--- ERROR SocketTimeoutException: Read timed out (2195ms)
      2025-09-17T22:17:49.732+08:00 DEBUG 15560 --- [cloud-consumer-openfeign-order] [p-nio-80-exec-2] com.atguigu.apis.PayFeignApi             : [PayFeignApi#getPayInfo] java.net.SocketTimeoutException: Read timed out
      	at java.base/sun.nio.ch.NioSocketImpl.timedRead(NioSocketImpl.java:288)
      
      2025-09-17T22:17:49.732+08:00 DEBUG 15560 --- [cloud-consumer-openfeign-order] [p-nio-80-exec-2] com.atguigu.apis.PayFeignApi             : [PayFeignApi#getPayInfo] <--- END ERROR
      2025-09-17T22:17:49.737+08:00 DEBUG 15560 --- [cloud-consumer-openfeign-order] [p-nio-80-exec-2] com.atguigu.apis.PayFeignApi             : [PayFeignApi#getPayInfo] ---> RETRYING
      2025-09-17T22:17:49.737+08:00 DEBUG 15560 --- [cloud-consumer-openfeign-order] [p-nio-80-exec-2] com.atguigu.apis.PayFeignApi             : [PayFeignApi#getPayInfo] ---> GET http://cloud-payment-service/pay/get/1 HTTP/1.1
      2025-09-17T22:17:49.738+08:00 DEBUG 15560 --- [cloud-consumer-openfeign-order] [p-nio-80-exec-2] com.atguigu.apis.PayFeignApi             : [PayFeignApi#getPayInfo] Accept-Encoding: gzip
      2025-09-17T22:17:49.738+08:00 DEBUG 15560 --- [cloud-consumer-openfeign-order] [p-nio-80-exec-2] com.atguigu.apis.PayFeignApi             : [PayFeignApi#getPayInfo] Accept-Encoding: deflate
      2025-09-17T22:17:49.738+08:00 DEBUG 15560 --- [cloud-consumer-openfeign-order] [p-nio-80-exec-2] com.atguigu.apis.PayFeignApi             : [PayFeignApi#getPayInfo] ---> END HTTP (0-byte body)
      2025-09-17T22:17:51.756+08:00 DEBUG 15560 --- [cloud-consumer-openfeign-order] [p-nio-80-exec-2] com.atguigu.apis.PayFeignApi             : [PayFeignApi#getPayInfo] <--- ERROR SocketTimeoutException: Read timed out (2017ms)
      2025-09-17T22:17:51.757+08:00 DEBUG 15560 --- [cloud-consumer-openfeign-order] [p-nio-80-exec-2] com.atguigu.apis.PayFeignApi             : [PayFeignApi#getPayInfo] java.net.SocketTimeoutException: Read timed out
      	at java.base/sun.nio.ch.NioSocketImpl.timedRead(NioSocketImpl.java:288)
      	
      2025-09-17T22:17:51.757+08:00 DEBUG 15560 --- [cloud-consumer-openfeign-order] [p-nio-80-exec-2] com.atguigu.apis.PayFeignApi             : [PayFeignApi#getPayInfo] <--- END ERROR
      2025-09-17T22:17:51.759+08:00 DEBUG 15560 --- [cloud-consumer-openfeign-order] [p-nio-80-exec-2] com.atguigu.apis.PayFeignApi             : [PayFeignApi#getPayInfo] ---> RETRYING
      2025-09-17T22:17:51.759+08:00 DEBUG 15560 --- [cloud-consumer-openfeign-order] [p-nio-80-exec-2] com.atguigu.apis.PayFeignApi             : [PayFeignApi#getPayInfo] ---> GET http://cloud-payment-service/pay/get/1 HTTP/1.1
      2025-09-17T22:17:51.759+08:00 DEBUG 15560 --- [cloud-consumer-openfeign-order] [p-nio-80-exec-2] com.atguigu.apis.PayFeignApi             : [PayFeignApi#getPayInfo] Accept-Encoding: gzip
      2025-09-17T22:17:51.759+08:00 DEBUG 15560 --- [cloud-consumer-openfeign-order] [p-nio-80-exec-2] com.atguigu.apis.PayFeignApi             : [PayFeignApi#getPayInfo] Accept-Encoding: deflate
      2025-09-17T22:17:51.759+08:00 DEBUG 15560 --- [cloud-consumer-openfeign-order] [p-nio-80-exec-2] com.atguigu.apis.PayFeignApi             : [PayFeignApi#getPayInfo] ---> END HTTP (0-byte body)
      2025-09-17T22:17:53.774+08:00 DEBUG 15560 --- [cloud-consumer-openfeign-order] [p-nio-80-exec-2] com.atguigu.apis.PayFeignApi             : [PayFeignApi#getPayInfo] <--- ERROR SocketTimeoutException: Read timed out (2014ms)
      2025-09-17T22:17:53.776+08:00 DEBUG 15560 --- [cloud-consumer-openfeign-order] [p-nio-80-exec-2] com.atguigu.apis.PayFeignApi             : [PayFeignApi#getPayInfo] java.net.SocketTimeoutException: Read timed out
      	at java.base/sun.nio.ch.NioSocketImpl.timedRead(NioSocketImpl.java:288)
      
      2025-09-17T22:17:53.777+08:00 DEBUG 15560 --- [cloud-consumer-openfeign-order] [p-nio-80-exec-2] com.atguigu.apis.PayFeignApi             : [PayFeignApi#getPayInfo] <--- END ERROR
      ---调用结束:2025-09-17 22:17:53

**补充:**OpenFeign 集成 Sentinel 可实现 fallback 的服务降级,具体操作见后续章节 Spring Cloud Alibaba。

上一页 2-SpringCloud-Consul服务注册与发现和分布式配置管理

相关推荐
好好研究20 小时前
Spring框架 - 开发方式
java·后端·spring
编啊编程啊程21 小时前
【029】智能停车计费系统
java·数据库·spring boot·spring·spring cloud·kafka
java1234_小锋1 天前
Spring事件监听的核心机制是什么?
java·spring·面试
半梦半醒*1 天前
k8s——pod详解2
linux·运维·docker·容器·kubernetes·负载均衡
CodeBlossom1 天前
Spring Cache快速入门
java·数据库·spring
⑩-1 天前
如何保证Redis和Mysql数据缓存一致性?
java·数据库·redis·mysql·spring·缓存·java-ee
我要去腾讯1 天前
Springcloud核心组件之Sentinel详解
java·spring cloud·sentinel
虎子_layor1 天前
实现异步最常用的方式@Async,快速上手
后端·spring
不光头强1 天前
Spring整合单元测试
spring·单元测试·log4j
没有bug.的程序员1 天前
Eureka 注册中心原理与服务注册发现机制
java·spring·云原生·eureka·架构·注册中心·服务注册发现