3、服务的熔断降级(HyStrix)
能够保障一个依赖出现问题的情况下,不会导致整体的服务失败,避免级联故障,以提高分布式系统的弹性
是用来处理分布式系统的延迟和容错的开源库
服务器忙,不让客户端等待并且立刻返回一个友好的提示,调用方法返回一个符合预期的可处理的备选的相应
程序运行时异常,超时,服务熔断触发服务降级,线程池/信号量打满也会导致服务降级
-
服务熔断 beak
当访问量达到最大服务访问的时候,直接拒绝访问,拉闸限电,然后调用服务降级的方法返回友好的提示
-
服务限流 flowlimit
秒杀,高并发等操作同一时刻量过大,使其有序的进行
操作步骤:
-
创建Model
-
引入pom
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
还有eureka座位注册中心
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
编写application.yml
server:
port: 8001
spring:
application:
name: cloud-provider-hystrix-payment
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
编写启动类和服务
@Service
public class PaymentService {
//正常访问
public String paymentInfo_Ok(Integer id){
return "线程池"+Thread.currentThread().getName()+" paymentInfo_ok,id: "+id+"\t";
}
//使其超时测试
public String paymentInfo_TimeOut(String id){
int timeout=3;
try {
TimeUnit.SECONDS.sleep(timeout);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "线程池"+Thread.currentThread().getName()+" paymentInfo_error id"+id+"\t"+"耗时:"+timeout;
}
}
@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("*****result: "+result);
return result;
}
//超时的请求
@GetMapping("/payment/hystrix/error/{id}")
public String paymentInfo_error(@PathVariable String id){
String timeOut = paymentService.paymentInfo_TimeOut(id);
log.info("****result"+timeOut);
return timeOut;
}
}
@SpringBootApplication
//@EnableEurekaClient
public class PaymentHystrixMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8001.class,args);
}
}
创建消费者
pom.xml与application.yml
<!-- hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!--用oopfeign做负载 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--还是以Eureka作为注册中心-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
spring:
application:
name: cloud-consumer-feign-hystrix
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
server:
port: 88
这里使用的是Feign 所以要设置Service和Controller进行负载均衡
设置Service
@Component
@FeignClient("CLOUD-PROVIDER-HYSTRIX-PAYMENT")
public interface HystrixService {
@GetMapping(value = "/payment/hystrix/ok/{id}")
public String getPaymentById(@PathVariable("id") Long id);
@GetMapping("/payment/hystrix/error/{id}")
public String paymentInfo_error(@PathVariable("id") Long id);
}
设置Controller
@RestController
public class HystrixController {
@Resource
private HystrixService hystrixService;
@GetMapping("/consumer/select/{id}")
public String getPaymentId(@PathVariable("id") Long id){
return hystrixService.getPaymentById(id);
}
@GetMapping("/consumer/error/{id}")
public String getErrorPaymentId(@PathVariable("id") Long id){
return hystrixService.paymentInfo_error(id);
}
}
当有多个线程进行访问地时候容易产生异常会导致客户端访问减慢,所以就会有降级/容错/限流等产生,解决服务器变慢,出现(宕机)等问题,出现错误的时候不可以返回错误信息返回指定的界面
服务降级
对方服务器(8001)超时了,调用者(80)不能一直卡死等待,所以必须要有服务的降级
对方的服务器(8001)宕机了,调用了无法进行访问,所以要有服务的降级
调用者(80)访问的速度太慢,提供者出现多个线程产生堵塞,所以要有服务的降级
服务降级
使用@HystrixCommand
@HystrixCommand 注解 能对某个一个接口定制 Hystrix的超时时间
1、在8001的角度设置自身的峰值,峰值内可以正常的运行,如果超过了要有兜底的方法作为降级的fallback
提供者
//使其超时测试 在3秒内执行完成都是可以接受的,但是超过了3秒就会出现错误 只要这个服务不可用自己跳转到兜底的服务里面
@HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandle",commandProperties = {
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="3000")
})
//如果超时了会执行哪一个方法
public String paymentInfo_TimeOut(String id){
int timeout=5;
try {
TimeUnit.SECONDS.sleep(timeout);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "线程池"+Thread.currentThread().getName()+" paymentInfo_error id"+id+"\t"+"耗时:"+timeout;
}
public String paymentInfo_TimeOutHandle(String id){
return "我是当出现异常的时候进行兜底的服务";
}
在主启动类里面需要开启服务降级的活动
@EnableCircuitBreaker开启服务降级的活动
全局默认的兜底方法
如果有服务出现超时 宕机等这些方法并且没有指定
2、对于80消费者方向来说
feign:
hystrix:
enabled: true #在feign里面开启Hystrix
其余的步骤与消费者是一致的
定义全局的兜底方法(全局 fallback)
使用注解指定默认的全局的注解方法
@DefaultProperties(defaultFallback = )
在Controller里面
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")
public String payment_Global_FallbackMethod(){
return "这个是全局的接受的界面,如果没有指定的页面则执行这个页面";
}
这样就可以对那些没有指定具体兜底方法的方法指定一个兜底的方法
配置指定方法的兜底方法
对每一个服务提供的方法进行解耦,这样就不用在Controller里面对每一个想要指定方法的方法进行兜底实现了指定兜底方法的全局性
进行解耦:根据Service接口新建一个实现了他的类,就可以对Service管理的提供者的服务进行统一的管理,就不用进行对每一个绑定的方法都设置一个兜底的方法了,就可以将Controller里面指定的兜底方法给取消了
feign:
hystrix:
enabled: true #在feign里面开启Hystrix
@Component
public class HystrixServiceImpl implements HystrixService {
@Override
public String getPaymentById(Long id) {
return "我是getPaymentById方法的指定的兜底方法";
}
@Override
public String paymentInfo_error(Long id) {
return "我是paymentInfo_error方法的指定的兜底方法";
}
}
一定要放入到容器里面
实现了指定方法接口的类,这些类就是指定方法的兜底方法
最后在Service接口里面指定默认的兜底方法
@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = HystrixServiceImpl.class)
public interface HystrixService {
@GetMapping(value = "/payment/hystrix/ok/{id}")
public String getPaymentById(@PathVariable("id") Long id);
@GetMapping("/payment/hystrix/error/{id}")
public String paymentInfo_error(@PathVariable("id") Long id);
}
@EnableFeignClients
@SpringBootApplication
@EnableHystrix
@EnableCircuitBreaker
启动类里面
服务熔断
熔断机制应对雪崩效应的是一种微服务链路保护机制,当扇出链路的某个微服务出错不可用或者响应的时间太长了就会对服务进行降级,进而熔断该节点微服务的调用,快速返回错误的响应信息,当检测到该节点微服务调用响应正常后恢复调用的链路
@HyStrixCommand
@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 不能负数,请稍后再试,/(ㄒoㄒ)/~~ id: " +id;
}
@GetMapping("/payment/circuit/{id}")
public String paymentCircuitBreaker(@PathVariable("id") Integer id) {
String result = paymentService.paymentCircuitBreaker(id);
log.info("****result: " + result);
return result;
}
当短时间错误的次数超过了6次就会触发一个熔断机制这样再次访问正确的也不会有效果 只有过了时间窗口期才会重新有效果
熔断的类型:
- 熔断打开:请求不在进行调用当前的服务,内部设置时钟一般为MTTR(平均故障的处理时间),当打开的时长达到所设值的时钟的时候就会进入到版熔断的状态
- 熔断关闭:熔断关闭不会对服务进行熔断
- 熔断断开:部分请求根据规则调用当前的服务,如果请求成功并且符合规则则认为当前的服务恢复正常,关闭熔断
熔断器开启或者关闭的条件:
当满足一定的阈值的时候(默认是10秒超过了20次的请求)
当失败率达到了一定的时候(默认10秒内超过50%的请求失败)
到达以上阈值的时候断路器就会开启
当开启之后所有请求都不会再进行转发
一段时候后默认是5秒,这个时候断路器是半开的状态,会让其中的一个请求进行转发,如果成功断路器会关闭如果失败继续开启重复4~5
服务限流
后期在高级部分进行讲解
服务监控hystrixDashboard
model +pom+yml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
注意:要想实现监控对应的提供者(8001这些)都需要添加监控依赖配置actuator与web进行连用
端口设置9001
http://localhost:9001/hystrix进行测试 是否配置成功
这里通过9001来监控8001 所以8001要配置actuar的配置文件
在主启动类里面
@SpringBootApplication
@EnableCircuitBreaker
//EnableCircuitBreaker 实现服务熔断与降级
//@EnableEurekaClient
public class PaymentHystrixMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8001.class,args);
}
/**
*此配置是为了服务监控而配置,与服务容错本身无关,springcloud升级后的坑
*ServletRegistrationBean因为springboot的默认路径不是"/hystrix.stream",
*只要在自己的项目里配置上下面的servlet就可以了
*/
@Bean
public ServletRegistrationBean getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
}
在9001.hystrix里面访问http://localhost:8001//hystrix.stream显示监控的实时情况