一、RestTemplate 服务接口调用

方法中Response参数自动获取 获取responseentity数据_spring cloud

  1. getForEntity():返回对象为 响应体中数据 转化成的对象,基本上可以理解为 Json。
  • getForObject():返回对象为 ResponseEntity 对象,包含了响应中的一些重要信息。比如:响应头、响应状态码、响应体 等。
  • postForEntity():需要 getBody() 获取 Body 数据。
  • postForObject():直接返回 Body 数据。
  • @LoadBalanced:注解赋予 RestTemplate 负载均衡的能力。

二、Ribbon 服务负载均衡

1. 是什么

  • Netflix/ribbon Github
  • Spring Cloud Ribbon 是基于 Netflix Ribbon 实现的一套 客户端负载均衡 的工具。
  1. 简单的说:Ribbon 是 Netflix 发布的开源项目,主要功能是提供 客户端的软件负载均衡算法 和 服务调用
  • Ribbon 客户端组件提供一系列完善的配置项,如:连接超时、重试等。
  1. 简单的说:就是在配置文件中列出 Load Balancer(负载均衡 简称 LB) 后面所有的机器,Ribbon 会自动的帮助你基于 某种规则(如:简单轮询、随机连接 等) 去连接这些机器。
  2. 我们很容易使用 Ribbon 实现 自定义的负载均衡算法

1.1 Ribbon 架构说明
  • Ribbon 在工作时分成两步:
  1. 第一步:先选择 Server,它优先选择在同一个区域内 负载较少 的 Server
  2. 第二步:根据用户指定的策略,再从 Eureka Server 取到的 服务注册列表 中选择一个地址。
  • Ribbon 提供了多种策略:轮询、随机 和 根据响应时间加权 等。
  • 总结:Ribbon 其实就是一个 软负载均衡 的 客户端组件。
  1. 他可以和 其他所需请求的客户端 结合使用。
  2. 和 Eureka 结合只是其中的一个实例。

方法中Response参数自动获取 获取responseentity数据_resttemplate_02


1.2 Eureka Client 已集成 Ribbon
<!--eureka-client-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--eureka-client 已集成 ribbon-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

2. 能干嘛

2.1 负载均衡是什么
  • 简单的说:就是将 用户的请求平摊分配到多个服务上,从而达到系统的 高可用(HA)
  • 负载均衡解决方法:
  1. 集中式负载均衡:
    即在服务的 消费方 和 提供方 之间使用 独立的 LB 设施(可以是 硬件如 F5,也可以是 软件如 Nginx),由该软件负责把访问请求通过 某种策略 转发至服务的提供方。
  2. 进程内负载均衡:
    将 LB 逻辑集成到 消费方,消费方 从 服务注册中心 获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。
  • 常见的 负载均衡软件 有:软件 Nginx、LVS,硬件 F5 等。

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

3. Ribbon 核心组件 IRule

  • IRule:根据 特定算法 从 服务列表 中选取一个要访问的服务。

方法中Response参数自动获取 获取responseentity数据_resttemplate_03


3.1 轮询规则
  • com.netflix.loadbalancer.RoundRobinRule

3.2 随机规则
  • com.netflix.loadbalancer.RandomRule

3.3 重试规则
  • com.netflix.loadbalancer.RetryRule
  • 先按照 轮询策略 获取服务,如果 获取服务失败,则在 指定时间 内会进行重试,获取可用的服务。

3.4 加权响应时间规则
  • WeightedResponseTimeRule
  • 轮询规则 扩展,响应速度越快 的实例选择 权重越大,越容易被选择。

3.5 最佳可用规则
  • BestAvailableRule
  • 会先过滤掉由于多次访问故障而 处于断路器跳闸状态 的服务,然后选择一个 并发量最小 的服务。

3.6 可用性筛选规则
  • AvailabilityFilteringRule
  • 先过滤掉 故障 实例,再选择 并发较小 的实例。

3.7 分区无效规则【默认规则】
  • ZoneAvoidanceRule
  • 复合判断 Server 所在区域的性能 和 Server 的可用性选择服务器。

4. 自定义随机策略

  • cloud-consumer-order-eureka80

4.1 修改 RestTemplate
  • RestTemplate 加注解 @LoadBalanced
@Configuration
public class ApplicationContextBean {

    // 注解 @Bean 等于 applicationContext.xml > <bean id="" class="" />
    @Bean
    /*
    1. 使用 @LoadBalanced 注解,赋予 RestTemplate 负载均衡的能力。
    2. 不使用 @LoadBalanced 注解,自定义负载均衡策略。
    */
	@LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

4.2 新建规则类 com.qs.rule.MySelfRule
  • 官方文档明确给出了警告:
  1. 这个自定义配置类不能放在 @ComponentScan 注解 所扫描的 当前包下 及 子包下。
  2. 否则我们自定义的这个配置类就会被所有的 Ribbon 客户端所共享,达不到特殊化定制的目的了。
@Configuration
public class MySelfRule {

    @Bean
    public IRule myRule() {
        // 修改随机策略
        return new RandomRule();
    }
}

4.3 主启动
  • 加注解 @RibbonClient
@SpringBootApplication
// 启用 EurekaClient
@EnableEurekaClient
// 在启动该微服务的时候,就能去加载我们的自定义 Ribbon 随机规则类,从而使配置生效
@RibbonClient(name = "PROVIDER-PAYMENT-SERVICE", configuration = MySelfRule.class)
public class OrderMain80 {

    public static void main(String[] args) {
        SpringApplication.run(OrderMain80.class, args);
    }
}

4.4 测试
  • http://localhost/order/get/1

5. 自定义轮询策略

  • cloud-consumer-order-eureka80
  • 每次服务重启动后 Rest 接口计数从 1 开始。
  • Rest 接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标
List<ServiceInstance> instances = 
	discoveryClient.getInstances("PAYMENT-SERVICE-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
以此类推 ...
*/

5.1 修改 RestTemplate
  • RestTemplate 去掉注解 @LoadBalanced
@Configuration
public class ApplicationContextBean {

    // 注解 @Bean 等于 applicationContext.xml > <bean id="" class="" />
    @Bean
    /*
    1. 使用 @LoadBalanced 注解,赋予 RestTemplate 负载均衡的能力。
    2. 不使用 @LoadBalanced 注解,自定义负载均衡策略。
    */
	/*@LoadBalanced*/
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

5.2 新建接口 com.qs.springcloud.lb.LoadBalancer
public interface LoadBalancer {

    ServiceInstance instances(List<ServiceInstance> serviceInstances);
    
}

5.3 新建类 com.qs.springcloud.lb.MyLoadBalancer
@Component
@Slf4j
public class MyLoadBalancer implements LoadBalancer {

    private AtomicInteger atomicInteger = new AtomicInteger(0);

    public final int getAndIncrement() {
        int current, next;
        do {
            current = atomicInteger.get();
            next = current >= 2147483647 ? 0 : current + 1;
            // 自旋锁
        } while (!atomicInteger.compareAndSet(current, next));
        log.info("next:" + next);
        return next;
    }

    /**
     * 负载均衡算法:
     * 1. 每次服务重启动后 Rest 接口计数从 1 开始。
     * 2. Rest 接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标
     */
    @Override
    public ServiceInstance instances(List<ServiceInstance> serviceInstances) {
        int index = getAndIncrement() % serviceInstances.size();
        return serviceInstances.get(index);
    }
}

5.4 主启动
@SpringBootApplication
// 启用 EurekaClient
@EnableEurekaClient
public class OrderMain80 {

    public static void main(String[] args) {
        SpringApplication.run(OrderMain80.class, args);
    }
    
}

5.5 测试

http://localhost/order/lb


三、OpenFeign 服务接口调用

1. 是什么

  1. Feign 是一个声明式 WebService 客户端,使用 Feign 能让编写 WebService 客户端更加简单。
  2. 使用方法是 定义一个 服务接口 然后在上面 添加注解,Feign 也支持 可拔插式的 编码器 和 解码器
  3. Spring Cloud 对 Feign 进行了封装,使其支持了 Spring MVC 标准注解 和 HttpMessageConverters
  4. Feign 可以与 Eureka 和 Ribbon 组合使用以支持 负载均衡

2. 能干嘛

  • Feign 旨在使编写 Java Http 客户端变的更容易。
  • 前面在使用 Ribbon + RestTemplate 时,利用 RestTemplate 对 Http 请求的封装处理,形成了一套模版化的调用方法。
  • 但是在实际开发中,由于对服务依赖的调用可能不止一处
    往往一个接口会被多处调用,所以通常都会针对每个微服务,自行封装一些客户端类 来包装这些 依赖服务的调用。
  • Feign 在此基础上做了进一步封装
    由他来帮助我们 定义和实现依赖服务接口的定义。
  • 在 Feign 的实现下,我们只需 创建一个接口 并 使用注解的方式来配置它
    以前是 Dao 接口上标注 @Mapper 注解,现在是一个 微服务接口 上面标注一个 Feign 注解即可。
    即可完成对服务提供方的接口绑定,简化了使用 Spring Cloud Ribbon 时,自动封装服务调用客户端的开发量。

3. Feign 集成了 Ribbon

  • 用 Ribbon 维护了 Payment 的服务列表信息,并通过 轮询 实现了 客户端的负载均衡
  • 与 Ribbon 不同的是,通过 Feign 只需要定义 服务绑定接口,且以声明式的方法,优雅而简单的实现了服务调用。

4. Feign 和 OpenFeign 区别

4.1 Feign
  1. Feign 是 Spring Cloud 组件中的一个轻量级 RestFul 的 Http 服务客户端。
  2. Feign 内置了 Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务。
  3. Feign 的使用方式是:使用 Feign 的注解定义接口,调用这个接口,就可以调用服务注册中心的服务。
<dependency>
	<groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
</dependency>

4.2 OpenFeign
  1. OpenFeign 是 Spring Cloud 在 Feign 的基础上支持了 SpringMVC 的注解,如:@RequesMapping 等。
  2. OpenFeign 的 @FeignClient 可以解析 SpringMVC 的 @RequestMapping 注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

5. OpenFeign 使用步骤

5.1 新建 Module cloud-consumer-order-feign80

5.2 POM
<?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">
    <parent>
        <artifactId>cloud-2020</artifactId>
        <groupId>com.qs.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-consumer-order80-feign</artifactId>

    <dependencies>
        <!--openfeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--eureka-client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <!--web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--actuator-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!--自定义API通用包-->
        <dependency>
            <groupId>com.qs.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!--热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

5.3 YML
server:
  port: 80

eureka:
  client:
    # false 表示是否将自己注册进 EurekaServer,默认为true
    register-with-eureka: false
    service-url:
      defaultZone: http://localhost:7001/eureka

5.4 主启动
@SpringBootApplication
// 开启使用 @FeignClient注解
@EnableFeignClients
public class OrderFeignMain80 {

    public static void main(String[] args) {
        SpringApplication.run(OrderFeignMain80.class, args);
    }
}

5.5 新建接口 PaymentFeignService
@Component
@FeignClient("PROVIDER-PAYMENT-SERVICE")
public interface PaymentFeignService {

    @GetMapping("/payment/get/{id}")
    CommonResult get(@PathVariable("id") Long id);

    @GetMapping(value = "/payment/feignTimeOut")
    String feignTimeOut();
}

5.6 新建类 OrderFeignController
@RestController
@RequestMapping("order")
@Slf4j
public class OrderFeignController {

    @Resource
    private PaymentFeignService paymentFeignService;

    @GetMapping("get/{id}")
    public CommonResult get(@PathVariable("id") Long id) {
        CommonResult commonResult = paymentFeignService.get(id);
        log.info("commonResult = " + commonResult);
        return commonResult;
    }

    @GetMapping("feignTimeOut")
    public String feignTimeOut() {
        return paymentFeignService.feignTimeOut();
    }
}

5.7 测试
  • http://localhost/order/get/1

5.8 修改 PaymentController
  • 线程暂停 3 秒钟(Feign 默认超时时间 1 秒)。
@RestController
@RequestMapping("payment")
@Slf4j
public class PaymentController {
	/**
     * 测试 Feign连接超时
     * @return
     */
    @GetMapping("feignTimeOut")
    public String feignTimeOut() {
        // 线程暂停 3 秒钟(Feign 默认超时时间 1 秒)
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("serverPort = " + serverPort);
        return serverPort;
    }
}

5.9 超时测试
  • http://localhost/order/feignTimeOut

6. OpenFeign 超时控制

  • OpenFeign 默认等待1 秒钟,超过后报错。
    默认 Feign 客户端只等待 1 秒钟,但是服务端处理需要超过 1 秒钟,导致 Feign 客户端不想等待了,直接返回报错。
  • 为了避免这样的情况,有时候我们需要设置 Feign 客户端的超时控制。
## OpenFeign 超时控制
## 设置 Feign客户端超时时间(OpenFeign默认支持 Ribbon)
ribbon:
  # 指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
  ReadTimeout: 5000
  # 指的是建立连接后从服务器读取到可用资源所用的时间
  ConnectTimeout: 5000

7. OpenFeign 日志打印功能

  • Feign 提供了日志打印功能,我们可以通过配置来调整日志级别,从而了解 Feign 中 Http 请求的细节。
  • 说白了就是 对 Feign 接口的 调用情况进行 监控 和 输出
  • 日志级别:
  1. NONE:默认的,不显示任何日志。
  2. BASIC:仅记录 请求方法、URL、响应状态码 及 执行时间。
  3. HEADERS:除了 BASIC 中定义的信息之外,还有请求和响应的头信息。
  4. FULL:除了 HEADERS 中定义的信息之外,还有请求和响应的正文及元数据。

7.1 新建 FeignConfig
@Configuration
public class FeignConfig {

    /**
     * OpenFeign 日志打印功能
     * @return
     */
    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }
}

7.2 YML
## OpenFeign 日志打印功能
logging:
  level:
    # Feign 日志以什么级别监控哪个接口
    com.qs.springcloud.service.PaymentFeignService: debug

7.3 测试
  • http://localhost/order/get/1
  • 方法中Response参数自动获取 获取responseentity数据_负载均衡_04