在分布式系统里,许多依赖不可避免的会调用失败,比如超时,异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整个体系服务失败,避免级联故障,以提高分布式系统的弹性。
当出现以下情况,服务器忙,不让客户端等待并立刻返回一个友好提示,fallback
当服务器请求超过最大数,直接拒绝访问,然后调用服务降级方法并返回一个友好提示
秒杀高并发等操作,严禁拥挤,排队有序进行
启动类
@SpringBootApplication
@EnableEurekaClient
public class PaymentHystrixMain {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain.class,args);
}
}
yml文件
server:
port: 8001
spring:
application:
name: cloud-provider-hystrix-payment
eureka:
client:
register-with-eureka: true #是否将自己注册进eurekaServer默认true
fetch-registry: true #是否从eurekaServer抓取已有的注册信息,默认true。集群必须设置true才能配合Ribbon使用负载均衡
service-url:
#设置与Eureka Server交互的地址查询服务和注册服务
defaultZone: http://eureka7001.com:7001/eureka
#集群版
# defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
两个service方法,一个正常返回,另外一个睡眠3秒钟返回
@Service
public class PaymentService {
//正常访问返回的方法
public String paymentInfo_OK(Integer id){
return "线程池:"+Thread.currentThread().getName()+" paymentInfo_OK, id: "+id+" ==》正常返回";
}
//耗时等待返回的方法
public String paymentInfo_TimeOut(Integer id) throws InterruptedException {
int timeOut = 3;
TimeUnit.SECONDS.sleep(timeOut);
return "线程池:"+Thread.currentThread().getName()+" paymentInfo_TimeOut, id: "+id+" ==》耗时等待"+timeOut+"秒";
}
}
controller方法
@RestController
@Slf4j
public class PaymentController {
@Resource
private PaymentService paymentService;
@Value("${server.port}")
private String serverPort;
@GetMapping("/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id){
String result = paymentService.paymentInfo_OK(id);
log.info("paymentInfo_OK 响应结果:"+result);
return result;
}
@GetMapping("/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id) throws InterruptedException {
String result = paymentService.paymentInfo_TimeOut(id);
log.info("paymentInfo_TimeOut 响应结果:"+result);
return result;
}
}
访问正常返回方法(立刻返回)
访问睡眠3秒钟再返回方法(转圈等待3秒)
2000个并发访问paymentInfo_TimeOut,每次请求都需要睡眠3秒钟才能返回
后台一直在请求
此时访问paymentInfo_OK方法(以前立刻秒回)也在转圈等待
结论:20000个请求一直发送到paymentInfo_TimeOut,tomcat工作线程被使用完了,没有多余的线程来处理paymentInfo_OK,此时访问paymentInfo_OK也需要等3秒钟(等其中访问paymentInfo_TimeOut的线程执行完)。
@SpringBootApplication
@EnableFeignClients
public class OrderHystrixMain {
public static void main(String[] args) {
SpringApplication.run(OrderHystrixMain.class, args);
}
}
yml文件
server:
port: 80
eureka:
client:
#是否将自己注册进eurekaServer默认true
register-with-eureka: false
service-url:
#设置与Eureka Server交互的地址查询服务和注册服务
defaultZone: http://localhost:7001/eureka
#集群版
# defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
ribbon:
#指建立连接后从服务器读取到可用资源所用的时间
# openFeign-ribbon,客户端一般默认等待1秒钟
ReadTimeout: 3000
利用Feign调用客户端微服务方法
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT")
public interface PaymentHystrixService {
@GetMapping("/payment/hystrix/ok/{id}")
String paymentInfo_OK(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/timeout/{id}")
String paymentInfo_TimeOut(@PathVariable("id") Integer id);
}
controller方法
@RestController
@Slf4j
public class OrderHystrixController {
@Resource
private PaymentHystrixService PaymentHystrixService;
@GetMapping("/consumer/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id){
return PaymentHystrixService.paymentInfo_OK(id);
}
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
return PaymentHystrixService.paymentInfo_TimeOut(id);
}
}
当服务端20000个并发访问paymentInfo_TimeOut,此时消费者发送请求,无论哪里方法,都需要等待3秒。服务端发送请求出现的问题一致。
服务降级:当消费端发送请求到客户端,客户端出现超时、异常、宕机,此时需要有兜底处理方式,不至于整个服务崩溃。
Controller业务层
@RestController
@Slf4j
@DefaultProperties(defaultFallback = "paymentGlobalFallbackMethod")
public class OrderHystrixController {
@Resource
private PaymentHystrixService PaymentHystrixService;
@GetMapping("/consumer/payment/hystrix/ok/{id}")
@HystrixCommand
public String paymentInfo_OK(@PathVariable("id") Integer id){
int i = 10/0;
return PaymentHystrixService.paymentInfo_OK(id);
}
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
@HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod",commandProperties = {
//超过2秒及服务降级
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "2000")
})
public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
return PaymentHystrixService.paymentInfo_TimeOut(id);
}
//此方法为paymentInfo_TimeOut方法的专属降级方法
public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id){
return "对方支付系统繁忙,请稍后再试 ==>paymentInfo_TimeOut方法专属降级方法";
}
//全局公共的降级方法
public String paymentGlobalFallbackMethod(){
return "对方支付系统繁忙,请稍后再试 ==>全局公共降级方法";
}
}
消费者调用paymentInfo_OK方法(有10/0异常)
后台无报错信息,相当于直接跳转方法了
消费者调用paymentInfo_TimeOut方法(睡眠3秒,2秒超时)
结论和注意点:
业务层
@RestController
@Slf4j
//@DefaultProperties(defaultFallback = "paymentGlobalFallbackMethod")
public class OrderHystrixController {
@Resource
private PaymentHystrixService PaymentHystrixService;
@GetMapping("/consumer/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id){
int i = 10/0;
return PaymentHystrixService.paymentInfo_OK(id);
}
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
return PaymentHystrixService.paymentInfo_TimeOut(id);
}
}
为Feigin添加fallback类,此类实现本接口,每个实现类即为调用服务的降级方法
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = PaymentFallbackService.class)
public interface PaymentHystrixService {
@GetMapping("/payment/hystrix/ok/{id}")
String paymentInfo_OK(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/timeout/{id}")
String paymentInfo_TimeOut(@PathVariable("id") Integer id);
}
降级方法
@Component
public class PaymentFallbackService implements PaymentHystrixService {
@Override
public String paymentInfo_OK(Integer id) {
return "PaymentFallbackService 的 paymentInfo_OK的降级方法";
}
@Override
public String paymentInfo_TimeOut(Integer id) {
return "PaymentFallbackService 的 paymentInfo_TimeOut的降级方法";
}
}
访问paymentInfo_OK方法,消费者中有10/0
这里模拟服务端的处理情况
service服务
@Service
public class PaymentService {
//服务熔断
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"), //是否开启断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"), //请求次数
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), //时间范围
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"), //失败率达到多少后跳闸
})
public String paymentCircuitBreaker(Integer id){
if (id < 0){
throw new RuntimeException("*****id 不能负数");
}
String serialNumber = IdUtil.simpleUUID();
return Thread.currentThread().getName()+"\t"+"调用成功,流水号:"+serialNumber;
}
public String paymentCircuitBreaker_fallback(Integer id){
return "id 不能负数,请稍候再试,(┬_┬)/~~ id: " +id;
}
}
controller业务层
@RestController
@Slf4j
public class PaymentController {
@Resource
private PaymentService paymentService;
//===服务熔断
@GetMapping("/payment/circuit/{id}")
public String paymentCircuitBreaker(@PathVariable("id") Integer id){
String result = paymentService.paymentCircuitBreaker(id);
log.info("*******result:"+result);
return result;
}
}
开启条件
关闭条件