断路器(Hystrix)
为什么需要 Hystrix?
在微服务架构中,我们将业务拆分成一个个的服务,服务与服务之间可以相互调用(RPC)。为了保证其高可用,单个服务又必须集群部署。由于网络原因或者自身的原因,服务并不能保证服务的100%可用,如果单个服务出现问题,调用这个服务就会出现网络延迟,此时若有大量的网络涌入,会形成任务累计,导致服务瘫痪,甚至导致服务“雪崩”。为了解决这个问题,就出现断路器模型。
Hystrix 是一个帮助解决分布式系统交互时超时处理和容错的类库, 它同样拥有保护系统的能力.什么是服务雪崩
分布式系统中经常会出现某个基础服务不可用造成整个系统不可用的情况, 这种现象被称为服务雪崩效应. 为了应对服务雪崩, 一种常见的做法是手动服务降级. 而Hystrix的出现,给我们提供了另一种选择.
雪崩效应
什么是雪崩效应
模拟服务雪崩效果产生
使用jmeter来模拟请求,压力测试。
会员服务的测试的代码:
package com.cc.member;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
public class MemberController {
@Value("${server.port}")
private String port;
private static int count = 0;
@RequestMapping("/getUserList")
public List<String> getUserList() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count++;
System.out.println(count);
List<String> listUser = new ArrayList<String>();
listUser.add("zhangsan");
listUser.add("lisi");
listUser.add("yushengjun");
listUser.add(port);
return listUser;
}
@RequestMapping("/getMemberInfo")
public String getMemberInfo(){
return "member server";
}
}
订单服务的测试代码,订单服务调用会会员服务:
package com.cc.order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class OrderController {
@Autowired
private MemberService memberService;
@RequestMapping("/getOrderByUserList")
public List<String> getOrderByUserList(){
return memberService.getOrderByUserList();
}
@RequestMapping("/getOrderInfo")
public String getOrderInfo(){
return "order server";
}
}
还有我们还需要在订单服务中配置tomact的最大线程数,设置为50。
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8888/eureka/
server:
port: 8764
tomcat:
max-threads: 50
spring:
application:
name: service-order
试着访问一下:
三秒之后才刷出数据
访问 http://127.0.0.1:8764/getOrderInfo
发现访问开始转圈了。
服务雪崩应对策略
针对造成服务雪崩的不同原因, 可以使用不同的应对策略:
- 流量控制
- 改进缓存模式
- 服务自动扩容
- 服务调用者降级服务
流量控制 的具体措施包括:
- 网关限流
- 用户交互限流
- 关闭重试
服务雪崩解决办法
1.服务雪崩的原因
(1)某几个机器故障:例如机器的硬驱动引起的错误,或者一些特定的机器上出现一些的bug(如,内存中断或者死锁)。
(2)服务器负载发生变化:某些时候服务会因为用户行为造成请求无法及时处理从而导致雪崩,例如阿里的双十一活动,若没有提前增加机器预估流量则会造服务器压力会骤然增大二挂掉。
(3)人为因素:比如代码中的路径在某个时候出现bug
一般情况对于服务依赖的保护主要有3中解决方案:
(1)熔断模式:这种模式主要是参考电路熔断,如果一条线路电压过高,保险丝会熔断,防止火灾。放到我们的系统中,如果某个目标服务调用慢或者有大量超时,此时,熔断该服务的调用,对于后续调用请求,不在继续调用目标服务,直接返回,快速释放资源。如果目标服务情况好转则恢复调用。
(2)隔离模式:这种模式就像对系统请求按类型划分成一个个小岛的一样,当某个小岛被火少光了,不会影响到其他的小岛。例如可以对不同类型的请求使用线程池来资源隔离,每种类型的请求互不影响,如果一种类型的请求线程资源耗尽,则对后续的该类型请求直接返回,不再调用后续资源。这种模式使用场景非常多,例如将一个服务拆开,对于重要的服务使用单独服务器来部署,再或者公司最近推广的多中心。
(3)限流模式:上述的熔断模式和隔离模式都属于出错后的容错处理机制,而限流模式则可以称为预防模式。限流模式主要是提前对各个类型的请求设置最高的QPS阈值,若高于设置的阈值则对该请求直接返回,不再调用后续资源。这种模式不能解决服务依赖的问题,只能解决系统整体资源分配问题,因为没有被限流的请求依然有可能造成雪崩效应。
3.熔断设计
在熔断的设计主要参考了hystrix的做法。其中最重要的是三个模块:熔断请求判断算法、熔断恢复机制、熔断报警
(1)熔断请求判断机制算法:使用无锁循环队列计数,每个熔断器默认维护10个bucket,每1秒一个bucket,每个blucket记录请求的成功、失败、超时、拒绝的状态,默认错误超过50%且10秒内超过20个请求进行中断拦截。
(2)熔断恢复:对于被熔断的请求,每隔5s允许部分请求通过,若请求都是健康的(RT<250ms)则对请求健康恢复。
(3)熔断报警:对于熔断的请求打日志,异常请求超过某些设定则报警
隔离的方式一般使用两种
(1)线程池隔离模式:使用一个线程池来存储当前的请求,线程池对请求作处理,设置任务返回处理超时时间,堆积的请求堆积入线程池队列。这种方式需要为每个依赖的服务申请线程池,有一定的资源消耗,好处是可以应对突发流量(流量洪峰来临时,处理不完可将数据存储到线程池队里慢慢处理)
(2)信号量隔离模式:使用一个原子计数器(或信号量)来记录当前有多少个线程在运行,请求来先判断计数器的数值,若超过设置的最大线程个数则丢弃改类型的新请求,若不超过则执行计数操作请求来计数器+1,请求返回计数器-1。这种方式是严格的控制线程且立即返回模式,无法应对突发流量(流量洪峰来临时,处理的线程超过数量,其他的请求会直接返回,不继续去请求依赖的服务)
超时分两种,一种是请求的等待超时,一种是请求运行超时。
等待超时:在任务入队列时设置任务入队列时间,并判断队头的任务入队列时间是否大于超时时间,超过则丢弃任务。
运行超时:直接可使用线程池提供的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)来隔离资源.
服务的降级
什么是服务降级
所有的RPC技术里面服务降级是一个最为重要的话题,所谓的降级指的是当服务的提供方不可使用的时候,程序不会出现异常,而会出现本地的操作调用。
Rest方式使用断路器
service-order工程新增Maven依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
启动方式:
@HystrixCommand 作用:服务发生错误,回调方法。
@EnableHystrix 启动断路器
测试
压力测试:
访问不转圈了
Fegin使用断路器
改造service-order-feign工程
package com.cc.feign.order;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
@FeignClient(value="service-member",fallback= MemberFeignFallback.class)
public interface MemberFeign {
@RequestMapping("/getUserList")
public List<String> getOrderByUserList();
}
package com.cc.feign.order;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component
public class MemberFeignFallback implements MemberFeign {
//服务降级处理
public List<String> getOrderByUserList() {
List<String> listUser = new ArrayList<String>();
listUser.add("not user list");
return listUser;
}
}
package com.cc.feign.order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class MemberFeignController {
@Autowired
private MemberFeign memberFeign;
@RequestMapping("/getUserList")
public List<String> getUserList(){
return memberFeign.getOrderByUserList();
}
@RequestMapping("/getOrderInfo")
public String getOrderInfo(){
return "order server";
}
}
加入配置:
feign:
hystrix:
enabled: true
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8888/eureka/
server:
port: 8765
tomcat:
max-threads: 50
spring:
application:
name: service-order-feign
feign:
hystrix:
enabled: true
启动服务然后直接访问:
压力测试:
发现访问其他的订单服务没有转圈。
因为 Hystrix默认超时1秒则进行服务降级,调用fallback。
修改超时时间,然后再来看看效果
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8888/eureka/
server:
port: 8765
tomcat:
max-threads: 50
spring:
application:
name: service-order-feign
feign:
hystrix:
enabled: true
###超时时间
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 6000
设置超时时间为6秒,再次进行压力测试,然后访问其他的订单服务。
可以看到访问其他网络又转圈了
把tomcat的最大线程数改小一点效果会更好。
demo源码
https://gitee.com/cckevincyh/Spring-Cloud-Learning