互联网并发与安全系列教程(01) - 基于Hystrix实现服务隔离与降级

简介: 互联网并发与安全系列教程(01) - 基于Hystrix实现服务隔离与降级

引言

随着项目的SOA架构,业务复杂性的提高,系统的不断拆分,一个面向用户端的API,其内部的RPC调用层层嵌套,调用链条可能会非常长。

假设tomcat对外提供的一个application,其内部依赖了30个服务,每个服务的可用性都很高,为99.99%。那整个applicatiion的可用性就是:99.99%的30次方 = 99.7%,即0.3%的失败率。这也就意味着,每1亿个请求,有30万个失败;按时间来算,就是每个月的故障时间超过2小时。

我们都知道,线程的产生及运行时需要CPU来支撑的,并不是说可以无限的产生线程或者线程池?还需要看CPU的能力,如果超过CPU的负载能力,那么服务器很容易就会造成宕机或者无响应的情况。所以为了降低服务的故障率,防止服务器的宕机,我们必须要有一个合理的方案,就是本文主要讲的“如何基于Hystrix(音标:[hɪst’rɪks])实现服务隔离与降级”。

首先使用白话来理解几个概念:

  • 服务雪崩: 服务雪崩效应产生服务堆积在同一个线程池中,因为同一个线程池中,所有请求全部到一个服务进行访问,这时候会导致其它服务没有线程接收请求访问,所以产生了服务雪崩效应。(像服务器接口收受到高频访问(DDOS攻击),就会造成雪崩效应了)
  • 服务隔离: 保证每个服务互不影响,可以使用信号量和线程池的方式
  • 服务降级: 当服务不可用时,不会被等待,返回一个友好的提示。
  • 服务熔断: 当服务器达到了最大承受能力之后,直接拒绝访问服务,最后会调用 “服务降级” 的方式,返回友好提示,目的保证服务器不会宕机。

下面来开始学习Hystrix!

Hystrix

Hystrix 是一个微服务关于服务保护的框架,是Netflix开源的一款针对分布式系统的延迟和容错解决框架,目的是用来隔离分布式服务故障。

  • 它提供线程信号量隔离,以减少不同服务之间资源竞争带来的相互影响,提供优雅降级机制;
  • 提供熔断机制使得服务可以快速失败,而不是一直阻塞等待服务响应,并能从中快速恢复。
  • Hystrix通过这些机制来阻止级联失败并保证系统弹性、可用。

当大多数人在使用Tomcat时,多个HTTP服务会共享一个线程池,假设其中一个HTTP服务访问的数据库响应非常慢,这将造成服务响应时间延迟增加,大多数线程阻塞等待数据响应返回,导致整个Tomcat线程池都被该服务占用,甚至拖垮整个Tomcat。因此,如果我们能把不同HTTP服务隔离到不同的线程池,则某个HTTP服务的线程池满了也不会对其他服务造成灾难性故障。

这就需要 线程隔离或者信号量隔离 来实现了,使用线程隔离或信号隔离的目的是为不同的服务分配一定的资源,当自己的资源用完,直接返回失败而不是占用别人的资源。

下面我们来举个例子:“如何使用订单服务区调用会员服务实现服务隔离?”

案例

step1: 分别新建两个工程:订单工程和会员工程(步骤略)

step2: 引入“hystrix”依赖:

<dependency>
  <groupId>com.netflix.hystrix</groupId>
  <artifactId>hystrix-metrics-event-stream</artifactId>
  <version>1.5.12</version>
</dependency>
<dependency>
  <groupId>com.netflix.hystrix</groupId>
  <artifactId>hystrix-javanica</artifactId>
  <version>1.5.12</version>
</dependency>

step3: 请求订单工程,订单工程里面的接口会通过远程调用会员工程的接口:

订单工程:

@RequestMapping("/orderIndex")
public Object orderIndex() throws InterruptedException {
  JSONObject member = memberService.getMember();
  System.out.println("当前线程名称:" + Thread.currentThread().getName() + ",订单服务调用会员服务:member:" + member);
  return m
}

被调用的会员工程接口:

@RequestMapping("/memberIndex")
public Object memberIndex() throws InterruptedException {
  Map<String, Object> hashMap = new HashMap<String, Object>();
  hashMap.put("code", 200);
  hashMap.put("msg", "memberIndex");
  Thread.sleep(1500);
  return hashMap;
}

------------------------分割线 :现在使用两种方式对服务进行隔离------------------------

线程池方式

优点:

  1. 使用线程池隔离可以完全隔离第三方应用,请求线程可以快速放回。
  2. 请求线程可以继续接受新的请求,如果出现问题线程池隔离是独立的不会影响其他应用。
  3. 当失败的应用再次变得可用时,线程池将清理并可立即恢复,而不需要一个长时间的恢复。
  4. 独立的线程池提高了并发性

缺点:

  • 线程池隔离的主要缺点是它们增加计算开销(CPU)。每个命令的执行涉及到排队、调度和上下文切换都是在一个单独的线程上运行的。

代码片段如下:

public class OrderHystrixCommand extends HystrixCommand<JSONObject> {
  @Autowired
  private MemberService memberService;
  public OrderHystrixCommand(MemberService memberService) {
    super(setter());
    this.memberService = memberService;
  }
  protected JSONObject run() throws Exception {
    JSONObject member = memberService.getMember();
    System.out.println("当前线程名称:" + Thread.currentThread().getName() + ",订单服务调用会员服务:member:" + member);
    return member;
  }
  private static Setter setter() {
    // 服务分组
    HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey("members");
    // 服务标识
    HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey("member");
    // 线程池名称
    HystrixThreadPoolKey threadPoolKey = HystrixThreadPoolKey.Factory.asKey("member-pool");
    // #####################################################
    // 线程池配置 线程池大小为10,线程存活时间15秒 队列等待的阈值为100,超过100执行拒绝策略
    HystrixThreadPoolProperties.Setter threadPoolProperties = HystrixThreadPoolProperties.Setter().withCoreSize(10)
        .withKeepAliveTimeMinutes(15).withQueueSizeRejectionThreshold(100);
    // ########################################################
    // 命令属性配置Hystrix 开启超时
    HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter()
        // 采用线程池方式实现服务隔离
        .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD)
        // 禁止
        .withExecutionTimeoutEnabled(false);
    return HystrixCommand.Setter.withGroupKey(groupKey).andCommandKey(commandKey).andThreadPoolKey(threadPoolKey)
        .andThreadPoolPropertiesDefaults(threadPoolProperties).andCommandPropertiesDefaults(commandProperties);
  }
  @Override
  protected JSONObject getFallback() {
    // 如果Hystrix发生熔断,当前服务不可用,直接执行Fallback方法
    System.out.println("系统错误!");
    JSONObject jsonObject = new JSONObject();
    jsonObject.put("code", 500);
    jsonObject.put("msg", "系统错误!");
    return jsonObject;
  }
}
信号量方式

使用一个原子计数器(或信号量)来记录当前有多少个线程在运行,当请求进来时先判断计数 器的数值,若超过设置的最大线程个数则拒绝该请求,若不超过则通行,这时候计数器+1,请求返 回成功后计数器-1,与线程池隔离最大不同在于执行依赖代码的线程依然是请求线程 ,信号量的大小可以动态调整, 线程池大小不可以。

public class OrderHystrixCommand2 extends HystrixCommand<JSONObject> {
  @Autowired
  private MemberService memberService;
  public OrderHystrixCommand2(MemberService memberService) {
    super(setter());
    this.memberService = memberService;
  }
  protected JSONObject run() throws Exception {
    JSONObject member = memberService.getMember();
    System.out.println("当前线程名称:" + Thread.currentThread().getName() + ",订单服务调用会员服务:member:" + member);
    return member;
  }
  private static Setter setter() {
    // 服务分组
    HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey("members");
    // 命令属性配置 采用信号量模式
    HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter()
        .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE)
        // 使用一个原子计数器(或信号量)来记录当前有多少个线程在运行,当请求进来时先判断计数
        // 器的数值,若超过设置的最大线程个数则拒绝该请求,若不超过则通行,这时候计数器+1,请求返 回成功后计数器-1。
        .withExecutionIsolationSemaphoreMaxConcurrentRequests(50);
    return HystrixCommand.Setter.withGroupKey(groupKey).andCommandPropertiesDefaults(commandProperties);
  }
  @Override
  protected JSONObject getFallback() {
    // 如果Hystrix发生熔断,当前服务不可用,直接执行Fallback方法
    System.out.println("系统错误!");
    JSONObject jsonObject = new JSONObject();
    jsonObject.put("code", 500);
    jsonObject.put("msg", "系统错误!");
    return jsonObject;
  }
}
应用场景

线程池隔离:

  1. 第三方应用或者接口
  2. 并发量大

信号量隔离:

  1. 内部应用或者中间件(redis)
  2. 并发需求不大

总结

目录
相关文章
|
6月前
服务熔断器-Hystrix
服务熔断器-Hystrix
58 2
|
3月前
|
Java UED 开发者
Spring Boot 降级功能的神秘面纱:Hystrix 与 Resilience4j 究竟藏着怎样的秘密?
【8月更文挑战第29天】在分布式系统中,服务稳定性至关重要。为应对故障,Spring Boot 提供了 Hystrix 和 Resilience4j 两种降级工具。Hystrix 作为 Netflix 的容错框架,通过隔离依赖、控制并发及降级机制增强系统稳定性;Resilience4j 则是一个轻量级库,提供丰富的降级策略。两者均可有效提升系统可靠性,具体选择取决于需求与场景。在面对服务故障时,合理运用这些工具能确保系统基本功能正常运作,优化用户体验。以上简介包括了两个工具的简单示例代码,帮助开发者更好地理解和应用。
77 0
|
5月前
springCloud之服务降级熔断Hystrix、OpenFeign
springCloud之服务降级熔断Hystrix、OpenFeign
336 0
|
2月前
|
存储 NoSQL 调度
|
5月前
|
XML 缓存 Java
Spring Boot 优雅实现降级功能:Hystrix 与 Resilience4j 的实践
【6月更文挑战第19天】在分布式系统中,服务降级是一种重要的容错机制。当某个服务不可用或响应慢时,降级机制可以保证系统的整体稳定性。本文将详细介绍如何在 Spring Boot 中使用 Hystrix 和 Resilience4j 实现降级功能。
328 7
|
4月前
|
SQL Java 索引
SQL 能力问题之Hystrix的降级触发条件问题如何解决
SQL 能力问题之Hystrix的降级触发条件问题如何解决
|
6月前
|
监控 微服务
Hystrix熔断器设计思想(学习笔记)附(服务监控hystrixDashboard识图)
Hystrix熔断器设计思想(学习笔记)附(服务监控hystrixDashboard识图)
58 0
|
6月前
|
监控 Java Sentinel
springcloud4-服务熔断hystrix及sentinel
springcloud4-服务熔断hystrix及sentinel
69 0
|
6月前
|
监控 数据可视化 Java
Spring Cloud Hystrix:服务容错保护
Spring Cloud Hystrix:服务容错保护
213 0
|
6月前
|
监控 负载均衡 数据可视化
SpringCloud - Hystrix断路器-服务熔断与降级和HystrixDashboard
SpringCloud - Hystrix断路器-服务熔断与降级和HystrixDashboard
60 0