学习一下 SpringCloud (四)-- 服务降级、熔断 Hystrix、Sentinel

肝了一个半月的 Java 项目快速开发脚手架:Chewing

(1) 相关博文地址:

学习一下 SpringCloud (一)-- 从单体架构到微服务架构、代码拆分(maven 聚合): https://www.cnblogs.com/l-y-h/p/14105682.html
学习一下 SpringCloud (二)-- 服务注册中心 Eureka、Zookeeper、Consul、Nacos :https://www.cnblogs.com/l-y-h/p/14193443.html
学习一下 SpringCloud (三)-- 服务调用、负载均衡 Ribbon、OpenFeign : https://www.cnblogs.com/l-y-h/p/14238203.html

 

(2)代码地址:

https://github.com/lyh-man/SpringCloudDemo

 

一、引入 服务降级、熔断

1、问题 与 解决

【问题:】
    通过前面几篇博客介绍,完成了基本项目创建、服务注册中心、服务调用 以及 负载均衡(也即 各个模块 已经能正常通信、共同对外提供服务了)。
    
    对于一个复杂的分布式系统来说,可能存在数十个模块,且模块之间可能会相互调用(嵌套),
    这就带来了一个问题:
        如果某个核心模块突然宕机(或者不能提供服务了),那么所有调用该 核心模块服务 的模块 将会出现问题,
        类似于 病毒感染,一个模块出现问题,将逐步感染其他模块出现问题,最终导致系统崩溃(也即服务雪崩)。

【服务雪崩:】
    服务雪崩 指的是 服务提供者 不可用(不能提供服务) 而导致 服务消费者不可用,并逐级放大的过程。
    比如:
        多个微服务之间形成链式调用,A、B 调用 C,C 调用 D,D 调用其他服务等。。。
        如果 D 因某种原因(宕机、网络延迟等) 不能对外提供服务了,将导致 C 访问出现问题,而 C 出现问题,将可能导致 A、B 出现问题,也即 问题逐级放大(最终可能引起系统崩溃)。

【解决:】
    服务降级、服务熔断 是解决 服务雪崩的 常用手段。
相关技术:
    Hystrix(维护状态,不推荐使用)
    Sentienl(推荐使用)

 

 

 

 

2、服务降级 与 服务熔断

(1) 服务降级

【服务降级:】
    服务降级 指的是 当服务器压力 剧增 时,根据当前 业务、流量 情况 对一些服务(一般为非核心业务)进行有策略的降级,确保核心业务正常执行。
    即 释放非核心服务 占用的服务器资源 确保 核心任务正常执行。
注:
    可以理解为 损失一部分业务能力,保证系统整体正常运行,从而防止 服务雪崩。
    资源是有限的,请求并发高时,若不对服务进行降级处理,系统可能花费大量资源进行非核心业务处理,导致 核心业务 效率降低,进而影响整体服务性能。
    此处的降级可以理解为 不提供服务 或者 延时提供服务(服务执行暂时不正常,给一个默认的返回结果,等一段时间后,正常提供服务)。
    
【服务降级分类:】
手动降级:
    可以通过修改配置中心配置,并根据事先定义好的逻辑,执行降级逻辑。

自动降级:
    超时降级:设置超时时间、超时重试次数,请求超时则服务降级,并使用异步机制检测 进行 服务恢复。
    失败次数降级:当请求失败达到一定次数则服务降级,同样使用异步机制检测 进行服务恢复。
    故障降级:服务宕机了则服务降级。
    限流降级:请求访问量过大则服务降级。

 

(2)服务熔断

【服务熔断:】
    服务熔断 指的是 目标服务不可用 或者 请求响应超时时,为了保证整体服务可用,
    不再调用目标服务,而是直接返回默认处理(释放系统资源),通过某种算法检测到目标服务可用后,则恢复其调用。
注:
    在一定时间内,服务调用失败次数达到一定比例,则认为 当前服务不可用。
    服务熔断 可以理解为 特殊的 服务降级(即 服务不可用 --> 服务降级 --> 服务调用恢复)。 

【martinfowler 相关博客地址:】
    https://martinfowler.com/bliki/CircuitBreaker.html

 

 

 

(3)服务降级 和 服务熔断 的区别

【相同点:】
    目标相同:均从 可靠性、可用性 触发,避免系统崩溃(服务雪崩)。
    效果相同:均属于 某功能暂不可用。
    
【不同点:】
    服务降级 一般 是从整体考虑,可以手动关闭 非核心业务,确保 核心业务正常执行。
    服务熔断 一般 是某个服务不可用,自动关闭 服务调用,并在一定时间内 重新尝试 恢复该服务调用。
注(个人理解(仅供参考)):
    服务降级 可以作为 预防措施(手动降级),即 服务并没有出错,但是为了提升系统效率,我主动放弃 一部分非核心业务,保证系统资源足够用于 执行 核心业务。
    服务熔断 就是 服务出错的 解决方案(自动降级),即 服务出错后 的一系列处理。

 

二、服务降级、熔断 -- Hystrix

1、什么是 Hystrix ?

【Hystrix:】
    Hystrix 是一个用于处理分布式系统 延迟 和 容错的 开源库,
    目的是 隔离远程系统、服务和第三方库的访问点,停止级联故障,并在不可避免发生故障的复杂分布式系统中实现恢复能力。
注:
    分布式系统难免出现 阻塞、超时、异常 等问题,Hystrix 可以保证在一个服务出问题时,不影响整个系统使用(避免服务雪崩),提高系统的可用性。
    虽然 Hystrix 已进入维护模式,不再更新,但还是可以学习一下思想、基本使用。

【常用特性:】
    服务降级
    服务熔断
    服务监控

【相关地址:】
    https://github.com/Netflix/Hystrix

 

 

 

2、使用 JMeter 模拟超时故障发生

(1)什么是 JMeter ?

【JMeter】
    Apache 的一款基于 Java 的压力测试工具。
注:
    有兴趣的自行研究,此处不过多赘述。
    
【官网下载地址:】
    http://jmeter.apache.org/download_jmeter.cgi
    
【JMeter 简单使用:】
    https://www.cnblogs.com/stulzq/p/8971531.html

 

 

 

(2)说明

【说明:】
    此处仅简单演示,不需要启动集群(单机版 Eureka 即可)。
    eureka_server_7000 作为 服务注册中心。
    eureka_client_producer_8001 作为服务提供者。
    eureka_client_consumer_9001 作为服务提供者。 
注:
    单机版 Eureka 可参考:https://www.cnblogs.com/l-y-h/p/14193443.html#_label2_1
    此处使用 RestTemplate 发送请求,使用上一篇 讲到的 OpenFeign 技术亦可。

【演示说明:】
    在 eureka_client_producer_8001 新定义一个接口 testTimeout(),内部暂停 2 秒模拟业务处理所需时间。
    一般情况下,访问 eureka_client_producer_8001 提供的 getUser() 接口时,会立即响应。
    
    但是如果大量请求访问 testTimeout(),而将系统资源(线程)耗尽时,
    此时若有请求访问 getUser() 就需要等待 前面请求执行完成后,才能继续处理。
    而此时就可能造成 超时等待 的情况,从而引起一系列问题。

即:   
    并发度低时:
        先访问 /consumer/user/testTimeout,再访问 /consumer/user/get/{id} 可以瞬间返回结果。

    并发度高时:
        若有大量请求访问 /consumer/user/testTimeout,导致系统资源(线程)暂时耗尽,
        此时再访问  /consumer/user/get/{id} 就需要等待一些时间才能返回结果。
        严重时请求会出现超时故障,从而引起系统异常。

 

(3)定义接口
  在 eureka_client_producer_8001 中定义一个新接口 testTimeout()。
  在 eureka_client_consumer_9001 中定义一个新接口 调用 testTimeout()。

【eureka_client_producer_8001:】
@GetMapping("/testTimeout")
public Result testTimeout() {
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return Result.ok();
}

【eureka_client_consumer_9001:】
@GetMapping("/testTimeout")
public Result testTimeout() {
    return restTemplate.getForObject(PRODUCER_URL + "/producer/user/testTimeout", Result.class);
}

 

 

 

 

 

 

(4)启动服务,并使用 JMeter 测试
并发度低时:
  先访问 /consumer/user/testTimeout,再访问 /consumer/user/get/{id} 可以瞬间返回结果。
注:
  看页面的刷新按钮。

 

 

并发度高时:
  使用 JMeter 模拟 200 个线程,循环 100 次,访问 /consumer/user/testTimeout。
  此时再访问 /consumer/user/get/{id} 时,不能瞬间返回结果(等待一段时间)。

 

 

 

 

(5)超时故障
  前面已经演示了高并发情况下可能出现超时等待情况,而若 业务执行时间过长 或者 服务调用设置了超时时间,那么当访问被阻塞时,将有可能引起故障。

【在声明 RestTemplate 时,定义超时时间】
@Bean
@LoadBalanced // 使用 @LoadBalanced 注解赋予 RestTemplate 负载均衡的能力
public RestTemplate getRestTemplate() {
    SimpleClientHttpRequestFactory httpRequestFactory = new SimpleClientHttpRequestFactory();
    httpRequestFactory.setConnectTimeout(2000);
    httpRequestFactory.setReadTimeout(2000);
    return new RestTemplate(httpRequestFactory);
}

 

 

 

 

3、Hystrix 实现服务降级

(1)服务降级使用场景
  服务降级 目的是 防止 服务雪崩,本质也就是在 服务调用 出问题时,应该如何处理。

【服务降级使用场景:】
    服务器资源耗尽,请求响应慢,导致请求超时。
    服务器宕机 或者 程序执行出错,导致请求出错。
即:
    服务提供者 响应请求超时了,服务消费者 不能一直等待,需要 服务提供者进行 服务降级,保证 请求在一定的时间内被处理。
    服务提供者 宕机了,服务消费者 不能一直等待,需要 服务消费者进行 服务降级,保证 请求在一定的时间内被处理。
    服务提供者正常,但 服务消费者 出现问题了,需要服务消费者 自行 服务降级。
    
注:
    服务降级一般在 服务消费者 中处理,服务提供者 也可以 进行处理。

 

(2)在 服务提供者 上实现服务降级(超时自动降级)
  在 eureka_client_producer_8001 代码基础上进行补充。
Step1:
  引入 hystrix 依赖。

【引入依赖:】
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

 

 

Step2:
  通过 @HystrixCommand 注解 编写 服务降级策略。

【简单说明:】
    @HystrixCommand 表示指定 服务降级 或者 服务熔断的策略。
    fallbackMethod 表示服务调用失败(请求超时 或者 程序执行异常)后执行的方法(方法参数要与 原方法一致)。
    commandProperties 表示配置参数。
    @HystrixProperty 设置具体参数。
注:
    详细参数情况可以参考 HystrixCommandProperties 类。
    com.netflix.hystrix.HystrixCommandProperties 


【定义服务降级策略:】
public Result testTimeoutReserveCase() {
    return Result.ok().message("当前服务器繁忙,请稍后再试!!!");
}

// 定义服务降级策略
@HystrixCommand(
        // 当请求超时 或者 接口异常时,会调用 fallbackMethod 声明的方法(方法参数要一致)
        fallbackMethod = "testTimeoutReserveCase",
        commandProperties = {
                @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value="1500")
        }
)
@GetMapping("/testTimeout")
public Result testTimeout() {
    try {
        Thread.sleep(500);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return Result.ok();
}

 

 

Step3:
  在启动类上添加 @EnableCircuitBreaker 注解,开启服务降级、熔断。

 

 

Step4:
  运行测试(此处演示的是 超时自动降级)。
  此处定义接口超时时间为 1.5 秒,模拟 0.5 秒业务处理时间,使用 JMeter 压测该接口时,与上面演示的类似,会出现请求超时的情况,而一旦请求超时,则会触发 fallbackMethod 方法,直接返回数据,而不会持续等待。
如下图所示。

 

 

(3)配置默认服务降级方法
  通过上面简单演示可以完成 服务降级,但是存在一个问题,如果为每一个接口都绑定一个 fallbackMethod,那么代码将非常冗余。
  通过 @DefaultProperties 注解 定义一个默认的 defaultFallback 方法,接口异常时调用默认的方法,并仅对特殊的接口进行单独处理,从而减少代码冗余。

如下,新增一个 运行时异常,访问接口时,将会调用 globalFallBackMethod() 方法。
而前面特殊定义的 testTimeout 超时后,仍调用 testTimeout_reserve_case() 方法。

@DefaultProperties(defaultFallback = "globalFallBackMethod")
public class UserController {
    public Result globalFallBackMethod() {
        return Result.ok().message("系统异常,请稍后再试!!!");
    }

    @GetMapping("/testRuntimeError")
    @HystrixCommand
    public Result testRuntimeError() {
        int temp = 10 / 0;
        return Result.ok();
    }
}

 

 

 

 

4、OpenFeign 实现服务降级

(1)说明

【说明:】
    上面使用 Hystrix 简单演示了 服务提供者 的服务降级。
    这里使用 OpenFeign 演示 服务消费者 的服务降级。
注:
    重新新建一个模块 eureka_openfeign_client_consumer_9007 作为服务消费者用于演示。
    可参考上一篇 OpenFeign 的使用:https://www.cnblogs.com/l-y-h/p/14238203.html#_label3_2
    服务提供者仍然是 eureka_client_producer_8001。

 

(2)配置 OpenFeign 基本代码环境
Step1:
  创建模块 eureka_openfeign_client_consumer_9007。
  修改父工程 与 当前工程 pom.xml 文件。
  修改配置类。
  在启动类上添加 @EnableFeignClients 注解。

【依赖:】
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

【application.yml】
server:
  port: 9007
spring:
  application:
    name: eureka-openfeign-client-consumer

eureka:
  instance:
    appname: eureka-openfeign-client-consumer-9007 # 优先级比 spring.application.name 高
    instance-id: ${eureka.instance.appname} # 设置当前实例 ID
  client:
    register-with-eureka: true # 默认为 true,注册到 注册中心
    fetch-registry: true # 默认为 true,从注册中心 获取 注册信息
    service-url:
      # 指向 注册中心 地址,也即 eureka_server_7000 的地址。
      defaultZone: http://localhost:7000/eureka

# 设置 OpenFeign 超时时间(OpenFeign 默认支持 Ribbon)
ribbon:
  # 指的是建立连接所用的超时时间
  ConnectTimeout: 2000
  # 指的是建立连接后从服务器获取资源的超时时间(即请求处理的超时时间)
  ReadTimeout: 2000

 

 

Step2:
  使用 @FeignClient 编写服务调用。

【ProducerFeignService:】
package com.lyh.springcloud.eureka_openfeign_client_consumer_9007.service;

import com.lyh.springcloud.common.tools.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;

@FeignClient(value = "EUREKA-CLIENT-PRODUCER-8001")
@Component
public interface ProducerFeignService {
    @GetMapping("/producer/user/get/{id}")
    Result getUser(@PathVariable Integer id);

    @GetMapping("/producer/user/testTimeout")
    Result testFeignTimeout();

    @GetMapping("/producer/user/testRuntimeError")
    Result testRuntimeError();
}

 

 

Step3:
  编写 controller,并进行测试 openfeign 是否能成功访问服务。

【ConsumerController】
package com.lyh.springcloud.eureka_openfeign_client_consumer_9007.controller;

import com.lyh.springcloud.common.tools.Result;
import com.lyh.springcloud.eureka_openfeign_client_consumer_9007.service.ProducerFeignService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/consumer/user")
public class ConsumerController {
    @Autowired
    private ProducerFeignService producerFeignService;

    @GetMapping("/get/{id}")
    public Result getUser(@PathVariable Integer id) {
        return producerFeignService.getUser(id);
    }

    @GetMapping("/testTimeout")
    public Result testFeignTimeout() {
        return producerFeignService.testFeignTimeout();
    }

    @GetMapping("/testRuntimeError")
    public Result testFeignRuntimeError() {
        return producerFeignService.testRuntimeError();
    }
}

 

 

 

 

(3)OpenFeign 实现服务降级

【步骤:】
Step1:在配置文件中,配置 feign.feign.enabled=true,开启服务降级。
Step2:定义一个 实现类,实现 服务调用的 接口,并为每个方法重写 调用失败的逻辑。
Step3:在 @FeignClient 注解中,通过 fallback 参数指定 该实现类。

Step1:
  在配置文件中,开启服务降级。

【application.yml】
# 开启服务降级
feign:
  hystrix:
    enabled: true

 

 

Step2:
  定义一个实现类,实现 服务调用的接口。
  @Component 注解不要忘了,启动时可能会报错。

【ProducerFeignServiceImpl:】
package com.lyh.springcloud.eureka_openfeign_client_consumer_9007.service.impl;

import com.lyh.springcloud.common.tools.Result;
import com.lyh.springcloud.eureka_openfeign_client_consumer_9007.service.ProducerFeignService;
import org.springframework.stereotype.Component;

@Component
public class ProducerFeignServiceImpl implements ProducerFeignService {
    @Override
    public Result getUser(Integer id) {
        return Result.ok().message("系统异常,请稍后再试 -- 11111111111");
    }

    @Override
    public Result testFeignTimeout() {
        return Result.ok().message("系统异常,请稍后再试 -- 222222222222");
    }

    @Override
    public Result testRuntimeError() {
        return Result.ok().message("系统异常,请稍后再试 -- 333333333333");
    }
}

 

 

注:
  未添加 @Component 注解,启动会报下面的错误。

【报错信息:】
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'consumerController': 
Unsatisfied dependency expressed through field 'producerFeignService'; nested exception is org.springframework.beans.factory.BeanCreationException: 
Error creating bean with name 'com.lyh.springcloud.eureka_openfeign_client_consumer_9007.service.ProducerFeignService': 
FactoryBean threw exception on object creation; nested exception is java.lang.IllegalStateException: 
No fallback instance of type class com.lyh.springcloud.eureka_openfeign_client_consumer_9007.service.impl.ProducerFeignServiceImpl found for feign client EUREKA-CLIENT-PRODUCER-8001

 

 

Step3:
  在 @FeignClient 注解上,通过 fallback 参数指定上面定义的实现类。

package com.lyh.springcloud.eureka_openfeign_client_consumer_9007.service;

import com.lyh.springcloud.common.tools.Result;
import com.lyh.springcloud.eureka_openfeign_client_consumer_9007.service.impl.ProducerFeignServiceImpl;
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;

@FeignClient(value = "EUREKA-CLIENT-PRODUCER-8001", fallback = ProducerFeignServiceImpl.class)
@Component
public interface ProducerFeignService {
    @GetMapping("/producer/user/get/{id}")
    Result getUser(@PathVariable Integer id);

    @GetMapping("/producer/user/testTimeout")
    Result testFeignTimeout();

    @GetMapping("/producer/user/testRuntimeError")
    Result testRuntimeError();
}

 

 

Step4:
  简单测试一下。
  当服务提供者 宕机时,此时服务调用失败,将会执行 实现类中的逻辑。
  而服务提供者正常提供服务时,由于上面已经在 服务提供者 处配置了 服务降级,则执行 服务提供者的服务降级策略。

 

 

 

5、Hystrix 实现服务熔断

(1)说明

【服务熔断:】
    服务熔断可以理解为特殊的服务降级,当某个服务出错或者响应时间长时,将会进行服务降级处理,
    从而熔断该服务的调用,但其会检测服务是否正常,若正常,则恢复服务调用。
注:
    代码方面 与 上面配置 超时服务自动降级 类似(在其基础上进行代码扩充)。

【断路器开启、关闭条件:】
开启条件:
    满足一定的请求阈值(默认 10 秒内请求数超过 20),且失败率达到阈值(默认 10 秒内 50% 的请求失败)。此时将会开启断路器。

关闭条件:
    断路器开启一段时间后(默认 5 秒),此时断路器处于半开状态,会对其中一部分请求进行转发,如果成功访问,则断路器关闭。
    若请求仍然失败,则再次进入开启状态。  

 

 

(2)添加接口配置服务熔断
  在 eureka_client_producer_8001 中新增一个接口,并配置服务熔断逻辑。

【服务熔断:】
public Result testCircuitBreakerFallBack(@PathVariable Integer id) {
    return Result.ok().message("调用失败, ID 不能为负数");
}

@GetMapping("/testCircuitBreaker/{id}")
@HystrixCommand(fallbackMethod = "testCircuitBreakerFallBack", commandProperties = {
        // 是否开启断路器。默认为 true。
        @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),
        // 在一定时间内,请求总数达到了阈值,才有资格进行熔断。默认 20 个请求。
        @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
        // 熔断之后,重新尝试恢复服务调用的时间,在此期间,会执行 fallbackMethod 定义的逻辑。默认 5 秒。
        @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),
        // 出错阈值,请求总数超过了阈值,并且调用失败率达到一定比率,会熔断。默认 50%。
        @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60")
})
public Result testCircuitBreaker(@PathVariable Integer id) {
    if (id < 0) {
        throw new RuntimeException("ID 不能为负数");
    }
    return Result.ok().message("调用成功, ID = " + id);
}

 

 

(3)直接访问该服务测试一下(使用 JMeter 测试亦可)。
  如下图所示,当请求失败达到一定比率,将会开启断路器。
  一段时间后,尝试恢复服务调用,关闭断路器。

 

 

6、Hystrix Dashboard

(1)Dashboard
  Hystrix 提供了图形化的监控工具(Hystrix Dashboard)进行准实时的调用监控,其可以持续的记录通过 Hystrix 发送的请求执行信息,并以图形、统计报表的形式呈现给用户。
  SpringCloud 对其进行了整合,导入相关依赖即可。

 

(2)使用
Step1:
  引入依赖(hystrix-dashboard 以及 actuator)。

【依赖:】
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>

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

 

 

Step2:
  在启动类上添加 @EnableHystrixDashboard 注解,开启 Dashboard。

 

 

Step3:
  启动服务后,访问 http://localhost:8001/hystrix,填写需要监控的地址即可。
  开启监控后,访问配置了 @HystrixCommand 注解的服务时,将会触发监控。

 

 

 

 

7、Dashboard 错误解决(Unable to connect to Command Metric Stream.)

(1)错误
  使用 Dashboard 最常见的错误就是 Unable to connect to Command Metric Stream。
  根据控制台打印的 日志进行相关配置即可解决此错误。
  错误效果如下图所示,

 

 

(2)错误一与解决:

【错误一:】
控制台打印错误如下:
    Proxy opening connection to: http://localhost:8001/hystrix.stream

【解决:】
    在配置类中添加如下配置。
/**
 * 错误 Unable to connect to Command Metric Stream. 本质是无法解析  "/hystrix.stream"。
 * 自行配置一下即可。
 */
@Bean
public ServletRegistrationBean getServlet() {
    HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
    ServletRegistrationBean<HystrixMetricsStreamServlet> registrationBean = new ServletRegistrationBean(streamServlet);
    registrationBean.setLoadOnStartup(1);
    registrationBean.addUrlMappings("/hystrix.stream");
    registrationBean.setName("HystrixMetricsStreamServlet");
    return registrationBean;
}

 

 

(3)错误二与解决:

【错误二:】
    上面解决了连接错误,但是仍然报错。
控制台打印错误如下:
    Origin parameter: http://localhost:8001/hystrix.stream is not in the allowed list of proxy host names.  
    If it should be allowed add it to hystrix.dashboard.proxyStreamAllowList.
    
【解决:】
    在配置文件中配置 proxyStreamAllowList 放行 host 即可。
hystrix:
  dashboard:
    proxy-stream-allow-list: "*"
#    proxy-stream-allow-list: "localhost"

 

DDD领域驱动设计:CQRS

 

三、服务降级、熔断 -- Sentinel

1、什么是 Sentinel?

(1)什么是 Sentinel?

【Sentinel:】
    Sentinel 是阿里开源的项目,面向云原生微服务的高可用流控防护组件。
    从流量控制、熔断降级、系统负载保护等多个维度来保障服务之间的稳定性。
注:
    官方文档上写的还是很详细的,并提供了相应的中文文档。
    此处不过多描述,仅介绍使用,相关概念、原理 请自行翻阅文档。

【官网地址:】
    https://github.com/alibaba/Sentinel
    https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D

Sentinel 主要特性如下(图片来源于网络:)

 

 

(2)Sentinel 组成

【Sentinel 组成:】
    Sentinel 可以分为两部分:Java 客户端  、Dashboard 控制台。

Java 客户端(核心库):    
    核心库 不依赖于 任何框架、库,能够运行于 Java7 及以上版本的 Java 运行时环境,
    同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
    
Dashboard 控制台:
    基于 SpringBoot 开发,打包后可直接运行,无需额外的 Tomcat 容器部署。
    控制台主要负责管理推送规则、监控、集群限流分配管理、机器发现等。
注:
    控制台使用参考文档:
        https://github.com/alibaba/Sentinel/wiki/%E6%8E%A7%E5%88%B6%E5%8F%B0
    控制台 jar 包下载:
        https://github.com/alibaba/Sentinel/releases

注:
    通过 Dashboard 控制台,可以很轻松的通过 web 页面设置 服务降级、熔断等规则。也可以通过代码的方式进行配置。
    个人比较倾向于 web 页面操作,省去了编写代码的工作(视工作环境而定)。
     后面介绍的 Dashboard 操作均以 web 界面进行操作,若想通过代码进行配置,请自行翻阅官方文档。
     web 页面编辑的规则 在 重启服务后 会丢失,需要将其进行持久化处理,一般都是持久化到 nacos 中(后续介绍)。

 

(3)Hystrix 与 Sentinel 区别

【官网地址:】
    https://github.com/alibaba/Sentinel/wiki/Guideline:-%E4%BB%8E-Hystrix-%E8%BF%81%E7%A7%BB%E5%88%B0-Sentinel
注:
    详情请自行查阅官网文档。

 

 

 

 

2、基本模块构建

(1)说明

【说明:】
    Sentinel 一般与 Nacos 一起使用,Nacos 使用后续介绍,此处仍使用 Eureka 进行整合。
    新建一个 eureka_client_sentinel_producer_8010 模块(引入核心库依赖)进行演示。
    从官网下载 sentinel-dashboard,用于启动 Dashboard 界面。
注:
    下载地址:https://github.com/alibaba/Sentinel/releases    

 

(2)下载并启动 sentinel-dashboard。
Step1:下载 sentinel-dashboard。

 

 

Step2:命令行启动 jar 包。

【命令行启动 jar 包:】
    java -jar sentinel-dashboard-1.8.0.jar
注:
    启动后,默认访问端口为 8080,可以通过 -Dserver.port 参数进行修改。
    比如: java -jar -Dserver.port=8888 sentinel-dashboard-1.8.0.jar
    
【访问地址:】
    通过 IP + 端口 即可进入登录界面,登录用户、密码 都默认为 sentinel。
    比如:http:localhost:8888

 

 

 

 

(3)基本模块 eureka_client_sentinel_producer_8010 创建
Step1:
  修改 父工程、当前工程 pom.xml 文件,并引入相关依赖。

【直接引入依赖(带上版本号):】
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    <version>2.1.0.RELEASE</version>
</dependency>

【或者在父工程中统一进行版本管理:】
<properties>
  <spring.cloud.alibaba.version>2.1.0.RELEASE</spring.cloud.alibaba.version>
</properties>

<dependencyManagement>
    <dependencies>
        <dependency>
          <groupId>com.alibaba.cloud</groupId>
          <artifactId>spring-cloud-alibaba-dependencies</artifactId>
          <version>${spring.cloud.alibaba.version}</version>
          <type>pom</type>
          <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

注:
    spring-cloud-alibaba 各版本地址:
    https://github.com/alibaba/spring-cloud-alibaba/releases

 

 

Step2:
  修改配置文件,配置 dashboard 信息。

【application.yml】
server:
  port: 8010

spring:
  application:
    name: eureka_client_sentinel_producer_8010
  # 配置 sentinel
  cloud:
    sentinel:
      transport:
        # 配置 sentinel-dashboard 地址,此处在本地启动,所以 host 为 localhost
        dashboard: localhost:8888
        # 应用与 dashboard 交互的端口,默认为 8719 端口
        port: 8719

eureka:
  instance:
    appname: eureka_client_sentinel_producer_8010 # 优先级比 spring.application.name 高
    instance-id: ${eureka.instance.appname} # 设置当前实例 ID
  client:
    register-with-eureka: true # 默认为 true,注册到 注册中心
    fetch-registry: true # 默认为 true,从注册中心 获取 注册信息
    service-url:
      # 指向 注册中心 地址,也即 eureka_server_7000 的地址。
      defaultZone: http://localhost:7000/eureka

 

 

Step3:
  编写测试 controller,进行测试。

【TestController】
package com.spring.cloud.eureka_client_sentinel_producer_8010.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/testSentinel")
public class TestController {

    @GetMapping("/hello")
    public String hello() {
        return "Hello World";
    }

    @GetMapping("/relation")
    public String relation() {
        return "relation";
    }
}

 

Step4:
  启动 eureka_server_7000、以及当前服务 ,测试一下。
注:
  即使服务启动,Sentinel Dashboard 也是空白的,需要调用一下当前服务的接口,其相关信息才会出现在 Sentinel 中。

 

 

 

 

3、Sentinel Dashboard 使用 -- 流控规则

(1)什么是流量控制(flow control)?

【流量控制:】
    流量控制 本质上是 监控 应用流量的 QPS 或者 并发线程数等指标,
    当监控的指标达到 阈值 后,将会对访问进行限制,减少请求的正常响应。
    从而避免应用 被瞬间的高并发请求击垮而崩溃,进而保障应用的高可用性。
注:
    QPS(Query Per Second):每秒查询率,即服务每秒能响应的查询(请求)次数。

【文档地址:】
    https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6

 

(2)流控规则基本参数

【相关类:】
    com.alibaba.csp.sentinel.slots.block.flow.FlowRule
注:
    通过代码设置流控规则可以参考如下代码:
    https://github.com/alibaba/Sentinel/blob/master/sentinel-demo/sentinel-demo-basic/src/main/java/com/alibaba/csp/sentinel/demo/flow/FlowQpsDemo.java

【流控规则页面参数:】
资源名:
    默认为 请求的资源路径,唯一。
    
针对来源:
    默认为 default,表示不区分来源。
注:
    网上查阅的资料都说可以根据 微服务名 进行限流,有待确认。
    此处未研究原理,留个坑,后续有时间再去研究。
    
阈值类型:
    QPS:
        当调用该资源接口的 QPS 达到阈值后,将会限流。
    线程数:
        当调用该资源接口的 线程数 达到阈值后,将会限流。
        
单机阈值:
    设置 阈值类型(QPS、线程数)的 阈值。

流控模式:
    直接:
        当资源接口达到限流条件时,会当前资源直接限流。
    关联:
        当某个资源关联的资源达到限流条件时,则 当前资源 被限流。
    链路:
        资源之间的调用形成调用链路,而 链路 模式仅记录 指定入口的流量,如果达到限流条件,则限流。
        
流控效果:
    快速失败:
        一旦达到限流条件,则直接抛异常(FlowException),然后走失败的处理逻辑。
    Warm Up:
        根据冷加载因子(coldFactor,默认 3),刚开始阈值请求数为 原阈值/coldFactor,经过一段时间后,阈值才会变为 原阈值。
    排队等待:
        请求会排队等待执行,匀速通过,此时的 阈值类型必须为 QPS。 

 

 

(3)演示 -- 直接、快速失败。

【说明:】
配置 /testSentinel/hello 的流控规则,不区分请求来源,
当 QPS 超过 1(即 1 秒钟超过 1 次查询)时,将会执行 直接限流,效果为 快速失败(会显示默认错误)。

【设置流控规则如下:】
资源名: /testSentinel/hello
针对来源: default
阈值类型: QPS
单机阈值: 1
流控模式: 直接
流控效果: 快速失败

 

 

如下图所示,1 秒刷新一次是正常返回的结果,而 1 秒刷新多次后,将会输出默认的错误信息。

 

 

(4)演示 -- 关联、快速失败。

【说明:】
现有资源 A、B,A 为 /testSentinel/hello,B 为 /testSentinel/relation。
配置 A 的流控规则,不区分请求来源,将 A 关联 B。
当 B 的 QPS 超过 1(即 1 秒钟超过 1 次查询)时,A 将会被限流,效果为 快速失败(会显示默认错误)。

【设置流控规则如下:】
资源名: /testSentinel/hello
针对来源: default
阈值类型: QPS
单机阈值: 1
流控模式: 关联
关联资源: /testSentinel/relation
流控效果: 快速失败

【实际使用场景举例:】
    两个资源之间具有依赖关系或者竞争资源时,可以使用关联。
比如:
    对数据库 读操作、写操作 进行限制,可以设置写操作优先,当 写操作 过于频繁时,读操作将被限流。

 

 如下图所示,正常访问 A 是没问题的,但是 B 在 1 秒内多次刷新后,A 将会输出默认出错信息。

 

 

(5)演示 -- 直接、Wram Up。

【说明:】
配置 /testSentinel/hello 的流控规则,不区分请求来源,
设置 QPS 为 6,预热时间为 3 秒,则开始 QPS 阈值将为 2,预热时间结束后,QPS 会恢复到 6。

【设置流控规则如下:】
资源名: /testSentinel/hello
针对来源: default
阈值类型: QPS
单机阈值: 6
流控模式: 直接
流控效果: Warm Up
预热时长: 3

【实际场景举例:】
秒杀系统开启瞬间会有很多请求进行访问,如果不做限制,可能一下子系统直接崩溃了。
采用 Warm Up 方式,给系统一个缓冲时间,慢慢的增大 QPS。

 

 如下图所示,开始 QPS 较小,刷新容易报错,3 秒后,QPS 恢复原值,刷新不容易报错。

 

 

(6)演示 --  直接、排队等待

【说明:】
配置 /testSentinel/hello 的流控规则,不区分请求来源,
QPS 超过 1 时,请求将会排队等待,超时时间为 2 秒,超时后将会输出错误信息。

【设置流控规则如下:】
资源名: /testSentinel/hello
针对来源: default
阈值类型: QPS
单机阈值: 1
流控模式: 直接
流控效果: 排队等待
超时时间: 2000

【实际场景举例:】
    通常用于处理 间隔性请求。
比如:
    消息队列,某瞬间的请求很多,但之后却没有请求,此时可以使用排队等待,将请求延迟执行(而不是直接拒绝)。

 

 如下图所示,快速刷新页面时,请求将会排队等待执行,超时后将会报错。

 

 

4、@SentinelResource 注解

(1)@SentinelResource 注解
  @SentinelResource 可以等同于 Hystrix 中的 @HystrixCommand 注解进行理解。

【相关地址:】
    https://github.com/alibaba/Sentinel/wiki/%E6%B3%A8%E8%A7%A3%E6%94%AF%E6%8C%81
    
【@SentinelResource:】
    @SentinelResource 注解用于定义资源,并提供了可选的异常处理 以及 fallback 配置项。

value:资源名称,必须项(不能为空)。
    
blockHandler:
    指定限流异常(BlockException)发生后,应该执行的方法。
    注意事项:
        方法访问权限修饰符为 public。
        返回值类型 与 原方法返回值类型一致。
        参数类型 与 原方法一致,并追加一个 额外参数,参数类型为 BlockException。
        blockHandler 指定的函数默认需要与 原方法在同一个类中。

blockHandlerClass:
    指定限流异常(BlockException)发生后,应该执行的方法(此方法可以位于 其他类)。
    注意事项:
        方法访问权限修饰符为 public。
        方法必须是 static 函数,否则无法解析。

fallback:
    指定异常(除了 exceptionsToIgnore 指定的异常外的异常)发生后,应该执行的方法。
    注意事项:
        返回值类型 与 原方法返回值类型一致。
        参数类型 与 原方法一致,可以额外增加一个 Throwable 类型的参数用于接收对应的异常。
        fallback 指定的函数默认需要与 原方法在同一个类中。
        
fallbackClass:
    指定异常发生后,应该执行的方法(此方法可以位于 其他类)。
    注意事项:
        方法访问权限修饰符为 public。
        方法必须是 static 函数,否则无法解析。 

defaultFallback:
    指定异常发生后,执行默认的逻辑(即 通用处理逻辑)。
    与 fallback 类似,但 其指定的方法参数为空,可以额外增加一个 Throwable 类型的参数用于接收对应的异常。
   当 fallback 与 defaultFallback 同时存在时,只有 fallback 会生效。

exceptionsToIgnore:
    用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中(直接对外抛出原异常)。

【@SentinelResource 使用注意事项:】
    若 blockHandler 和 fallback 都配置了,当限流降级异常发生时(即 抛出 BlockException),只会执行 blockHandler 指定的方法。
   若未配置 fallback、blockHandler 时,则限流降级时,则可能直接抛出 BlockException 异常,若方法本身没定义 throws BlockException,则异常将会被 JVM 包装为 UndeclaredThrowableException 异常。

可以简单的理解为: 
    blockHandler 用于指定 限流、降级 等异常(BlockException)发生后应该执行的规则。
    fallback 用于指定 其他异常(比如: RuntimeException)发生后应该执行的规则。

 

 

(2)@SentinelResource 使用举例

【说明:】
    在 controller 中新增如下代码,
    fallback() 表示异常发生后的回调函数。
    defaultFallback() 表示异常发生后默认的回调函数。
    blockHandler()、blockHandler2() 表示 流控、降级 等 BlockException 异常发生后的回调函数。
        
    testBlockHandler() 用于测试 blockHandler 参数。
    testFallback() 用于测试 fallback 参数。
    testDefaultFallback() 用于测试 defaultFallback 参数。

【新增代码:】
public String fallback(Integer id, Throwable ex) {
    return ex.getMessage();
}

public String defaultFallback(Throwable ex) {
    return ex.getMessage();
}

public String blockHandler(BlockException ex) {
    return "block Handler";
}

public String blockHandler2(Integer id, BlockException ex) {
    return "block Handler --------- 2";
}

@GetMapping("/testBlockHandler")
@SentinelResource(value = "testBlockHandler", blockHandler = "blockHandler")
public String testBlockHandler() {
    return "ok";
}

@GetMapping("/testFallback/{id}")
@SentinelResource(value = "testFallback", blockHandler = "blockHandler", fallback = "fallback")
public String testFallback(@PathVariable Integer id) {
    if (id > 10) {
        throw new RuntimeException("fallback");
    }
    return "ok";
}

@GetMapping("/testDefaultFallback/{id}")
@SentinelResource(value = "testDefaultFallback", blockHandler = "blockHandler2", defaultFallback = "defaultFallback")
public String testDefaultFallback(@PathVariable Integer id) {
    if (id > 10) {
        throw new RuntimeException("defaultFallback");
    }
    return "ok";
}

 

 

Step1:
  访问 testBlockHandler(),测试 blockHandler 执行回调函数。
  如下,给 testBlockHandler 添加流控规则,当 QPS 大于 1 时,将进行限流,此时将会执行 blockHandler 指定的回调函数。
注:
  正常访问 testBlockHandler() 时,sentinel dashboard 会监控到两个资源名,此处应选择 @SentinelResource 注解中 value 定义的资源名,并配置 流控、降级 规则。

 

 

 

 

 

 

Step2:
  访问 testFallback(),测试 fallback 执行回调函数。
  当 id 小于等于 10 时,正常调用。
  当 id 大于 10 时,抛出异常后被 fallback 接收并执行回调函数。
  同样,设置 流控规则,QPS 大于 1 时,限流,但由于 blockHandler 参数指定的回调方法参数 与 原方法不同,所以该回调函数不生效(空白)。
注:
  限流、降级 等异常 执行的是 blockHandler 指定的回调函数。
  而其他异常 执行的是 fallback 指定的回调函数。

 

 

 

 

Step3:
  访问 testDefaultFallback(),测试 defaultFallback 执行回调函数。
  与上例类似,只是此处 blockHandler 回调函数参数 与 原方法相同,可以调用成功。

 

 

 

 

5、Sentinel Dashboard 使用 -- 降级规则、系统规则(系统自适应限流)

(1)熔断降级
  熔断降级相关概念,前面在 Hystrix 已经介绍了。
  此处仅演示 Sentinel 降级操作。

【降级策略:】
慢调用比例(SLOW_REQUEST_RATIO): 
    若选择 慢调用比例 作为阈值,需外同时设置几个参数。
    参数:
        慢调用最大响应时间(最大 RT)。当请求响应时间大于该值时,将被统计为慢调用。
        比例阈值。比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。当慢调用比例大于该值时,将会触发熔断机制。
        最小请求数。单位统计时长内接收请求的最小数。
        熔断时长。熔断执行的时间。
    简单解释:
        当单位统计时长(statIntervalMs)内请求数目 大于 最小请求数,且 慢调用比例(超时请求占总请求数的比例) 大于 比例阈值 时,
        将会在一定的 熔断时长 内熔断请求(执行 熔断的相关代码)。
        熔断时长结束后,会进入探测恢复状态(即 Hystrix 中提到的 HALF-OPEN 状态),若检测到接下来的一个请求正常调用,则结束熔断,若依旧超时,则再次熔断。
注:
    此处的 HALF-OPEN 状态,来源于官网介绍(针对 Sentinel 1.8.0 及以上版本)。
    旧版本可能没有 HALF-OPEN 状态(没实际验证过)。

异常比例(ERROR_RATIO):
    异常比例 与 慢调用比例 类似,异常比例的参数少了个 最大响应时间。
    简单解释:
        当单位统计时长(statIntervalMs)内请求数目 大于 最小请求数,且 异常比例(异常请求占总请求数的比例) 大于 比例阈值 时,
        将会在一定的 熔断时长 内熔断请求(执行 熔断的相关代码)。
        熔断时长结束后,会进入探测恢复状态(HALF-OPEN 状态),若检测到接下来的一个请求正常调用,则结束熔断,否则会再次被熔断。

异常数(ERROR_COUNT):
    异常数 与 异常比例 类似,异常数 将参数 异常比例 变为 异常数(直接监控异常数,而非比例)。
    简单解释:
        当单位统计时长(statIntervalMs)内 异常请求数 超过 异常数阈值 后,
        将会在一定的 熔断时长 内熔断请求(执行 熔断的相关代码)。
        熔断时长结束后,会进入探测恢复状态(HALF-OPEN 状态),若检测到接下来的一个请求正常调用,则结束熔断,否则会再次被熔断。

 

 

(2)演示 -- 异常比例
  在上面 testDefaultFallback() 基础上,添加 降级 规则,演示 异常比例 降级。
注:
  删除添加的流控规则,并指定 降级规则。
  当降级发生时,将会触发 blockHandler 指定的回调方法。

 

 

 

 

(3)系统规则

【相关文档:】
    https://github.com/alibaba/Sentinel/wiki/%E7%B3%BB%E7%BB%9F%E8%87%AA%E9%80%82%E5%BA%94%E9%99%90%E6%B5%81

【什么是系统规则(系统自适应限流):】
    系统自适应限流 是从 整体维度 对 应用程序 入口流量 进行控制,即 在调用应用程序的 方法(接口) 前,将请求拦截下来。
    通过自适应的流控策略,让 系统的入口流量 和 系统的负载 达到一个平衡,即 让系统尽可能 在保证 最大吞吐量的同时 保证 系统整体的稳定性,
常用指标: 
    应用的负载(Load)、
    CPU 使用率、
    总体平均响应时间(RT)、
    入口 QPS、
    并发线程数 等。

 

 

 

 

6、OpenFeign 整合 Sentinel(引入 sentinel 依赖需要注意版本问题)

(1)说明

【说明:】
    OpenFeign 一般用于消费端,此处以 eureka_client_consumer_9001 为基础,整合 Sentinel。
注:
    此处为了省事,直接用之前创建好的子模块,亦可自行创建新的模块。
    使用流程 与 Hystrix 类似。

 

(2)整合 Sentinel
Step1:
  在 eureka_client_consumer_9001 基础上引入 依赖。

【依赖:】
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

<!-- alibaba-sentinel -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    <version>2.2.1.RELEASE</version>
    <exclusions>
        <exclusion>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-xml</artifactId>
        </exclusion>
    </exclusions>
</dependency>

【注意事项:(巨坑)】
    alibaba-sentinel 的版本可能会影响程序的执行。

此处使用的版本:
    springcloud  Hoxton.SR9
    spring.cloud.alibaba 2.1.0.RELEASE
    springboot 2.3.5.RELEASE
引入 alibaba-sentinel 依赖后,服务一直无法启动。
报错:
    nested exception is java.lang.AbstractMethodError: Receiver class com.alibaba.cloud.sentinel.feign.SentinelContractHolder does not define or inherit an implementation of the resolved method 'abstract java.util.List parseAndValidateMetadata(java.lang.Class)' of interface feign.Contract.

具体原因没整明白,但是如上引入依赖后,可以解决问题(有时间再去研究)。

 

 

 

 

Step2:
  修改配置文件。开启 Sentinel 对 Feign 的支持。

【application.yml】
# 开启 sentinel 对 feign 的支持
feign:
  sentinel:
    enabled: true

 

 

Step3:
  使用 @FeignClient 编写服务调用。

【ProducerFeignService】
package com.lyh.springcloud.eureka_client_consumer_9001.service;

import com.lyh.springcloud.eureka_client_consumer_9001.service.impl.ProducerFeignServiceImpl;
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;

@FeignClient(value = "EUREKA-CLIENT-SENTINEL-PRODUCER-8010", fallback = ProducerFeignServiceImpl.class)
@Component
public interface ProducerFeignService {

    @GetMapping("/testSentinel/testDefaultFallback/{id}")
    String testDefaultFallback(@PathVariable Integer id);

    @GetMapping("/testSentinel/hello")
    String hello();
}

【ProducerFeignServiceImpl】
package com.lyh.springcloud.eureka_client_consumer_9001.service.impl;

import com.lyh.springcloud.eureka_client_consumer_9001.service.ProducerFeignService;
import org.springframework.stereotype.Component;

@Component
public class ProducerFeignServiceImpl implements ProducerFeignService {
    @Override
    public String testDefaultFallback(Integer id) {
        return "系统异常,请稍后重试 --------- 1111111111111";
    }

    @Override
    public String hello() {
        return "系统异常,请稍后重试 --------- 2222222222222";
    }
}

 

 

Step4:
  编写 controller。

【TestController】
package com.lyh.springcloud.eureka_client_consumer_9001.controller;

import com.lyh.springcloud.eureka_client_consumer_9001.service.ProducerFeignService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/consumer2")
@RestController
public class TestController {

    @Autowired
    private ProducerFeignService producerFeignService;

    @GetMapping("/testDefaultFallback/{id}")
    public String testDefaultFallback(@PathVariable Integer id) {
        return producerFeignService.testDefaultFallback(id);
    }

    @GetMapping("/hello")
    public String hello() {
        return producerFeignService.hello();
    }
}

 

 

Step5:
  在启动类上添加 @EnableFeignClients 注解,开启 feign 功能。

 

 

Step6:
  测试。
  给 testDefaultFallback() 添加流控规则,QPS 大于 1 时将限流。
  QPS 小于等于 1 时,正常访问。
  若远程服务断开后,访问 testDefaultFallback() 将失败,从而执行本地添加的逻辑。

 

 

 

 

7、持久化配置信息到 Nacos 

 后续使用到 Nacos 再介绍,此处暂时省略。。。

Redis 实战 —— 13. 扩展 Redis

相关推荐

发表评论

路人甲

网友评论(0)