SpringCloud之Hystrix服务降级入门全攻略

理论知识

  • Hystrix是什么?

    Hystrix是由Netflix开源的一个服务隔离组件,通过服务隔离来制止由于依赖延迟、异常,引起资源耗尽导致系统不可用的解决方案。这说的有点儿太官方了,它的功效主要有以下三个:

  • 服务降级

    ​ SpringCloud是通过HTTP Rest的方式在“微服务”之间举行挪用的,以是每一个“微服务”都是一个web项目。既然它是一个web项目,它就就有可能会发生错误,这个错误有可能是服务器内存不足、客户端传参错误、网络问题等,也有可能是人为的(这个就是服务熔断)。也就是说,会由于一些缘故原由从而不能给挪用者返回准确的信息。

    ​ 对于我们现在的单个SpringBoot项目来说,我们使用Ajax等一些方式挪用接口时,若是服务器发生错误,我们在前端就会对这个错误举行处置。有可能是重试挪用接口,或者给用户一个友好的提醒,好比“服务忙碌,稍后再试”啥的。

    ​ 然则在分布式系统中,同样也会发生一些“错误”,而且在多个服务之间挪用时,若是不能对这些“错误”举行友好的处置,就会导致我们整个项目瘫痪,这是万万不能发生的。以是Hystrix行使服务降级来很好的解决了这个问题。这个实在就类似于我们的try-catch这样的机制,发生错误了,我就执行catch中的代码。

    ​ 通过服务降级,能保证在某个或某些服务出问题的时间,不会导致整个项目出现问题,制止级联故障,从而来提高分布式系统的弹性。

  • 服务熔断

    ​ 建设先看下边的服务降级代码,将整个服务降级的代码部门所有看完,再来看下边这段理论,你一定会茅塞顿开的。

    ​ Hystrix意为“断路器”,就和我们生涯中的保险丝,开关一个原理。

    ​ 当我们给整个服务设置了服务降级后,若是服务提供者发生了错误后,就会挪用降级后的方式来保证程序的运行。然则呢?有一个问题,挪用者并不知道它挪用的这个服务出错了,就会在营业发生的时刻一直挪用,然后服务会一直报错,然后去挪用降级方式。好比下图中:

    SpringCloud之Hystrix服务降级入门全攻略

    ​ 它们的对话如下:

    Client:我要挪用你的方式A

    Server:不行,我报错了。你挪用降级方式吧,你的我的都行!

    Client:哎呀,服务器报错了,那我就挪用一下降级方式吧。

    ​ 过了一会儿。。。。。。

    Client:我要挪用你的方式A

    Server:适才不是说了吗?我报错了。你挪用降级方式吧,你的我的都行!

    Client:哎呀,服务器报错了,那我就挪用一下降级方式吧。

    ​ 又过了一会儿。。。。。。

    Client:我要挪用你的方式A

    Cinemachine中噪音的应用

    Server:没完了是吧?我说过我报错了,你去挪用这个降级方式啊。非要让我的代码又运行一次?

以上的对话说明晰一个问题,当服务端发生了错误后,客户端会挪用降级方式。然则,当有一个新的接见时,客户端会一直挪用服务端,让服务端运行一些明知会报错的代码。这能不能制止啊,我知道我错了,你接见我的时刻,就直接去接见降级方式,不要再让我执行错的代码。

​ 这就是服务熔断,就好比我们家中的保险丝。当检测到家中的用电负荷过大时,就断开一些用电器,来保证其他的可用。在分布式系统中,就是挪用一个系统时,在一定时间内,这个服务发生的错误次数到达一定的值时, 我们就打开这个断路器,不让挪用已往,而是让他直接去挪用降级方式。再过一段时间后,当一次挪用时,发现这个服务通了,就将这个断路器改为“半开”状态,让挪用一个一个的逐步已往,若是一直没有发生错误,就将这个断路器关闭,让所有的服务所有通过。

  • 服务限流

    ​ 服务限流就容易明白多了,顾名思义,这是对接见的流量举行限制,就好比上边的场景,我们还可能通过服务限流的方式来解决高并发以及秒杀等问题。主流的限流算法主要有:漏桶算法令牌算法

最先码代码吧

​ 不贴代码,说这么多有什么用?这不是耍流氓吗?

  • 先建立一个我们需要的几个项目:

    模块名称 代码中项目名称 备注
    Eureka注册中央 eureka-alone-7000 测试时代,使用一个注册中央而不是集群
    客户端(消费者,服务挪用者) hystrix-consumer-80 使用Feign或OpenFeign举行服务挪用
    服务端(提供者,服务提供者) hystrix-provider-8001

    ​ 这三个项目的建立代码略(项目代码地址

  • 在客户端和服务端都加入Hystrix的依赖(当然是在哪端举行服务降级就在哪端使用)
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
  • 服务降级

    服务降级有两种解决思绪:可以分别从服务挪用者和服务提供者举行服务降级,也就是举行错误的“兜底”

1. 从服务提供者方举行服务降级

我们先在提供者方的下列方式模拟一个“响应超时错误”。

  /**
     * 这个方式会造成服务挪用超时的错误
     * 实在本身体不是错误,而是服务响应时间超过了我们要求的时间,就以为它错了
     * @param id
     * @return
     */
    public String timeOutError(Integer id){
        return "服务挪用超时";
    }

我们就给它界说一个错误回调方式,加上如下注解:

/**
* 这个方式会造成服务挪用超时的错误
* 实在本身体不是错误,而是服务响应时间超过了我们要求的时间,就以为它错了
* @param id
* @return
*/
@HystrixCommand(fallbackMethod = "TimeOutErrorHandler",commandProperties = {
    @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "3000")
})
public String timeOutError(Integer id){
    try {
        //我们让这个方式休眠5秒,以是一定会发生错误,也就会挪用下边的fallbakcMethod方式
        TimeUnit.SECONDS.sleep(5);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "服务正常挪用"+id;
}

/**
*   这个就是当上边方式的“兜底”方式
*/
public String TimeOutErrorHandler(Integer id) {
    return "对不起,系统处置超时"+id;
}

上边这个注解要注重三点:

  1. fallCallbackMethod中的这个参数就是“兜底”方式
  2. fallCallbackMethod中的这个方式的声明要和本方式一致
  3. commandProperties属性中可以写多个@HystrixProperty注解,其中的name和value就是设置对应的属性,上例中的这个就是设置响应超时

最后在主启动类上加上这个注解

@SpringBootApplication
@EnableEurekaClient //本服务启动后会自动注册进eureka服务中
@EnableCircuitBreaker
public class ProviderAppication_8001 {
    public static void main(String[] args) {
        SpringApplication.run(ProviderAppication_8001.class, args);
    }
}

这个我们是在服务提供者方面举行的错误处置,以是对服务挪用者不做任何处置,启动三个项目(consumer,provder,eureka)。然后接见http://localhost/consumer/hello/999,理论上是要返回服务挪用正常999,然则呢,由于我们人为造成了超时错误,以是就一定会返回fallback中的对不起,系统处置超时999,而且这个返回是会在3秒后。

SpringCloud之Hystrix服务降级入门全攻略

​ 若是你以为上边这个超时的错误演示很贫苦,可以直接在方式中写一个运行时错误,好比:int i = 10/0;也会举行fallbackMethod的挪用。之以是要用这个超时设置,就是为了让你知道Hystrix可以对什么样的错误举行fallback,它的更多设置参考https://github.com/Netflix/Hystrix/wiki/Configuration

2.从服务提供者方举行服务降级

和在服务提供方举行服务降级相比,在服务挪用方(客户端、消费者)举行服务降级是更常用的方式。这两者相比,前者是要让服务提供者对自己可发生的错误举行“预处置”,这样,一定要保证挪用者接见到我才会挪用这个“兜底”方式。然则,人人想一下,若是我这个服务宕机了呢?客户端基本就挪用不到我,它怎么可能吸收到我的“兜底”方式呢?以是,在客户端举行服务降级是更常用的方式。

一个小疑问,若是我在客户端和服务端都举行了服务降级,是都市挪用?照样先挪用哪个?自己想喽,稍微动动你伶俐的小脑壳。

  • 为了反面上一个项目的代码冲突,我将上边这个@Service给注掉(也就是让Spring来治理它),从而用另外一个接口的实现,下边是我们新的serive类
@Service
public class OrignService implements IExampleService {
    /**
     * 不用这个做演示,就空实现
     */
    @Override
    public String timeOutError(Integer id) {
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "服务正常挪用"+id;
    }
    /**
     * 不发生错误的准确方式
     */
    @Override
    public String correct(Integer id) {
        return "接见正常,服务端没有举行任何错误"+id;
    }
}
  • 在主启动类上添加如下注解:
@EnableHystrix //注重这个和服务端的注解是不一样的
  • 在application.yml中开户feign对Hystrix的支持
feign:
    hystrix:
        enabled:true
  • 将之前在provider项目中的@HystrixCommond放在feign的接口中

SpringCloud之Hystrix服务降级入门全攻略

3.改善下解决方案
  • 以上的两种方案看似可行, 然则,现实呢?心想,这是一个及格程序员应该做的事吗?每个接口我们都要写一个fallback方式?然后和我们的营业代码要写在一起?就好的“低耦合,高内聚”呢?
  • 第一种解决方案,就是使用@DefaultProperties在整个Controller类上,顾名思义,就是给它一个默认的“兜底”方式,就不用每一个需要降级的方式举行设置fallbackMethod了,我们只需要加上@HystrixCommand好了。这个方式太过简朴,不做代码演示,在文末的代码中专门写了注释
  • 第二种解决方式:我们在客户端不是通过Feign挪用的吗?是有一个Feign的内陆接口类,我们直接对这个类举行设置就好了。直接上代码。
@Component
//@FeignClient(value = "hystrix-provider") //这是之前的挪用
@FeignClient(value = "hystrix-provider",fallback = ProviderServiceImpl.class) //这回使用了Hystrix的服务降级
public interface IProviderService {
    @GetMapping("provider/hello/{id}")
    public String hello(@PathVariable("id") Integer id);
}
@Component
public class ProviderServiceImpl implements IProviderService {
    @Override
    public String hello(Integer id) {
        return "挪用远程服务错误了";
    }
}
  • 以上两种方式的对比:
    • 第一种和我们的营业类举行了耦合,而且若是要对每个方式举行fallback,就要多写一个方式,代码太过臃肿。然则,它提供了一个DefaultProperties注解,可以提供默认的方式,这个后者是没有的。这种方式适合直接使用Ribbon连系RestTemplate举行挪用的方式
    • 第二种提供了一个Feign接口的实现类来处置服务降级问题,将所有的fallback方式写到了一起,和我们的营业代码完全解耦了。对比第一个,我们可以界说一个统一的方式来实现DefalutPropeties。这种方式适合Feign作为客户端的挪用,对照推荐这种。

服务熔断

​ 请再回去看一下上边的关于服务熔断的理论知识,我相信你一定能看懂。当启用服务降级时,会默认启用服务熔断机制,我们只需要对一些参数举行设置就可以了,就是在上边的@HystrixCommand中的一些属性,好比:

@HystrixCommand(fallbackMethod = "TimeOutErrorHandler",commandProperties = {
    @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "3000"),
    @HystrixProperty(name="circuitBreaker.enabled",value="true"),//开户断路器
    @HystrixProperty(name="circuitBreaker.requestVolumeThreshold",value="20"),//请求次数的峰值
    @HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds",value="10000"),//检测错误次数的时间局限
    @HystrixProperty(name="circuitBreaker.errorThresholdPercentage",value="60")//请求失败率到达若干比例后会打开断路器
})

这些设置可以在https://github.com/Netflix/Hystrix/wiki/Configuration领会,也可以打开查看HystrixCommandProperties类中的属性(使用idea一搜索就有),所有都有默认设置

服务限流

器械太多,关注我期待后续。

项目代码和更多的学习地址

关注微信公从号“小鱼与Java”,回复“SpringCloud”获取
SpringCloud之Hystrix服务降级入门全攻略

原创文章,作者:admin,如若转载,请注明出处:https://www.2lxm.com/archives/1892.html