spring cloud 入门12-Eureka断路器——Hystrix

在我们的cloud中,各个节点之间相互配合、互相支持,大家齐心协力完成工作,但是天佑不测风云,可能存在某一个微服务的某个时刻压力变大导致服务缓慢,甚至出现故障,导致服务不能响应,例如:我们的网站推出新的营销方案,注册即得200元现金,这个时候注册这个节点出现压力过大,服务响应速度变缓,进入瘫痪状态。

而这个时候产品微服务响应还是正常响应。但是如果出现产品微服务大量调用用户微服务,就会出现大量的等待,如果还是持续地调用,则会造成大量请求的积压,导致产品微服务最终也不可用

总结来说:如果一个服务不可用,而其他微服务还大量地调用这个不可用的微服务,也会导致其自身不可用,其自身不可用之后又可能继续蔓延到其他与之相关的微服务上,这样就会使得更多的微服务不可用,最终导致分布式服务瘫痪

那么怎么防止这种情况的发生呢?

Hystrix-断路器就是我们正确的选择了,断路器就如同电路中的保险丝,如果电器耗电大,导致电流过大,那么保险丝就会熔断,从而保证用电的安全。同样地,在微服务系统之间大量调用可能导致服务消费者自身出现瘫痪的情况下,断路器就会将这些积压的大量请求“熔断”,来保证其自身服务可用,而不会蔓延到其他微服务系统上。通过这样的断路机制可以保持各个微服务持续可用。

在Spring Cloud中断路器是由NetFlix的Hystrix实现的,它默认监控微服务之间的调用超时时间为2000 ms(2 s),如果超过这个超时时间,它就会根据你的配置使用其他方法进行响应。

我们做第一次实验,还在在user节点中,修改之前能正常调用的getUserById方法
在这里插入图片描述
新增了这段方法,就是让我们的程序休眠很长的时候以后在返回相应的结果值

 @GetMapping("/user/{id}")
    public User getUserById(@PathVariable("id")long id){
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        User user=new User();
        user.setUserId(id);
        user.setUserName("user1");
        user.setUserPwd("admin");
        return user;
    }

再观测product节点中相应的调用
在这里插入图片描述
我们这里使用Feign访问user节点下边的 @GetMapping("/user/{id}")这个方法
在浏览器中访问结果如下

我们看product后台打印的错误信息
在这里插入图片描述
我们发现,并不是程序有错误(在没加入睡眠代码之前,这个程序是能正常运行的),发生的错误是Read timed out错误(访问超时)

这里没做任何操作,是因为ribbon有个默认的超时机制(好像是1.5S什么的,记不清了),但是有时候我们的服务本身就需要容许一点超时,我们可以自己配置一下,在product节点的配置文件中添加如下配置

ribbon:
  ReadTimeout: 6000
  ConnectTimeout: 3000

(idea打死没有提示,直接放上去就好了)
这样我们配置了ReadTimeout: 6000为6s,然后在user节点中是睡眠5s,再次测试,经历很长时间等待之后(大概5s之后),我们得到了我们的需求结果
在这里插入图片描述
这样可以避免了ribbon默认超时的困扰(特殊情况比如容许3s的情况也有)
我们继续修改为:

ribbon:
  ReadTimeout: 3000
  ConnectTimeout: 3000

超过3s就真的有点过分了,所以设置为3s
然后在项目中引入我们的断路器—Hystrix

第一步:引入pom文件

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

第二步:在你的spring cloud项目中启用Hystrix
启用的方式非常简单,就是在你的main入口文件处加入这样一句配置

@EnableCircuitBreaker

在这里插入图片描述
第三部:在需要降级服务的方法位置制定相应的规则
我们现在设置全局ribbon超时时间是3000ms,而Hystrix的默认超时时间是1000ms所以我们可以直接配置一下:
在这里插入图片描述
在getUser方法上边标记位置设置

@HystrixCommand(fallbackMethod = “error”)

意思就是说,在断路情况发生的时候,麻烦去找error方法处理断路情况
代码如下

 // 测试
    @GetMapping("product/getUser/{id}")
    @HystrixCommand(fallbackMethod = "error")
    public User getUser(@PathVariable int id) {
        User user = service.getUser(id);
        return user;
    }

PS:这里请忽略返回值,一般情况下我们不会直接返回对象,而应该是封装的response信息,里边包含data、status等信息,这里为了做实验直接写的返回user

然后我们这里规定了断路情况下去找error方法处理,所以我们先写一个error方法

 /**
     * 降级服务调用方法
     * @return
     */
    public String error() {
        return "超时";
    }

然后重启,运行服务,并且访问该方法,然后结果报错,错误信息如下:
在这里插入图片描述
这里的错误原因是我们做实验引发的必要错误,原因是我们的getUser这个方法,参数是public User getUser(@PathVariable int id) { 但是我们的error方法没有任何参数,这里需要两边保持一样的参数,我们修改为
在这里插入图片描述
然后再次执行,依旧报错:
在这里插入图片描述
重点是这句话:

com.bigsoft.bigsoftproduct.controller.UserController.error(int)’ must
return: class com.bigsoft.bigsoftproduct.pojo.User or its subclass

也就是error方法的返回值也必须和getUser方法保持一直(再次说,一般返回值我们会统一封装的,这里做实验随意写的返回值出错了)
这里强行让返回值统一一下
在这里插入图片描述
这里强行返回了和getUser方法一直的(为了做实验),再次执行
这里我们user节点提供的服务睡眠了5000s,而Hystrix默认1000ms会启用断路,所以实验结果如下
在这里插入图片描述
这里返回到了error方法里边返回的空的User,证明实验完成了。

这里我们的Hystrix默认的断路时间是1000ms,我们也可以对这个时间做修改,即认为设置段榕时间(根据我们的服务器的实际工作性能来设置),设置方式有两种,一种是注解的方式,注解方式如下:

 // 测试
    @GetMapping("product/getUser/{id}")
    @HystrixCommand(fallbackMethod = "error",
            commandProperties = { //设置属性
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3200")
            })
    public User getUser(@PathVariable int id) {
        User user = service.getUser(id);
        return user;
    }

这里我们在注解中设置了断熔属性,设置断熔时间为3200ms
第二种方式是配置的方式,代码如下,在我们配置文件appliocation.yml文件中:

hystrix:
  command:
    default:
      execution:
        timeout:
          #如果enabled设置为false,则请求超时交给ribbon控制,为true,则超时作为熔断根据
          enabled: true
        isolation:
          thread:
            timeoutInMilliseconds: 5000 #断路器超时时间,默认1000ms

这里设置timeoutInMilliseconds的时间为5000ms(当两种方式同时设置的时候,注解方式有限)

特别注意:
这里有个小坑,就是当你单独使用Hystrix的时候上边的代码已经可以生效了,但是当我们fegin+Hystrix一起的时候,我们会发现上述代码失效,原因就是这里一共同时有两个超时时间,一为Hystrix的超时时间,二为fegin的超时时间

那他们俩以哪个为主主要是看上述文件中我们配置的

enabled: true

这个配置了,原则如下:

如果hystrix.command.default.execution.timeout.enabled为true,则会有两个执行方法超时的配置,一个就是ribbon的ReadTimeout,一个就是熔断器hystrix的timeoutInMilliseconds, 此时谁的值小谁生效
如果hystrix.command.default.execution.timeout.enabled为false,则熔断器不进行超时熔断,而是根据ribbon的ReadTimeout抛出的异常而熔断,也就是取决于ribbon

所以我们配置的最终代码如下:

ribbon:
  OkToRetryOnAllOperations: false #对所有操作请求都进行重试,默认false
  ReadTimeout: 3000   #负载均衡超时时间
  ConnectTimeout: 2000 #ribbon请求连接的超时时间
  MaxAutoRetries: 0     #对当前实例的重试次数,默认0
  MaxAutoRetriesNextServer: 1 #对切换实例的重试次数,默认1

hystrix:
  command:
    default:
      execution:
        timeout:
                   #如果enabled设置为false,则请求超时交给ribbon控制,为true,则超时作为熔断根据(两个值一起生效,谁的值小谁生效)
          enabled: true

代码中配置如下:

 // 测试
    @GetMapping("product/getUser/{id}")
    @HystrixCommand(fallbackMethod = "error",
            commandProperties = {
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3200")
            })
    public User getUser(@PathVariable int id) {
        User user = service.getUser(id);
        return user;
    }

在我们user节点代码如下

  @GetMapping("/user/{id}")
    public User getUserById(@PathVariable("id")long id){
        try {
            Thread.sleep(2500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        User user=new User();
        user.setUserId(id);
        user.setUserName("user1");
        user.setUserPwd("admin");
        return user;
    }

现在我们的配置就是user节点休眠2500ms做出响应,而我们的projuct节点在3200ms的时候才出发熔断,我们来测试,根据刚才的结论,我们现在说过当设置hystrix.command.default.execution.timeout.enabled为true时候,ribbon的timneout和execution.isolation.thread.timeoutInMilliseconds这两个值一起生效,而且谁的值小谁生效,这里ribbon的值为3000,理应ribbon的值生效
所以我们看实验1:(ribbon3000,Hystrix3200,服务延时2500)
在这里插入图片描述
发现正常执行,然后我们进行实验2:
在这里插入图片描述
设置Hystrix的时间为2600ms
在这里插入图片描述
设置ribbon的超时时间为2000ms
在这里插入图片描述
user节点延时时间为2500ms
实验结果如下:
在这里插入图片描述
我们发现被熔断了,这符合我们上述的规律,当我们同时设置ribbon超时时间和Hystrix的超时时间,hystrix.command.default.execution.timeout.enabled为true时候谁的超时时间短算谁的

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章