SpringCloud基础知识 小结(Eureka、Ribbon、Hystrix、Feign、Zuul)

SpringCLoud从学到用,该做个小结了

SpringCloud介绍

spring cloud 是一种分布式服务的框架,它为开发人员提供了快速构建分布式系统的常用模式的一些工具,比如说配置管理、服务的注册与发现、服务调用的负载均衡、资源隔离、熔断降级等等,spring cloud为这些提供了一阵套完整的解决方案。
传统系统
传统系统是几十万行代码部署在一台服务器上,缺点十分明显:代码耦合严重,可重用低,开发效率低下,请求量大。有点就是部署和运维简单
springcloud优势与不足
优势:
spring cloud 是基于spring boot,大大简化开发。spring cloud提供了一套较为完整的分布式系统化解决方案,这也是为什么很多的中小型公司现在比较倾向于使用spring cloud来作为分布式开发框架。
不足:
服务的注册发现和服务之间的调用采用的都是http,因此这对网络调用的性能有一定的影响。部署和运维难度增加。

SpringCloud组件

Spring最擅长的就是集成,把世界上最好的框架拿过来,集成到自己的项目中。

SpringCloud也是一样,它将现在非常流行的一些技术整合到一起,实现了诸如:配置管理,服务发现,智能路由,负载均衡,熔断器,控制总线,集群状态等等功能。其主要涉及的组件包括:

  • Eureka:服务治理组件,包含服务注册中心,服务注册与发现机制的实现。(服务治理,服务注册/发现)
  • Zuul:网关组件,提供智能路由,访问过滤功能
  • Ribbon:客户端负载均衡的服务调用组件(客户端负载)
  • Feign:服务调用,给予Ribbon和Hystrix的声明式服务调用组件 (声明式服务调用)
  • Hystrix:容错管理组件,实现断路器模式,帮助服务依赖中出现的延迟和为故障提供强大的容错能力。(熔断、断路器,容错)

Eureka注册中心

Eureka就好比是滴滴,负责管理、记录服务提供者的信息。服务调用者无需自己寻找服务,而是把自己的需求告诉Eureka,然后Eureka会把符合你需求的服务告诉你。

同时,服务提供方与Eureka之间通过“心跳”机制进行监控,当某个服务提供方出现问题,Eureka自然会把它从服务列表中剔除。

这就实现了服务的自动注册、发现、状态监控。
在这里插入图片描述

  • Eureka:就是服务注册中心(可以是一个集群),对外暴露自己的地址
  • 提供者:启动后向Eureka注册自己信息(地址,提供什么服务)
  • 消费者:向Eureka订阅服务,Eureka会将对应服务的所有提供者地址列表发送给消费者,并且定期更新
  • 心跳(续约):提供者定期通过http方式向Eureka刷新自己的状态

服务同步

多个Eureka Server之间也会互相注册为服务,当服务提供者注册到Eureka Server集群中的某个节点时,该节点会把服务的信息同步给集群中的每个节点,从而实现数据同步。因此,无论客户端访问到Eureka Server集群中的任意一个节点,都可以获取到完整的服务列表信息。

动手搭建高可用的EurekaServer

我们假设要运行两个EurekaServer的集群,端口分别为:10086和10087。只需要把itcast-eureka启动两次即可。这是微服务也应该注册到两个Eureka中

服务提供者

服务注册

服务提供者在启动时,会检测配置属性中的:eureka.client.register-with-eureka=true参数是否正确,事实上默认就是true。如果值确实为true,则会向EurekaServer发起一个Rest请求,并携带自己的元数据信息,Eureka Server会把这些信息保存到一个双层Map结构中。

  • 第一层Map的Key就是服务id,一般是配置中的spring.application.name属性
  • 第二层Map的key是服务的实例id。一般host+ serviceId + port,例如:locahost:service-provider:8081
  • 值则是服务的实例对象,也就是说一个服务,可以同时启动多个不同实例,形成集群。

服务续约

在注册服务完成以后,服务提供者会维持一个心跳(定时向EurekaServer发起Rest请求),告诉EurekaServer:“我还活着”。这个我们称为服务的续约(renew);

有两个重要参数可以修改服务续约的行为:

eureka:
  instance:
    lease-expiration-duration-in-seconds: 90 #服务失效时间,默认值90秒
    lease-renewal-interval-in-seconds: 30 #服务续约(renew)的间隔,默认为30秒

也就是说,默认情况下每个30秒服务会向注册中心发送一次心跳,证明自己还活着。如果超过90秒没有发送心跳,EurekaServer就会认为该服务宕机,会从服务列表中移除,这两个值在生产环境不要修改,默认即可

但是在开发时,这个值有点太长了,经常我们关掉一个服务,会发现Eureka依然认为服务在活着。所以开发阶段适当调小。

eureka:
  instance:
    lease-expiration-duration-in-seconds: 10 # 10秒即过期
    lease-renewal-interval-in-seconds: 5 # 5秒一次心跳

服务消费者

获取服务列表

当服务消费者启动时,会检测eureka.client.fetch-registry=true参数的值,如果为true,则会拉取Eureka Server服务的列表只读备份,然后缓存在本地。并且每隔30秒会重新获取并更新数据。我们可以通过下面的参数来修改:

生产环境中,我们不需要修改这个值。

但是为了在开发环境下,能够快速得到服务的最新状态,我们可以将其设置小一点。

eureka:
  client:
    registry-fetch-interval-seconds: 30 #生产环境默认30即可

失效剔除和自我保护

服务下线

当服务进行正常关闭操作时,它会触发一个服务下线的REST请求给Eureka Server,告诉服务注册中心:“我要下线了”。服务中心接受到请求之后,将该服务置为下线状态。

失效剔除

有些时候,我们的服务提供方并不一定会正常下线
可能因为内存溢出、网络故障等原因导致服务无法正常工作。
Eureka Server需要将这样的服务剔除出服务列表。
因此它会开启一个定时任务,每隔60秒对所有失效的服务(超过90秒未响应)进行剔除。

可以通过eureka.server.eviction-interval-timer-in-ms参数对其进行修改,单位是毫秒。

开发环境中,服务重启,隔了60秒Eureka才反应过来,所以开发阶段可以适当调整。

Ribbon负载均衡(Eureka集成)

实际环境中,会开启很多服务提供方service-provider的集群。此时我们获取的服务列表中就会有多个,到底该访问哪一个呢?

一般这种情况下我们就需要编写负载均衡算法,在多个实例列表中进行选择。

不过Eureka中已经帮我们集成了负载均衡组件:Ribbon,简单修改代码即可使用。

什么是Ribbon:
在这里插入图片描述

负载均衡策略

Ribbon默认的负载均衡策略是简单的轮询,服务提供方轮流工作。

在源码中我们看到拦截中是使用RibbonLoadBalanceClient来进行负载均衡的,其中有一个choose方法,找到choose方法的接口方法,是这样介绍的:选择一种负载均衡策略。在这里插入图片描述
可以用这个类对象来进行测试(开启两个服务提供方:8081,8082):
在这里插入图片描述
结果就是顺序获取服务。
在这里插入图片描述
SpringBoot提供了修改负载均衡规则的配置入口,在服务消费方的配置中添加如下配置:RandomRule即随机获取服务

server:
  port: 80
spring:
  application:
    name: service-consumer
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
service-provider:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

负载均衡有好几种实现策略,常见的有:

随机 (Random)
轮询 (RoundRobin)
一致性哈希 (ConsistentHash)
哈希 (Hash)
加权(Weighted)
自定义

Hystrix熔断

Hystrix,英文意思是豪猪,全身是刺,看起来就不好惹,是一种保护机制。
Hystix是Netflix开源的一个延迟和容错库,用于隔离访问远程服务、第三方库,防止出现级联失败。在这里插入图片描述

雪崩问题

Hystix解决雪崩问题的手段有两个(后面提到):

  • 线程隔离
  • 服务熔断

微服务中,服务间调用关系错综复杂,一个请求,可能需要调用多个微服务接口才能实现,会形成非常复杂的调用链路

比如:一次业务请求,需要调用A、P、H、I四个服务,这四个服务又可能调用其它服务。如果此时,某个服务出现异常(IO异常等引起服务阻塞):
在这里插入图片描述
然后,用户不会得到响应,则tomcat的这个线程不会释放,于是越来越多的用户请求到来,越来越多的线程会阻塞
服务器支持的线程和并发数有限,服务器资源耗尽,形成雪崩效应。

在这里插入图片描述

1.线程隔离,服务降级

看下面的图↓
Hystrix为每个依赖服务调用分配一个小的线程池,如果线程池已满调用将被立即拒绝,默认不采用排队,加速失败判定时间。

用户的请求将不再直接访问服务,而是通过线程池中的空闲线程来访问服务,如果线程池已满,或者请求超时,则会进行降级处理,什么是服务降级?

服务降级:优先保证核心服务,而非核心服务不可用或弱可用。

用户的请求故障时,不会被阻塞,更不会无休止的等待或者看到系统崩溃,至少可以看到一个执行结果(例如返回友好的提示信息) 。

服务降级虽然会导致请求失败,但是不会导致阻塞,而且最多会影响这个依赖服务对应的线程池中的资源,对其它服务没有响应。

触发Hystix服务降级的情况:

  • 线程池已满
  • 请求超时

在这里插入图片描述

例子

首先在服务消费方引入Hystrix依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
1.01启动类开启熔断

在这里插入图片描述
在微服务中,经常会引入上面的三个注解,于是Spring就提供了一个组合注解:@SpringCloudApplication来替换掉原来的三个注解。在这里插入图片描述

1.02编写降级逻辑

我们编写服务消费方service-consumer,当目标服务的调用出现故障,我们希望快速失败,给用户一个友好提示。因此需要提前编写好失败时的降级处理逻辑,要使用HystixCommond来完成:

@Controller
@RequestMapping("consumer/user")
public class UserController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping
    @ResponseBody
    @HystrixCommand(fallbackMethod = "queryUserByIdFallBack")
    public String queryUserById(@RequestParam("id") Long id) {
        String user = this.restTemplate.getForObject("http://service-provider/user/" + id, String.class);
        return user;
    }

    public String queryUserByIdFallBack(Long id){
        return "请求繁忙,请稍后再试!";
    }
}

要注意,因为熔断的降级逻辑方法必须跟正常逻辑方法保证:相同的参数列表和返回值声明。失败逻辑中返回User对象没有太大意义,一般会返回友好提示。所以我们把queryById的方法改造为返回String,反正也是Json数据。这样失败逻辑中返回一个错误说明,会比较方便。

  • @HystrixCommand(fallbackMethod = “queryByIdFallBack”):用来声明一个降级逻辑的方法

测试:

当itcast-service-provder正常提供服务时,访问与以前一致。但是当我们将itcast-service-provider停机时,会发现页面返回了降级处理信息:在这里插入图片描述
默认FallBack

我们刚才把fallback写在了某个业务方法上,如果这样的方法很多,那岂不是要写很多。所以我们可以把Fallback配置加在类上,实现默认fallback:

@Controller
@RequestMapping("consumer/user")
@DefaultProperties(defaultFallback = "fallBackMethod") // 指定一个类的全局熔断方法
public class UserController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping
    @ResponseBody
    @HystrixCommand // 标记该方法需要熔断
    public String queryUserById(@RequestParam("id") Long id) {
        String user = this.restTemplate.getForObject("http://service-provider/user/" + id, String.class);
        return user;
    }
    /**
     * 熔断方法
     * 返回值要和被熔断的方法的返回值一致
     * 熔断方法不需要参数
     */
    public String fallBackMethod(){
        return "请求繁忙,请稍后再试!";
    }
}
  • @DefaultProperties(defaultFallback = “defaultFallBack”):在类上指明统一的失败降级方法, defaultFallback默认降级方法,不用任何参数,以匹配更多方法,但是返回值一定一致
  • @HystrixCommand:在方法上直接使用该注解,使用默认的剪辑方法。
1.03设置超时

在之前的案例中,请求在超过1秒后都会返回错误信息,这是因为Hystix的默认超时时长为1,我们可以通过配置修改这个值:

我们可以通过hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds来设置Hystrix超时时间。该配置没有提示。

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 6000 # 设置hystrix的超时时间为6000ms

在服务提供者方,使其响应时间超过6s,即可触发熔断

@GetMapping("{id}")
public User queryUserById(@PathVariable("id") Long id) {
    try {
        Thread.sleep(6000);//模拟
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return this.userService.queryUserById(id);
}

2.服务熔断

熔断原理

在这里插入图片描述
熔断状态机3个状态:

  • Closed:关闭状态,所有请求都正常访问。
  • Open:打开状态,所有请求都会被降级。Hystix会对请求情况计数,当一定时间内失败请求百分比达到阈值,则触发熔断,断路器会完全打开。默认失败比例的阈值是50%,请求次数最少不低于20次。
  • Half Open:半开状态,open状态不是永久的,打开后会进入休眠时间(默认是5S)。随后断路器会自动进入半开状态。此时会释放部分请求通过,若这些请求都是健康的,则会完全关闭断路器,否则继续保持打开,再次进行休眠计时

尝试熔断

为了能够精确控制请求的成功或失败,我们在consumer的调用业务中加入一段逻辑:

@GetMapping("{id}")
@HystrixCommand
public String queryUserById(@PathVariable("id") Long id){
    if(id == 1){
        throw new RuntimeException("太忙了");
    }
    String user = this.restTemplate.getForObject("http://service-provider/user/" + id, String.class);
    return user;
}

这样如果参数是id为1,一定失败,其它情况都成功。(不要忘了清空service-provider中的休眠逻辑)

我们准备两个请求窗口:

  • 一个请求:http://localhost/consumer/user/1,注定失败
  • 一个请求:http://localhost/consumer/user/2,肯定成功

当我们疯狂访问id为1的请求时(超过20次),就会触发熔断。断路器会断开,一切请求都会被降级处理。

此时你访问id为2的请求,会发现返回的也是失败,而且失败时间很短,只有几毫秒左右:在这里插入图片描述
默认的熔断触发要求较高,休眠时间窗较短,为了测试方便,我们可以通过配置修改熔断策略:

circuitBreaker.requestVolumeThreshold=10
circuitBreaker.sleepWindowInMilliseconds=10000
circuitBreaker.errorThresholdPercentage=50

解读:

  • requestVolumeThreshold:触发熔断的最小请求次数,默认20
  • errorThresholdPercentage:触发熔断的失败请求最小占比,默认50%
  • sleepWindowInMilliseconds:休眠时长,默认是5000毫秒

Feign

在这里插入图片描述
前面使用Ribbon的负载均衡功能,大大简化了远程调用时的代码:

String user = this.restTemplate.getForObject("http://service-provider/user/" + id, String.class);

如果就学到这里,你可能以后需要编写类似的大量重复代码,格式基本相同,无非参数不一样。有没有更优雅的方式,来对这些代码再次优化呢?

这就是我们接下来要学的Feign的功能了。
在这里插入图片描述
Feign可以把Rest的请求进行隐藏,伪装成类似SpringMVC的Controller一样。你不用再自己拼接url,拼接参数等等操作,一切都交给Feign去做。

快速入门

依赖导入

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

开启Feign
我们在启动类上,添加注解,开启Feign功能

@SpringCloudApplication
@EnableFeignClients // 开启feign客户端
public class ItcastServiceConsumerApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(ItcastServiceConsumerApplication.class, args);
    }
}

feign已经自动集成了Ribbon负载均衡的RestTemplate。
Feign的客户端

服务消费方service-consumer中,添加UserClient接口:
在这里插入图片描述
内容:

@FeignClient(value = "service-provider") // 标注该类是一个feign接口
public interface UserClient {

    @GetMapping("user/{id}")
    User queryById(@PathVariable("id") Long id);
}
  • 首先这是一个接口,Feign会通过动态代理,帮我们生成实现类。这点跟mybatis的mapper很像
  • @FeignClient,声明这是一个Feign客户端,类似@Mapper注解。同时通过value属性指定服务名称
  • 接口中的定义方法,完全采用SpringMVC的注解,Feign会根据注解帮我们生成URL,并访问获取结果

调用UserClient接口即可!

@Controller
@RequestMapping("consumer/user")
public class UserController {

    @Autowired
    private UserClient userClient;

    @GetMapping
    @ResponseBody
    public User queryUserById(@RequestParam("id") Long id){
        User user = this.userClient.queryUserById(id);
        return user;
    }

}

Ribbon、Hystrix

Feign本身集成Ribbon、Hystrix(默认关闭)配置,无需额外引入依赖

feign:
  hystrix:
    enabled: true # 开启Feign的熔断功能

但是,Feign中的Fallback配置不像hystrix中那样简单了。

1)首先,我们要定义一个类UserClientFallback,实现刚才编写的UserClient,作为fallback的处理类
在这里插入图片描述

@Component
public class UserClientFallback implements UserClient {

    @Override
    public User queryById(Long id) {
        User user = new User();
        user.setUserName("服务器繁忙,请稍后再试!");
        return user;
    }
}

2)然后在UserFeignClient中,指定刚才编写的实现类

@FeignClient(value = "service-provider", fallback = UserClientFallback.class) // 标注该类是一个feign接口
public interface UserClient {

    @GetMapping("user/{id}")
    User queryUserById(@PathVariable("id") Long id);
}

3)重启测试:
在这里插入图片描述

Zuul 网关

在这里插入图片描述
通过前面的学习,使用Spring Cloud实现微服务的架构基本成型,大致是下图:

在该架构中,我们的服务集群包含:内部服务Service A和Service B,他们都会注册与订阅服务至Eureka Server,而Open Service是一个对外的服务,通过均衡负载公开至服务调用方。我们把焦点聚集在对外服务这块,直接暴露我们的服务地址,这样的实现是否合理,或者是否有更好的实现方式呢?
在这里插入图片描述
先来说说这样架构需要做的一些事儿以及存在的不足:

  • 破坏了服务无状态特点。

    为了保证对外服务的安全性,我们需要实现对服务访问的权限控制,而开放服务的权限控制机制将会贯穿并污染整个开放服务的业务逻辑,这会带来的最直接问题是,破坏了服务集群中REST API无状态的特点。

    从具体开发和测试的角度来说,在工作中除了要考虑实际的业务逻辑之外,还需要额外考虑对接口访问的控制处理。

  • 无法直接复用既有接口。

    当我们需要对一个即有的集群内访问接口,实现外部服务访问时,我们不得不通过在原有接口上增加校验逻辑,或增加一个代理调用来实现权限控制,无法直接复用原有的接口。

面对类似上面的问题,我们要如何解决呢?答案是:服务网关!

为了解决上面这些问题,我们需要将权限控制这样的东西从我们的服务单元中抽离出去,而最适合这些逻辑的地方就是处于对外访问最前端的地方,我们需要一个更强大一些的均衡负载器的 服务网关。

服务网关是微服务架构中一个不可或缺的部分。通过服务网关统一向外系统提供REST API的过程中,除了具备服务路由均衡负载功能之外,它还具备了权限控制等功能。Spring Cloud Netflix中的Zuul就担任了这样的一个角色,为微服务架构提供了前门保护的作用,同时将权限控制这些较重的非业务逻辑内容迁移到服务路由层面,使得服务集群主体能够具备更高的可复用性和可测试性。
在这里插入图片描述
在这里插入图片描述

快速入门

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

通过@EnableZuulProxy注解开启Zuul的功能:

@SpringBootApplication
@EnableZuulProxy // 开启网关功能
@EnableDiscoveryClient
public class ItcastZuulApplication {

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

配置映射规则,添加微服务路由时自动根据微服务名称确定微服务地址,

  • 默认情况下,一切服务的映射路径就是服务名本身。例如服务名为:service-provider,则默认的映射路径就 是:/service-provider/**

如请求路径为 localhost:10010/api/service-provider/findall
发现/api开头,而且跟着/service-provider ,那么发送/findAll给service-provider微服务

server:
  port: 10010
spring:
  application:
    name: leyou-gateway
eureka:
  client:
    service-url:
      defaultZone: http://localhost:10086/eureka #注册到eureka
    registry-fetch-interval-seconds: 10 #注册表获取间隔秒数
zuul:
  prefix: /api  # 路由前缀
  routes:
    service-provider: /service-provider/** #添加微服务路由
    #如/api/service-provider/findall
    #发现/api开头,而且跟着/service-provider ,发送/findAll给service-provider/

过滤器

Zuul作为网关的其中一个重要功能,就是实现请求的鉴权。而这个动作我们往往是通过Zuul提供的过滤器来实现的。

ZuulFilter是过滤器的顶级父类。在这里我们看一下其中定义的4个最重要的方法:

public abstract ZuulFilter implements IZuulFilter{
    abstract public String filterType();
    abstract public int filterOrder();
    boolean shouldFilter();// 来自IZuulFilter
    Object run() throws ZuulException;// IZuulFilter
}
  • shouldFilter:返回一个Boolean值,判断该过滤器是否需要执行。返回true执行,返回false不执行。
  • run:过滤器的具体业务逻辑。
  • filterType:返回字符串,代表过滤器的类型。包含以下4种:
    • pre:请求在被路由之前执行
    • route:在路由请求时调用
    • post:在route和errror过滤器之后调用
    • error:处理请求时发生错误调用
  • filterOrder:通过返回的int值来定义过滤器的执行顺序,数字越小优先级越高。

过滤器执行生命周期

官网图
在这里插入图片描述
正常流程:

  • 请求到达首先会经过pre类型过滤器,而后到达route类型,进行路由,请求就到达真正的服务提供者,执行请求,返回结果后,会到达post过滤器。而后返回响应。

异常流程:

  • 整个过程中,pre或者route过滤器出现异常,都会直接进入error过滤器,在error处理完毕后,会将请求交给POST过滤器,最后返回给用户。
  • 如果是error过滤器自己出现异常,最终也会进入POST过滤器,将最终结果返回给请求客户端。
  • 如果是POST过滤器出现异常,会跳转到error过滤器,但是与pre和route不同的是,请求不会再到达POST过滤器了。

场景非常多:

  • 请求鉴权:一般放在pre类型,如果发现没有访问权限,直接就拦截了
  • 异常处理:一般会在error类型和post类型过滤器中结合来处理。
  • 服务调用时长统计:pre和post结合使用。

自定义过滤器

自定义一个过滤器,模拟一个登录的校验。
基本逻辑:如果请求中有access-token参数,则认为请求有效,放行。

@Component
public class LoginFilter extends ZuulFilter {
    @Override
    public String filterType() {//过滤器类型,前置过滤器
        return "pre";
    }
    @Override
    public int filterOrder() {//过滤器的执行顺序
        return 1;
    }
    @Override
    public boolean shouldFilter() {//该过滤器是否生效
        return true;
    }
    @Override
    public Object run() throws ZuulException {//登陆校验逻辑
        // 获取zuul提供的上下文对象
        RequestContext context = RequestContext.getCurrentContext();
        // 从上下文对象中获取请求对象
        HttpServletRequest request = context.getRequest();
        // 获取token信息
        String token = request.getParameter("access-token");
        // 判断
        if (StringUtils.isBlank(token)) {
            // 过滤该请求,不对其进行路由
            context.setSendZuulResponse(false);
            // 设置响应状态码,401
            context.setResponseStatusCode(HttpStatus.SC_UNAUTHORIZED);
            // 设置响应信息
            context.setResponseBody("{\"status\":\"401\", \"text\":\"request error!\"}");
        }
        // 校验通过,把登陆信息放入上下文信息,继续向后执行
        context.set("token", token);
        return null;
    }
}

在这里插入图片描述
Zuul中默认就已经集成了Ribbon负载均衡和Hystix熔断机制。但是所有的超时策略都是走的默认值,比如熔断超时时间只有1S,很容易就触发了。因此建议我们手动进行配置:

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 6000 # 设置hystrix的超时时间为6000ms
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客
应支付0元
点击重新获取
扫码支付

支付成功即可阅读