SpringCloud入门学习之Hystrix

前言

复杂分布式体系结构中的微服务存在着依赖关系,每个依赖关系在某些时候将不可避免的出现差错,将会导致服务调用者被阻塞,进而导致整个分布式系统面临瘫痪的风险,我们称这种问题叫做服务雪崩。

服务雪崩

服务雪崩的产生过程

服务雪崩效应是一种因服务提供者的不可用导致服务调用者的不可用,并将不可用 逐渐放大的过程,如果所示:

正常情况下,服务调用者发送请求,经过多个服务的传递到达服务F,服务F处理完毕后然后逐级返回,最终服务调用者拿到结果。

当服务E因为某种原因导致不可用时,整个服务调用调用过程会进行阻塞状态,但是服务调用者不知道该问题,会不断重试服务调用。

因为服务调用者的请求量多大,导致服务E压力过大,进而导致各级服务都会崩溃,最后服务调用者也将变为不可用。

服务雪崩的原因
  • 流量激增:比如异常流量、用户重试导致系统负载升高;
  • 缓存击穿:假设A为client端,B为Server端,假设A系统请求都流向B系统,请求超出了B系统的承载能力,就会造成B系统崩溃;
  • 程序Bug:代码循环调用的逻辑问题,资源未释放引起的内存泄漏等问题;
  • 硬件故障:比如宕机,机房断电,光纤被挖断等。
  • 线程同步等待:系统间经常采用同步服务调用模式,核心服务和非核心服务共用一个线程池和消息队列。如果一个核心业务线程调用非核心线程,这个非核心线程交由第三方系统完成,当第三方系统本身出现问题,导致核心线程阻塞,一直处于等待状态,而进程间的调用是有超时限制的。大量的等待线程会占用系统资源,一旦系统资源被耗尽,服务将会转为不可用状态,从而引发服务雪崩。

通过实践发现,线程同步等待是最常见引发的雪崩效应的场景。

服务雪崩的应对策略

针对上述雪崩产生的场景,有很多应对方案,但没有一个万能的模式能够应对所有场景。

  1. 流量控制
    • 网关限流。因为 Nginx 的高性能, 目前一线互联网公司大量采用 Nginx+Lua 的网关进行流量控制, 由此而来的 OpenResty 也越来越热门.
    • 用户交互限流。用户交互限流的具体措施有:1、 采用加载动画,提高用户的忍耐等待时间.;2、提交按钮添加强制等待时间机制。
    • 关闭重试
  2. 改进缓存模式
    • 缓存预加载
    • 同步改为异步刷新
  3. 服务自动扩容
    • AWS的auto scaling
  4. 针对硬件故障,多机房容灾,跨机房路由,异地多活等。
  5. 针对同步等待,使用 Hystrix 做故障隔离,熔断器机制等可以解决依赖服务不可用的问题。

通过实践发现,线程同步等待是最常见引发的雪崩效应的场景,本文将重点介绍使用 Hystrix 技术解决服务的雪崩问题。

简介

什么是Hystrix?

在分布式环境中,不可避免地会有许多服务依赖项中的某些失败。Hystrix是一个库,可通过添加延迟容限和容错逻辑来帮助您控制这些分布式服务之间的交互。Hystrix通过隔离服务之间的访问点,停止服务之间的级联故障并提供后备选项来实现此目的,所有这些都可以提高系统的整体弹性。

Github 地址: https://github.com/Netflix/Hystrix/wiki

Hystrix的工作原理

  • 防止任何单个依赖项耗尽所有容器(例如Tomcat)用户线程。
  • 减少负载并快速失败,而不是排队。
  • 在可行的情况下提供备用,以保护用户免受故障的影响。
  • 使用隔离技术(例如隔板,泳道和断路器模式)来限制任何一种依赖关系的影响。
  • 通过近实时指标,监视和警报优化发现时间
  • 通过在Hystrix的大多数方面中以低延迟传播配置更改来优化恢复时间,并支持动态属性更改,这使您可以通过低延迟反馈环路进行实时操作修改。
  • 防止整个依赖项客户端执行失败,而不仅仅是网络流量失败。

如何通过 Hystrix 解决服务雪崩?

降级

超时降级、资源不足时(线程或信号量)降级,降级后可以配合降级接口返回托底数据

实现一个 fallback 方法,当请求后端服务出现异常的时候,可以使用 fallback 方法返回值。

隔离(线程池隔离和信号量隔离)

限制调用分布式服务的资源使用,某一个调用的服务出现问题不会影响其他服务调用。

熔断

当失败率(网络故障、超时造成的失败率)达到阈值自动触发降级,熔断器触发的快速失败会进行快速恢复 。

请求合并

对于高并发情况下的多次请求合并为一个请求。

测试

基于Ribbon实现的负载均衡

降级

降级是针对客户端的操作,即我们对 springcloud-consumer-dept-80 项目进行修改。

1、导入 hystrix 依赖

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

2、修改 DeptConsumerController

@RestController
public class DeptConsumerController {

    @Autowired
    RestTemplate restTemplate;

//    private static final String REST_URL_PREFIX = "http://localhost:8001";
    //通过服务名进行调用
    private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT";

    @HystrixCommand(fallbackMethod = "getDeptHystrix")
    @RequestMapping("/consumer/dept/get/{id}")
    public Dept getDept(@PathVariable("id") long id) {
        Dept dept = restTemplate.getForObject(REST_URL_PREFIX+"/dept/get/"+id, Dept.class);
        return dept;
    }

    public Dept getDeptHystrix(long id){
        return new Dept().setDeptId(id).
                setDpName("id="+id+"=>没有对应的信息,null").
                setDbSource("no database in MySQL");
    }

    @RequestMapping("/consumer/dept/list")
    public List<Dept> queryAll(){
        return restTemplate.getForObject(REST_URL_PREFIX+"/dept/list",List.class);
    }

    @RequestMapping(name = "/consumer/dept/add")
    public boolean addDept(Dept dept){
        return restTemplate.postForObject(REST_URL_PREFIX+"/dept/add",dept,Boolean.class);
    }
}

3、改回轮询策略

@Configuration
public class RuleConfig {

    @Bean
    public IRule getRule(){
        //仅当测试自定义负载均衡策略时使用
//        return new MyRandomRule();
        return new RoundRobinRule();
    }
}

4、入口类增加注解

@SpringBootApplication
@EnableEurekaClient
//在微服务启动时去加载我们自定义的负载均衡策略
@RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT",configuration = RuleConfig.class)
@EnableCircuitBreaker
public class DeptConsumer_80 {

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

5、启动一个注册中心,3个服务提供者,以及本项目服务消费者,然后访问 http://localhost/consumer/dept/get/1,不断刷新可以看到,页面会挨个访问3个服务提供者的数据,假设此时我们关闭 8001 端口的服务提供者,然后继续刷新访问该网址,页面内容效果如下:

从结果可知,当轮询到第一个服务提供者,即 8001 端口代表的服务时,由于该端口被我们停止了,导致服务不可访问,所以才会返回我们在客户端代码中定义的服务降级后的结果。

隔离(线程池隔离)

我们可以用蓄水池做比喻来解释什么是资源隔离。生活中一个大的蓄水池由一个一个小的池子隔离开来,这样如果某一个水池的水被污染,也不会波及到其它蓄水池,如果只有一个蓄水池,水池被污染,整池水都不可用了。软件资源隔离如出一辙,如果采用资源隔离模式,将对远程服务的调用隔离到一个单独的线程池后,若服务提供者不可用,那么受到影响的只会是这个独立的线程池。

img

img

img

隔离是针对服务提供者 Provider 做的修改,我们根据 springcloud-provider-dept-8001 项目复制新增一个名为 springcloud-provider-dept-threadpool-8001 的项目,然后修改新项目。

1、导入 hystrix 依赖

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

2、修改 DeptServiceImpl

@Service
public class DeptServiceImpl implements DeptService {

    @Autowired
    DeptMapper deptMapper;

    @Override
    public boolean addDept(Dept dept) {
        return deptMapper.addDept(dept);
    }


    @HystrixCommand(groupKey = "ego-dept-provider",
            commandKey = "queryDept",
            threadPoolKey = "ego-dept-provider", //给线程名添加前缀
            threadPoolProperties = {
                    @HystrixProperty(name = "coreSize", value = "30"),//线程池大小
                    @HystrixProperty(name = "maxQueueSize", value = "100"),//最大队列长度
                    @HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),//线程存活时间
                    @HystrixProperty(name = "queueSizeRejectionThreshold", value = "15")//拒绝请求
            },
            fallbackMethod = "getDeptHystrix")
    @Override
    public Dept queryDept(long id) {
        return deptMapper.queryDept(id);
    }

    public Dept getDeptHystrix(long id){
        return new Dept().setDeptId(id).
                setDpName("id="+id+"=>没有对应的信息,null").
                setDbSource("no database in MySQL");
    }

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

线程池隔离参数详解:

img

3、DeptController

@RestController
public class DeptController {

    @Autowired
    DeptService deptService;

    @GetMapping("/dept/get/{id}")
    public Dept getDept(@PathVariable("id") long id) {
        Dept dept = deptService.queryDept(id);
        return dept;
    }

    @GetMapping("/dept/list")
    public List<Dept> queryAll(){
        List<Dept> list = deptService.queryAll();
        return list;
    }

    @PostMapping("/dept/add")
    public boolean addDept(@RequestBody Dept dept){
        System.out.println(dept);
        return deptService.addDept(dept);
    }
}

4、入口类增加@EnableCircuitBreaker 注解

@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
public class DeptProviderThreadPool_8001 {

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

5、模拟测试

我在本地使用 Jmeter 模拟高并发,虽然瞬间高并发会造成数据的错误返回,但是没有导致服务不可用,所以实验场景可能还有待考量。如果有大神知道该如何模拟测试,望不吝赐教。

隔离(信号隔离)

使用一个原子计数器来记录当前有多少个线程在运行,当请求来临时,先判断计数器的数值,若超过设置的最大线程个数则丢弃该类型的新请求,若不超过则执行计数操作,请求来计数器+1,请求返回计数器-1。这种方式是严格的控制线程且立即返回模式,无法应对突发流量(流量洪峰来临时,处理的线程超过数量,其他的请求会直接返回,不继续去请求依赖的服务),参考Java的信号量的用法。

Hystrix 默认采用线程池隔离机制,当然用户也可以配置 HystrixCommandProperties 为隔离策略为ExecutionIsolationStrategy.SEMAPHORE。

信号隔离的特点:

  • 信号隔离与线程隔离最大不同在于执行依赖代码的线程依然是请求线程,该线程需要通过信号申请;
  • 如果客户端是可信的且可以快速返回,可以使用信号隔离替换线程隔离,降低开销。

img

关于信号隔离的代码书写与线程池隔离的写法很相似,由于不知道怎么测试,所以就不做代码的展示。

熔断

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

熔断机制是针对服务提供者 Provider 做的修改,我们根据 springcloud-provider-dept-8001 项目复制新增一个名为 springcloud-provider-dept-hystrix-8001 的项目,然后修改新项目。

1、导入 hystrix 依赖

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

2、修改 DeptServiceImpl

@Service
public class DeptServiceImpl implements DeptService {

    @Autowired
    DeptMapper deptMapper;

    @Override
    public boolean addDept(Dept dept) {
        return deptMapper.addDept(dept);
    }

    @HystrixCommand(fallbackMethod = "getDeptHystrix",
                    commandProperties = {
                        //默认 20 个;10s 内请求数大于 20 个时就启动熔断器,当请求符合熔断条件时将触发 getFallback()。
                        @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD,
                                         value = "10"),
                        //请求错误率大于 50%时就熔断,然后 for 循环发起请求,当请求符合熔断条件时将触发 getFallback()。
                        @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE,
                                         value = "50"),
                        //默认 5 秒;熔断多少秒后去尝试请求
                        @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS,
                                         value = "5000"),
                    })
    @Override
    public Dept queryDept(long id) {
        Dept dept = deptMapper.queryDept(id);
        if (dept == null){
            throw new RuntimeException("id="+id+"=>没有对应的信息,null");
        }
        return dept;
    }

    public Dept getDeptHystrix(long id){
        return new Dept().setDeptId(id).
            setDpName("id="+id+"=>没有对应的信息,null").
            setDbSource("no database in MySQL");
    }

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

可以将@HystrixCommand 注解加在 Service 类上,也可以直接加在 controller 方法上,上述代码中我只加了关于 @GetMapping("/dept/get/{id}") 请求的熔断方法。

img

3、修改入口类

@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker       //开启熔断器
public class DeptProviderHystrix_8001 {

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

4、启动一个服务注册中心、本项目以及一个服务消费者即可,访问 http://localhost/consumer/dept/get/1 可以正常获取数据,但是访问 http://localhost/consumer/dept/get/10 页面显示内容为:

{"deptId":10,"dpName":"id=10=>没有对应的信息,null","dbSource":"no database in MySQL"}
请求合并

没合并的请求与合并的请求

img

Hystrix中的请求合并,就是利用一个合并处理器,将对同一个服务发起的连续请求合并成一个请求进行处理(这些连续请求的时间窗默认为10ms),在这个过程中涉及到的一个核心类就是HystrixCollapser。

什么情况下使用请求合并

在微服务架构中会拆分成多个小的模块,各个模块之间进行调用是需要通信的,但是在并发量大的情况下会令大量线程处于等待状态,这就会增加响应时间,所以可以使用请求合并将多次请求合并为一次请求。

请求合并的代价是什么

在设置了请求合并以后,本来一次请求只需要5ms就搞定了,但是使用了请求合并后可能还需要再等待10ms,看看还有没有其他请求一起,这样一次请求就从5ms增加到了15ms。不过如果我们发起的命令本来就是一个高延迟的命令,那么这个时候就可以使用请求合并了,因为这个时候时间窗的时间消耗就显得微不足道了。

所以总结:请求合并适合在高延迟 + 大量并发的情况下使用。

服务提供者

我们根据 springcloud-provider-dept-8001 项目复制一个名为 springcloud-provider-dept-dgrade-8001 的新项目,作为服务提供者。

1、相关依赖不变

2、在 DeptService 接口上增加一个方法,用于多个 deptId 查询。

public interface DeptService {

    .....
    List<Dept> getDepts(List<Long> ids);
}

3、同理修改 DeptMapper

@Mapper
@Repository
public interface DeptMapper {

    .....
    List<Dept> getDepts(List<Long> ids);
}

4、修改 mapper 映射文件

    <select id="getDepts" parameterType="list" resultType="Dept">
        select * from springcloud.dept
        <where>
            deptId in
            <foreach collection="list" item="id" open="(" close=")" separator=",">
                #{id}
            </foreach>
        </where>
    </select>

5、DeptServiceImpl

@Service
public class DeptServiceImpl implements DeptService {

    @Autowired
    DeptMapper deptMapper;

    .....

    @Override
    public List<Dept> getDepts(List<Long> ids) {
        return deptMapper.getDepts(ids);
    }
}

6、DeptController

    @RequestMapping("/dept/getList/{ids}")
    public List<Dept> getDepts2(@PathVariable("ids") String sid) throws ExecutionException, InterruptedException {
        List<Long> ids = Arrays.stream(sid.split(",")).map(Long::parseLong).collect(Collectors.toList());

        List<Dept> list = deptService.getDepts(ids);

        return list;
    }

用户可以在 url 地址中输入多个 id,来查询多条数据。

7、启动该项目,在浏览器中输入 http://localhost:8001/dept/getList/1,2 ,则可以得到数据显示。

服务消费者

新建项目名为 springcloud-consumer-dept-80-syn。

1、导入依赖

    <dependencies>
        <dependency>
            <groupId>com.msdn</groupId>
            <artifactId>springcloud-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <!--ribbon依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
    </dependencies>

2、application.yml

server:
  port: 80

#eureka配置
eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka7001:7001/eureka/,http://eureka7002:7002/eureka/,http://eureka7003:7003/eureka/

3、Myconfig

package com.msdn.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
 * @author hresh
 * @date 2020/4/28 21:27
 * @description
 */
@Configuration
public class MyConfig {

    //配置负载均衡实现RestTemplate
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }

}

4、DeptConsumerService 请求合并逻辑

@Service
public class DeptConsumerService {

    @Autowired
    RestTemplate restTemplate;

    private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT";

    //利用 hystrix 合并请求
    @HystrixCollapser(batchMethod = "batchDept", scope =
            com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL,
            collapserProperties = {
                    //请求时间间隔在 2s 之内的请求会被合并为一个请求,默认为 10ms
                    @HystrixProperty(name =
                            "timerDelayInMilliseconds", value = "2000"),
                    //设置触发批处理执行之前,在批处理中允许的最大请求数。
                    @HystrixProperty(name = "maxRequestsInBatch",
                            value = "200"),
            })
    public Future<Dept> getDept2(Long id){
        return null;
    }

    @HystrixCommand
    public List<Dept> batchDept(List<Long> ids){
        System.out.println("batchDept---------"+ids+"Thread.currentThread().getName():" + Thread.currentThread().getName());
        List<Dept> list = new ArrayList<>();
        String sid = Joiner.on(",").join(ids);
        Dept[] depts = restTemplate.getForObject(REST_URL_PREFIX+"/dept/getList/"+sid, Dept[].class);
        list = Arrays.asList(depts);
        return list;
    }
}

关于@HystrixCollapser 注解的参数详解,可以参考下图:

img

5、DeptConsumerController

@RestController
public class DeptConsumerController {

    @Autowired
    DeptConsumerService service;

    @RequestMapping("/consumer/dept/get/{id}")
    public Dept getDept(@PathVariable("id") long id) throws ExecutionException, InterruptedException {
        Future<Dept> dept1 = service.getDept2(id);

        return dept1.get();
    }

}

6、入口类

@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
public class DeptConsumerSyn_80 {

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

}

7、启动一个注册中心,以及上述的服务提供者,和本项目,注意服务消费者项目中需要进入 debug 状态,并在 controller 类中的 getDept 方法处打上断点,然后在浏览器中打开两个网页分别输入 http://localhost/consumer/dept/get/3 和 http://localhost/consumer/dept/get/2 ,然后来到 IDEA 中通过断点调试,查看控制台内容输出。

通过结果可知,两次请求会合并为一次请求,减少了对数据库的访问次数。本例中通过 debug 设置断点将两次请求拦截下来,合并为一次请求进行处理。让业务逻辑层把单个查询的 sql,改为批量查询的 sql。

疑惑:本来想通过 Jmeter 进行高并发测试的,不过没有得到想要的结果,可能是我应用场景处理的有问题,所以只能通过 debug 来实现。

基于Feign实现的负载均衡

降级

关于 Feign 的实现可以参考我的上一篇文章,其中主要修改了 springcloud-api 项目。

1、导入依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>

2、添加 DeptServiceFallbackFactory

//降级
@Component
public class DeptServiceFallbackFactory implements FallbackFactory {
    @Override
    public DeptFeignService create(Throwable throwable) {
        return new DeptFeignService() {
            @Override
            public boolean addDept(Dept dept) {
                return false;
            }

            @Override
            public Dept queryDept(long id) {
                return new Dept().setDeptId(id).
                        setDpName("id="+id+"=>没有对应的信息,null").
                        setDbSource("服务降级处理");
            }

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

3、修改 DeptFeignService

@Service
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT",fallbackFactory = DeptServiceFallbackFactory.class)
public interface DeptFeignService {

    @PostMapping("/dept/add")
    boolean addDept(@RequestBody Dept dept);

    @GetMapping("/dept/get/{id}")
    Dept queryDept(@PathVariable("id") long id);

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

接下来我们修改服务消费者 springcloud-consumer-dept-80-feign 项目。

1、修改配置文件

server:
  port: 80

#feign默认是不开启hystrix,默认值为false
feign:
  hystrix:
    enabled: true

#eureka配置
eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka7001:7001/eureka/,http://eureka7002:7002/eureka/,http://eureka7003:7003/eureka/

2、修改 DeptConsumerController

@RestController
public class DeptConsumerController {

    @Autowired
    DeptFeignService service;

    @RequestMapping("/consumer/dept/get/{id}")
    public Dept getDept(@PathVariable("id") long id) {
        return service.queryDept(id);
    }

    @RequestMapping("/consumer/dept/list")
    public List<Dept> queryAll() {
        return service.queryAll();
    }

    @RequestMapping(name = "/consumer/dept/add")
    public boolean addDept(Dept dept) {
        return service.addDept(dept);
    }

}

3、启动一个注册中心和一个服务提供者,以及本项目,然后访问 http://localhost/consumer/dept/get/3,页面正常显示数据,当关闭服务提供者,再次访问上述网址,页面会显示如下内容:

{"deptId":3,"dpName":"id=3=>没有对应的信息,null","dbSource":"服务降级处理"}

Hystrix-dashboard

Hystrix-dashboard 是一款针对 Hystrix 进行实时监控的工具,通过 Hystrix-dashboard 我们可以直观的看到各 Hystrix Command 的请求响应时间,请求成功率等数据。

新建一个名为 springcloud-consumer-hystrix-dashboard 的普通 maven 项目,当作监控中心。

1、导入相关依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
    </dependencies>

2、新增配置文件,设置端口号

server:
  port: 9001

3、新增启动类

package com.msdn;

@SpringBootApplication
@EnableHystrixDashboard
@EnableHystrix
public class DeptConsumerDashboard {

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

4、启动该项目,访问 http://localhost:9001/hystrix,页面内容如下:

监控项目

对 springcloud-provider-dept-hystrix-8001 项目进行扩增。

在入口类 DeptProviderHystrix_8001 中增加 ServletRegistrationBean 注入。

@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@EnableCircuitBreaker       //开启熔断器
public class DeptProviderHystrix_8001 {

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

    @Bean
    public ServletRegistrationBean getServletRegistrationBean(){
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
        servletRegistrationBean.addUrlMappings("/actuator/hystrix.stream");
        return servletRegistrationBean;
    }
}

启动一个注册中心,并启动该监控项目,在监控中心页面输入上述图片中内容,然后点击按钮跳转。

当你不断访问 http://localhost:8001/dept/get/1,可以观察上述页面变化。

详情代码访问: https://github.com/Acorn2/springcloud-eureka ,如有疑惑可联系我。

参考文献

分布式服务框架的雪崩问题

分布式系统雪崩效应处理方案

Spring Cloud中Hystrix的请求合并

高并发场景-请求合并(一)SpringCloud中Hystrix请求合并

SpringCloud实战3-Hystrix请求熔断与服务降级

学习SpringCloud Hystrix带你从0到1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值