为什么需要 Hystrix?
在微服务架构中, 我们将业务拆分成一个个的服务, 服务与服务之间可以相互调用(RPC). 为了保证其高可用, 单个服务又必须集群部署. 由于网络原因或者自身的原因, 服务并不能保证服务的 100% 可用, 如果单个服务出现问题, 调用这个服务就会出现网络延迟, 此时若有大量的网络涌入, 会形成任务累计, 导致服务瘫痪, 甚至导致服务 "雪崩". 为了解决这个问题, 就出现断路器模型.
Hystrix 是一个帮助解决分布式系统交互时超时处理和容错的类库, 它同样拥有保护系统的能力. 什么是服务雪崩
分布式系统中经常会出现某个基础服务不可用造成整个系统不可用的情况, 这种现象被称为服务雪崩效应. 为了应对服务雪崩, 一种常见的做法是手动服务降级. 而 Hystrix 的出现, 给我们提供了另一种选择.
服务雪崩应对策略
针对造成服务雪崩的不同原因, 可以使用不同的应对策略:
1. 流量控制
2. 改进缓存模式
3. 服务自动扩容
4. 服务调用者降级服务
流量控制的具体措施包括:
. 网关限流
. 用户交互限流
. 关闭重试
服务雪崩解决办法
1. 服务雪崩的原因
(1)某几个机器故障: 例如机器的硬驱动引起的错误, 或者一些特定的机器上出现一些的 bug(如, 内存中断或者死锁).
(2)服务器负载发生变化: 某些时候服务会因为用户行为造成请求无法及时处理从而导致雪崩, 例如阿里的双十一活动, 若没有提前增加机器预估流量则会造服务器压力会骤然增大二挂掉.
(3)人为因素: 比如代码中的路径在某个时候出现 bug
2. 解决或缓解服务雪崩的方案
一般情况对于服务依赖的保护主要有 3 中解决方案:
(1)熔断模式: 这种模式主要是参考电路熔断, 如果一条线路电压过高, 保险丝会熔断, 防止火灾. 放到我们的系统中, 如果某个目标服务调用慢或者有大量超时, 此时, 熔断该服务的调用, 对于后续调用请求, 不在继续调用目标服务, 直接返回, 快速释放资源. 如果目标服务情况好转则恢复调用.
(2)隔离模式: 这种模式就像对系统请求按类型划分成一个个小岛的一样, 当某个小岛被火少光了, 不会影响到其他的小岛. 例如可以对不同类型的请求使用线程池来资源隔离, 每种类型的请求互不影响, 如果一种类型的请求线程资源耗尽, 则对后续的该类型请求直接返回, 不再调用后续资源. 这种模式使用场景非常多, 例如将一个服务拆开, 对于重要的服务使用单独服务器来部署, 再或者公司最近推广的多中心.
(3)限流模式: 上述的熔断模式和隔离模式都属于出错后的容错处理机制, 而限流模式则可以称为预防模式. 限流模式主要是提前对各个类型的请求设置最高的 QPS 阈值, 若高于设置的阈值则对该请求直接返回, 不再调用后续资源. 这种模式不能解决服务依赖的问题, 只能解决系统整体资源分配问题, 因为没有被限流的请求依然有可能造成雪崩效应.
3. 熔断设计
在熔断的设计主要参考了 hystrix 的做法. 其中最重要的是三个模块: 熔断请求判断算法, 熔断恢复机制, 熔断报警
(1)熔断请求判断机制算法: 使用无锁循环队列计数, 每个熔断器默认维护 10 个 bucket, 每 1 秒一个 bucket, 每个 blucket 记录请求的成功, 失败, 超时, 拒绝的状态, 默认错误超过 50% 且 10 秒内超过 20 个请求进行中断拦截.
(2)熔断恢复: 对于被熔断的请求, 每隔 5s 允许部分请求通过, 若请求都是健康的 (RT<250ms) 则对请求健康恢复.
(3)熔断报警: 对于熔断的请求打日志, 异常请求超过某些设定则报警
4. 隔离设计
隔离的方式一般使用两种
(1)线程池隔离模式: 使用一个线程池来存储当前的请求, 线程池对请求作处理, 设置任务返回处理超时时间, 堆积的请求堆积入线程池队列. 这种方式需要为每个依赖的服务申请线程池, 有一定的资源消耗, 好处是可以应对突发流量(流量洪峰来临时, 处理不完可将数据存储到线程池队里慢慢处理)
(2)信号量隔离模式: 使用一个原子计数器 (或信号量) 来记录当前有多少个线程在运行, 请求来先判断计数器的数值, 若超过设置的最大线程个数则丢弃改类型的新请求, 若不超过则执行计数操作请求来计数器 + 1, 请求返回计数器 - 1. 这种方式是严格的控制线程且立即返回模式, 无法应对突发流量(流量洪峰来临时, 处理的线程超过数量, 其他的请求会直接返回, 不继续去请求依赖的服务)
5 超时机制设计
超时分两种, 一种是请求的等待超时, 一种是请求运行超时.
等待超时: 在任务入队列时设置任务入队列时间, 并判断队头的任务入队列时间是否大于超时时间, 超过则丢弃任务.
运行超时: 直接可使用线程池提供的 get 方法
什么是熔断机制
熔断机制, 就是下游服务出现问题后, 为保证整个系统正常运行下去, 而提供一种降级服务的机制, 通过返回缓存数据或者既定数据, 避免出现系统整体雪崩效应. 在 springcloud 中, 该功能可通过配置的方式加入到项目中.
Hystrix 作用
基于命令模式的请求: 解耦了请求者和接受者, 使得发送出去的命令可以排队, 异步执行.
资源隔离: 用 ConcurrentHashMap 绑定 commandKey 和线程池, 当某个功能运行不稳定或有问题时, 服务的其他部分不受影响. 服务恢复比较快.
熔断和服务降级: 用 ConcurrentHashMap 绑定 commandKey 和 HystrixCircuitBreaker 类(断路器类), 统计每次请求的结果并记录, 根据结果判断是否开启断路器, 自定义异常返回结果实现熔断降级.
服务降级案例
一, 创建服务注册中心项目 spring_server
1 向 pom 文件导入节点
- <parent>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-parent</artifactId>
- <version>2.1.4.RELEASE</version>
- </parent>
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-openfeign</artifactId>
- </dependency>
- <dependencyManagement>
- <dependencies>
- <!--springCloud 依赖 -->
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-dependencies</artifactId>
- <version>Greenwich.RELEASE</version>
- <type>pom</type>
- <scope>import</scope>
- </dependency>
- </dependencies>
- </dependencyManagement>
2 创建 application.YAML 文件
- ## 应用名称
- spring:
- application:
- name: euraka-server
- #声明当前 eurakaserver 端口号
- server:
- port: 8888
- # 配置 eureka
- eureka:
- client:
- #代表自己是否注册到注册中心去
- register-with-eureka: false
- #表明自己是注册中心
- fetch-registry: false
- #配置地址
- service-url:
- defaultZone: http://localhost:8888/eureka
3 创建启动程序类
- @SpringBootApplication
- @EnableEurekaServer
- public class StartEurekaService {
- public static void main(String[] args) {
- SpringApplication.run(StartEurekaService.class,args);
- }
- }
4 运行效果
二, 创建项目 springcloud_eureka_member
1 创建 service 层接口
- public interface MemberService {
- public String getMember();
- }
2 创建 service 层接口实现类
- @RestController
- public class MemberServiceImpl implements MemberService {
- @RequestMapping("/getMember")
- @Override
- public String getMember() {
- try {
- Thread.sleep(3000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("member getMember 方法");
- return "member";
- }
- }
3 创建 application.YAML 文件
- ## 应用名称
- spring:
- application:
- name: member-server
- #声明当前 eurakaserver 端口号
- server:
- port: 9001
- #配置 eureka
- eureka:
- client:
- #配置地址
- service-url:
- defaultZone: http://localhost:8888/eureka
4 创建启动项目类
- @SpringBootApplication
- @EnableEurekaClient
- public class StartProvider {
- public static void main(String[] args) {
- SpringApplication.run(StartProvider.class,args);
- }
- }
5 运行效果
三, 创建项目 springcloud_eureka_order
1 创建 service 层接口
- @FeignClient("member-server")
- public interface OrderService {
- @RequestMapping("/member")
- public String getOrder();
- }
2 创建 service 层接口实现类
- @RestController
- public class OrderServiceImpl implements OrderService {
- @RequestMapping("/getOrder")
- @Override
- public String getOrder() {
- System.out.println("=============== 订单工程 OrderService 接口 ================");
- return "orders";
- }
- }
3 创建 Controller 层
- @RestController
- public class OrderConroller {
- @Resource
- private MemberService memberService;
- @RequestMapping("/getOrderByMember")
- public String getOrderByMember(){
- return memberService.getMember();
- }
- @RequestMapping("/orderinfo")
- public String getOne(){
- return "orderinfo";
- }
- }
4 创建 application.YAML 文件
- ## 应用名称
- spring:
- application:
- name: order-server
- main:
- allow bean definition overriding: true
- #声明当前 eurakaserver 端口号
- server:
- port: 9002
- #配置 eureka
- eureka:
- client:
- #配置地址
- service-url:
- defaultZone: http://localhost:8888/eureka
5 创建启动程序类
- @SpringBootApplication
- @EnableEurekaClient
- @EnableFeignClients
- public class StartProvider {
- public static void main(String[] args) {
- SpringApplication.run(StartProvider.class, args);
- }
- }
6 启动测试工具
6 运行效果
我们在 springcloud_eureka_member 中利用线程模拟了雪崩场景, 如果当我们请求一个地址时大于一秒没有请求出来就代表请求失败, 我们使用测试工具实现
在 同一时间用 3000 个线程访问同一个地址时, 另一个地址会等待一直转圈, 直到访问另一个地址的线程有空闲线程再来请求这个地址, 我们将使用服务降级来解决
所谓的降级指的是当服务的提供方不可使用的时候, 程序不会出现异常, 而会出现本地的操作调
7 在 springclou_eureka_order 中 application.YAML 添加节点
- ## 配置 ribbon 读取时间
- ribbon:
- ReadTimeout: 15000
- ConnectTimeout: 15000
- ## 开启断路器
- feign:
- hystrix:
- enabled: true
- ## 因为我们在 members 服务中线程休眠 3s, 服务调用默认超过 1s 当做调用失败, 那么就会执行服务降级的类, 我们可以配置服务响应时间
- hystrix:
- command:
- default:
- execution:
- isolation:
- thread:
- timeoutInMilliseconds: 4000
8 在 springclou_eureka_order 中创建 MemberService
- /**
- * fallback 服务降级执行得本地类
- */
- @FeignClient(value = "member-server",fallback = MemberServiceFallBack.class)
- public interface MemberService {
- /**
- * 获取会员信息
- * @return
- */
- @RequestMapping("/getMember")
- public String getMember();
- }
9 创建 MemberServiceFallBack 实现 MemberService
- /**
- * 本地服务
- */
- @Component
- public class MemberServiceFallBack implements MemberService {
- @Override
- public String getMember() {
- return "服务降级";
- }
- }
10 开启测试工具运行效果
当请求地址没有反应会直接请求本地方法
为什么需要 Hystrix?
在微服务架构中, 我们将业务拆分成一个个的服务, 服务与服务之间可以相互调用(RPC). 为了保证其高可用, 单个服务又必须集群部署. 由于网络原因或者自身的原因, 服务并不能保证服务的 100% 可用, 如果单个服务出现问题, 调用这个服务就会出现网络延迟, 此时若有大量的网络涌入, 会形成任务累计, 导致服务瘫痪, 甚至导致服务 "雪崩". 为了解决这个问题, 就出现断路器模型.
Hystrix 是一个帮助解决分布式系统交互时超时处理和容错的类库, 它同样拥有保护系统的能力. 什么是服务雪崩
分布式系统中经常会出现某个基础服务不可用造成整个系统不可用的情况, 这种现象被称为服务雪崩效应. 为了应对服务雪崩, 一种常见的做法是手动服务降级. 而 Hystrix 的出现, 给我们提供了另一种选择.
服务雪崩应对策略
针对造成服务雪崩的不同原因, 可以使用不同的应对策略:
1. 流量控制
2. 改进缓存模式
3. 服务自动扩容
4. 服务调用者降级服务
流量控制的具体措施包括:
. 网关限流
. 用户交互限流
. 关闭重试
服务雪崩解决办法
1. 服务雪崩的原因
(1)某几个机器故障: 例如机器的硬驱动引起的错误, 或者一些特定的机器上出现一些的 bug(如, 内存中断或者死锁).
(2)服务器负载发生变化: 某些时候服务会因为用户行为造成请求无法及时处理从而导致雪崩, 例如阿里的双十一活动, 若没有提前增加机器预估流量则会造服务器压力会骤然增大二挂掉.
(3)人为因素: 比如代码中的路径在某个时候出现 bug
2. 解决或缓解服务雪崩的方案
一般情况对于服务依赖的保护主要有 3 中解决方案:
(1)熔断模式: 这种模式主要是参考电路熔断, 如果一条线路电压过高, 保险丝会熔断, 防止火灾. 放到我们的系统中, 如果某个目标服务调用慢或者有大量超时, 此时, 熔断该服务的调用, 对于后续调用请求, 不在继续调用目标服务, 直接返回, 快速释放资源. 如果目标服务情况好转则恢复调用.
(2)隔离模式: 这种模式就像对系统请求按类型划分成一个个小岛的一样, 当某个小岛被火少光了, 不会影响到其他的小岛. 例如可以对不同类型的请求使用线程池来资源隔离, 每种类型的请求互不影响, 如果一种类型的请求线程资源耗尽, 则对后续的该类型请求直接返回, 不再调用后续资源. 这种模式使用场景非常多, 例如将一个服务拆开, 对于重要的服务使用单独服务器来部署, 再或者公司最近推广的多中心.
(3)限流模式: 上述的熔断模式和隔离模式都属于出错后的容错处理机制, 而限流模式则可以称为预防模式. 限流模式主要是提前对各个类型的请求设置最高的 QPS 阈值, 若高于设置的阈值则对该请求直接返回, 不再调用后续资源. 这种模式不能解决服务依赖的问题, 只能解决系统整体资源分配问题, 因为没有被限流的请求依然有可能造成雪崩效应.
3. 熔断设计
在熔断的设计主要参考了 hystrix 的做法. 其中最重要的是三个模块: 熔断请求判断算法, 熔断恢复机制, 熔断报警
(1)熔断请求判断机制算法: 使用无锁循环队列计数, 每个熔断器默认维护 10 个 bucket, 每 1 秒一个 bucket, 每个 blucket 记录请求的成功, 失败, 超时, 拒绝的状态, 默认错误超过 50% 且 10 秒内超过 20 个请求进行中断拦截.
(2)熔断恢复: 对于被熔断的请求, 每隔 5s 允许部分请求通过, 若请求都是健康的 (RT<250ms) 则对请求健康恢复.
(3)熔断报警: 对于熔断的请求打日志, 异常请求超过某些设定则报警
4. 隔离设计
隔离的方式一般使用两种
(1)线程池隔离模式: 使用一个线程池来存储当前的请求, 线程池对请求作处理, 设置任务返回处理超时时间, 堆积的请求堆积入线程池队列. 这种方式需要为每个依赖的服务申请线程池, 有一定的资源消耗, 好处是可以应对突发流量(流量洪峰来临时, 处理不完可将数据存储到线程池队里慢慢处理)
(2)信号量隔离模式: 使用一个原子计数器 (或信号量) 来记录当前有多少个线程在运行, 请求来先判断计数器的数值, 若超过设置的最大线程个数则丢弃改类型的新请求, 若不超过则执行计数操作请求来计数器 + 1, 请求返回计数器 - 1. 这种方式是严格的控制线程且立即返回模式, 无法应对突发流量(流量洪峰来临时, 处理的线程超过数量, 其他的请求会直接返回, 不继续去请求依赖的服务)
5 超时机制设计
超时分两种, 一种是请求的等待超时, 一种是请求运行超时.
等待超时: 在任务入队列时设置任务入队列时间, 并判断队头的任务入队列时间是否大于超时时间, 超过则丢弃任务.
运行超时: 直接可使用线程池提供的 get 方法
什么是熔断机制
熔断机制, 就是下游服务出现问题后, 为保证整个系统正常运行下去, 而提供一种降级服务的机制, 通过返回缓存数据或者既定数据, 避免出现系统整体雪崩效应. 在 springcloud 中, 该功能可通过配置的方式加入到项目中.
Hystrix 作用
1. 断路器机制
断路器很好理解, 当 Hystrix Command 请求后端服务失败数量超过一定比例(默认 50%), 断路器会切换到开路状态(Open). 这时所有请求会直接失败而不会发送到后端服务. 断路器保持在开路状态一段时间后(默认 5 秒), 自动切换到半开路状态(HALF-OPEN). 这时会判断下一次请求的返回情况, 如果请求成功, 断路器切回闭路状态(CLOSED), 否则重新切换到开路状态(OPEN). Hystrix 的断路器就像我们家庭电路中的保险丝, 一旦后端服务不可用, 断路器会直接切断请求链, 避免发送大量无效请求影响系统吞吐量, 并且断路器有自我检测并恢复的能力.
2.Fallback
Fallback 相当于是降级操作. 对于查询操作, 我们可以实现一个 fallback 方法, 当请求后端服务出现异常的时候, 可以使用 fallback 方法返回的值. fallback 方法的返回值一般是设置的默认值或者来自缓存.
3. 资源隔离
在 Hystrix 中, 主要通过线程池来实现资源隔离. 通常在使用的时候我们会根据调用的远程服务划分出多个线程池. 例如调用产品服务的 Command 放入 A 线程池, 调用账户服务的 Command 放入 B 线程池. 这样做的主要优点是运行环境被隔离开了. 这样就算调用服务的代码存在 bug 或者由于其他原因导致自己所在线程池被耗尽时, 不会对系统的其他服务造成影响. 但是带来的代价就是维护多个线程池会对系统带来额外的性能开销. 如果是对性能有严格要求而且确信自己调用服务的客户端代码不会出问题的话, 可以使用 Hystrix 的信号模式 (Semaphores) 来隔离资源.
来源: http://www.bubuko.com/infodetail-3343050.html