1 分布式系统面临的问题

  • 复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免的失败。

2 服务雪崩

图片说明

  • 多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其他的微服务,这就是所谓的“扇出”。
  • 如果“扇出”的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,即所谓的”雪崩效应”。
  • 对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒内饱和。
  • 比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列、线程和其他系统资源紧张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统。
  • 我们要“弃车保帅”。

3 什么是Hystrix

图片说明

  • Hystrix是一个用于处理分布式系统的延迟和容错的开源库。
  • 在分布式系统里,许多依赖不可避免地会调用失败(如超时、异常等)。Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
  • “断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个服务预期的,可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方法无法处理的异常,这样就可以保证服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。

4 Hystrix能干什么?

  • 服务降级。
  • 服务熔断。
  • 服务限流。
  • 接近实时的监控。
  • ……

5 服务熔断

  • 熔断机制是对应雪崩效应的一种微服务链路保护机制。
  • 当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。当检查到该节点微服务调用响应正常后恢复调用链路。
  • 在SpringCloud里熔断机制通过Hystrix实现。
  • Hystrix会监控微服务间调用的状况,当失败的调用达到一定阈值(缺省是5秒内20次调用失败),就会启动熔断机制。
  • 熔断机制的注解是@HystrixCommand。

6 服务熔断实例

1、新建module

图片说明

2、导入依赖

    <!--Hystrix-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-hystrix</artifactId>
        <version>1.4.7.RELEASE</version>
    </dependency>

3、HystrixDeptProvider_8001(开启支持)

    package com.xianhuii.springcloud;

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

    @EnableCircuitBreaker   // 添加对熔断的支持
    @EnableDiscoveryClient  // 服务发现
    @EnableEurekaClient // 在服务启动后自动注册到Eureka中
    @SpringBootApplication
    public class HystrixDeptProvider_8001 {
        public static void main(String[] args) {
            SpringApplication.run(HystrixDeptProvider_8001.class, args);
        }
    }

4、DeptController(使用)

    package com.xianhuii.springcloud.controller;

    import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
    import com.xianhuii.springcloud.pojo.Dept;
    import com.xianhuii.springcloud.service.DeptService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cloud.client.ServiceInstance;
    import org.springframework.cloud.client.discovery.DiscoveryClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RestController;

    import java.util.List;

    /**
     * 提供RESTful服务
     */
    @RestController
    public class DeptController {
        @Autowired
        private DeptService deptService;

        @HystrixCommand(fallbackMethod = "hystrixGet")
        @GetMapping("/dept/get/{id}")
        public Dept getDept(@PathVariable("id") Long id) {
            Dept dept = deptService.queryById(id);
            if (dept == null) {
                throw new RuntimeException("id => " + id + ",不存在该用户,或者信息无法找到!");
            }
            return dept;
        }

        // 备选方法
        public Dept hystrixGet(@PathVariable("id") Long id) {
            Dept dept = new Dept();
            dept.setDeptno(id);
            dept.setDname("id => " + id + "没有对应的信息,null");
            dept.setdbSource("no this database in MySQL");
            return dept;
        }
    }

7 服务降级实例

1、springcloud-api:DeptClientServiceFallbackFactory

    package com.xianhuii.springcloud.service;

    import com.xianhuii.springcloud.pojo.Dept;
    import feign.hystrix.FallbackFactory;
    import org.springframework.stereotype.Component;

    import java.util.List;

    // 降级
    @Component
    public class DeptClientServiceFallbackFactory implements FallbackFactory {
        @Override
        public DeptClientService create(Throwable throwable) {
            return new DeptClientService() {
                @Override
                public Dept queryById(Long id) {
                    Dept dept = new Dept();
                    dept.setDeptno(id);
                    dept.setDname("服务降级信息");
                    return dept;
                }

                @Override
                public List<Dept> queryAll() {
                    return null;
                }

                @Override
                public Boolean addDept(Dept dept) {
                    return null;
                }
            };
        }
    }

2、DeptClientService

    package com.xianhuii.springcloud.service;

    import com.xianhuii.springcloud.pojo.Dept;
    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;
    import org.springframework.web.bind.annotation.PostMapping;

    import java.util.List;

    @Component
    @FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT", fallbackFactory = DeptClientServiceFallbackFactory.class)
    public interface DeptClientService {
        @GetMapping("/dept/get/{id}")
        Dept queryById(@PathVariable("id") Long id);

        @GetMapping("/dept/list")
        List<Dept> queryAll();

        @PostMapping("/dept/add")
        Boolean addDept(Dept dept);
    }

8 Dashboard流监控

1、新建module

图片说明

2、依赖

    <!--Hystrix-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-hystrix</artifactId>
        <version>1.4.7.RELEASE</version>
    </dependency>
    <!--dashboard-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
        <version>1.4.7.RELEASE</version>
    </dependency>

3、application.yaml

    server:
      port: 9001

4、启动类

    package com.xianhuii.springcloud;

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;

    @EnableHystrixDashboard // 开启Dashboard
    @SpringBootApplication
    public class DeptConsumerDashboard_9001 {
        public static void main(String[] args) {
            SpringApplication.run(DeptConsumerDashboard_9001.class, args);
        }
    }

5、springcloud-provider-dept-8001:pom.xml

    <!--Hystrix-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-hystrix</artifactId>
        <version>1.4.7.RELEASE</version>
    </dependency>
    <!--完善监控信息-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

6、springcloud-provider-dept-8001:DeptProvider_8001(增加Servlet)

    package com.xianhuii.springcloud;

    import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.web.servlet.ServletRegistrationBean;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    import org.springframework.context.annotation.Bean;

    import javax.servlet.ServletRegistration;

    @EnableDiscoveryClient  // 服务发现
    @EnableEurekaClient // 在服务启动后自动注册到Eureka中
    @SpringBootApplication
    public class DeptProvider_8001 {
        public static void main(String[] args) {
            SpringApplication.run(DeptProvider_8001.class, args);
        }

        // 增加一个Servlet
        @Bean
        public ServletRegistrationBean hystrixMetricsStreamServlet() {
            ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
            registrationBean.addUrlMappings("/actuator/hystrix.stream");
            return registrationBean;
        }
    }