复杂分布式体系结构中的应用程序有数十个依赖关系,每一个依赖关系在某些时候将不可避免的失败。
服务雪崩
多个微服务之间调用的时候,假如微服务A调用微服务B和微服务C,微服务B和微服务C又调用其他的微服务,这就是所谓的"扇出"。
如果扇出的链路上某个微服务的调用响应的时间过长或者不可用,对微服A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的"雪崩效应"。
对于高流量的应用来说,单一的后端依赖可能会导致所有的服务器上的所有资源都在几秒钟内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统。
所以,通常当你发现一个模块下的某个实例失败后,这时候这个模块依然还会接收流量,然后这个有问题的模块还调用了其他的模块,这样就会发生级联故障,或者叫雪崩。
Hystrix是什么:
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,
Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的d性。
“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(Fallback),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
主要功能分为
服务降级:简单来说,就是万一服务器发生错误,他可以返回一个统一的返回结果,不让用户看到服务器内部出错
服务熔断:简单来说,如何一个服务再请求时发生了高并发,他会返回”服务繁忙,请稍后再试“,然后将他设置成再10s(时间可以自定义)内无法访问,就是他再 次,发出请求的时候他不会被服务器接收,而是被直接熔断掉,并直接返回”服务繁忙,请稍后再试“,要等到10s后才可以再次访问
接近实时的监控:就是实时监控整个服务的实时情况
哪些情况会触发服务降级:
使用降级方法:
给8001端口服务添加降级方法(使用如下注解):
-
程序运行异常
-
超时自动降级
-
服务熔断触发服务降级
-
线程池/信号量打满也会导致服务降级
-
人工降级
服务熔断Breaker
类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示
就是保险丝
服务的降级->进而熔断->恢复调用链路
服务限流Flowlimit
秒杀高并发等 *** 作,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行
hystrix案例新建一个子项目(cloud-provider-hystrix-payment8001):
引入相关的依赖:
org.springframework.cloud spring-cloud-starter-netflix-hystrixorg.springframework.cloud spring-cloud-starter-netflix-eureka-clientcom.atguigu.springcloud cloud-api-commons${project.version} org.springframework.boot spring-boot-starter-weborg.springframework.boot spring-boot-starter-actuatororg.springframework.boot spring-boot-devtoolsruntime true org.projectlombok lomboktrue org.springframework.boot spring-boot-starter-testtest 添加配置文件
server: port: 8001 spring: application: name: cloud-hystrix-payment-service eureka: client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://localhost:7001/eureka/
创建启动类,并将他设置成Eureka客户端:
添加服务接口,并添加两个方法,一个是直接访问的,一个是访问超时的,让他休眠三秒:
package com.atguigu.springcloud.service; public interface PaymentService { public String paymentInfo_OK(Integer id); public String payment_Timeout(Integer id); }
实现服务接口:
package com.atguigu.springcloud.service.impl; import com.atguigu.springcloud.service.PaymentService; import org.springframework.stereotype.Service; import java.util.concurrent.TimeUnit; @Service public class PaymentServiceImpl implements PaymentService { @Override public String paymentInfo_OK(Integer id) { return "线程池:"+Thread.currentThread().getName()+" paymentInfo_OK,id: "+id+"\t"+"哈哈哈" ; } @Override public String payment_Timeout(Integer id) { int timeNumber = 3; try { TimeUnit.SECONDS.sleep(timeNumber); } catch (Exception e) { e.printStackTrace(); } return "线程池:"+Thread.currentThread().getName()+" paymentInfo_TimeOut,id: "+id+"\t"+"呜呜呜"+" 耗时(秒)"+timeNumber; } }
添加controller控制器:
添加两个http请求接口:
package com.atguigu.springcloud.controller; import com.atguigu.springcloud.entities.Payment; import com.atguigu.springcloud.service.PaymentService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; @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/timeout/{id}") public String paymentInfo_TimeOut(@PathVariable("id") Integer id){ String result = paymentService.payment_Timeout(id); log.info("*******result:"+result); return result; } }
利用浏览器测试两个接口:
我们可以使用jmeter来对我们的服务进行压力测试
我们再创建一个80端口去访问刚才新建的8001端口的服务,让他跨服务访问创建项目cloud-consumer-feign-hystrix-order80,并修改他的pom文件:
如何解决服务器宕机和超时问题:
超时导致服务器变慢(转圈)
超时不再等待
出错(宕机或程序运行出错)
出错要有兜底
解决
-
对方服务(8001)超时了,调用者(80)不能一直卡死等待,必须有服务降级
-
对方服务(8001)down机了,调用者(80)不能一直卡死等待,必须有服务降级
-
对方服务(8001)OK,调用者(80)自己出故障或有自我要求(自己的等待时间小于服务提供者),自己处理降级
使用降级方法:
给8001端口服务添加降级方法(使用如下注解):
//超时降级演示 @HystrixCommand(fallbackMethod = "payment_TimeoutHandler",commandProperties = { @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="5000") //5秒钟以内就是正常的业务逻辑 })
fallbackMethod:后面是的是一定降级方法,就是超时或者宕机了,让服务不断续请求,而 是返回特定的数据数据
在给启动类添加降级的注解:
@EnableCircuitBreaker
再利用浏览器发出请求
如果要给一个跨服务调用的服务添加降级处理,先需要到他的配置文件中去添加容错开启模式,如给80服务添加降级:
先给他添加文件配置:
feign: hystrix: enabled: true #如果处理自身的容错就开启。开启方式与生产端不一样。
给启动类添加降级注解@EnableHystrix
package com.atguigu.springcloud.controller; import com.atguigu.springcloud.service.PaymentHystrixService; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; @RestController @Slf4j public class OrderHystrixController { @Resource private PaymentHystrixService paymentHystrixService; @GetMapping("/consumer/payment/hystrix/ok/{id}") public String paymentInfo_OK(@PathVariable("id") Integer id){ String result = paymentHystrixService.paymentInfo_OK(id); log.info("*******result:"+result); return result; } // 为此方法添加上降级 @HystrixCommand(fallbackMethod = "payment_TimeoutHandler",commandProperties = { @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="1500") //超过1.5秒就降级自己 } ) @GetMapping("/consumer/payment/hystrix/timeout/{id}") public String paymentInfo_TimeOut(@PathVariable("id") Integer id){ String result = paymentHystrixService.paymentInfo_TimeOut(id); log.info("*******result:"+result); return result; } public String payment_TimeoutHandler(Integer id) { return "线程池:"+Thread.currentThread().getName()+" payment_TimeoutHandler,系统繁忙,请稍后再试\t o(╥﹏╥)o "; } }
设置默认降级方法:
先给服务的整个类添加默认的降级方法的注解:
以上这种给跨服务写降级方法会和它自身的业务逻辑混乱在一起,所以我们最好给PaymentHystrixService这个接口添加降级方法--------------详情请看:@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod") //全局的
https://www.bilibili.com/video/BV1f341167hQ?p=22&spm_id_from=pageDriver 时间:19:38
什么是熔断:
就是在接收请求的时候看他是否会超时或者发生异常,如何发生那就等待10s(自定义),在这10s内,所以有的从那个ip地址发出这请求都会被直接返回降级的结果,等到10s后再次进行请求,如何还是会发生超时/异常,那就继续熔断直到下一个10s秒后再发出请求
使用:修改cloud-proider-hystrix-payment8001:
给他的PaymentService文件添加一个方法(实现他的方法),并给他设置熔断:
package com.atguigu.springcloud.service;
public interface PaymentService {
public String paymentInfo_OK(Integer id);
public String payment_Timeout(Integer id);
//熔断
public String paymentCircuitBreaker(Integer id);
}
实现类中的熔断实现
//服务熔断
// fallbackMethod="降级方法名"
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties =
{
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"), //是否开启断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"), //10s内这个上服务出现了10次的请求降级,对会触发熔断器
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), //断路10s以后开始尝试是否恢复,默认5s
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"), //出错百分比阈值,当达到此阈值后,开始短路。默认50%
}
)
public String paymentCircuitBreaker(Integer id){
if (id < 0){
throw new RuntimeException("*****id 不能负数");
}
String serialNumber = IdUtil.simpleUUID();//hutool.cn工具包
return Thread.currentThread().getName()+"\t"+"调用成功,流水号:"+serialNumber;
}
public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){
return "id 不能负数,请稍候再试,(┬_┬)/~~ id: " +id;
}
利用浏览器进行测试:
第一次利用id是正数进行访问
第二次利用id是负数进行访问:
然后再10s内连续用负数访问10次,再去利用正数访问:
Hsytrix服务监控
除了隔离依赖服务的调用以外,Hystrix还提供了准实时的调用监控(Hystrix Dashboard),Hystrix会持续地记录所有通过Hystrix发起的请求的执行信息,并以统计报表和图形的形式展示给用户,包括每秒执行多少请求多少成功,多少失败等。Netflix通过hystrix-metrics-event-stram项目实现了对以上指示的监控。Spring Cloud也提供了Hystrix Dashboard的整合,对监控内容转化成可视化界面。
新建仪表盘模块:
cloud-consumer-hystrix-dashboard9001:
导入依赖:
org.springframework.cloud
spring-cloud-starter-netflix-hystrix-dashboard
org.springframework.boot
spring-boot-starter-actuator
org.springframework.boot
spring-boot-devtools
runtime
true
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
添加配置文件:
server:
port:9001
添加启动类,并开户仪表盘的功能:
package com.atguigu.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
@SpringBootApplication
@EnableHystrixDashboard//开户仪表盘的功能
public class HystrixDashboardMain9001 {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboardMain9001.class,args);
}
}
启动服务并访问:
http://localhost:9001/hystrix
我们想要给监控哪个服务,我们就需要给这个子模块添加如下的依赖:
org.springframework.boot
spring-boot-starter-actuator
给要进行监控的服务子模块的启动类上添加如下的配置:
/**
*此配置是为了服务监控而配置,与服务容错本身无关,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;
}
就拿cloud-provider-hystrix-payment8001来说,给他添加相应的依赖,并给他的启动类添加如下的配置:
观察监控窗口
9001监控8001
http://localhost:9001/hystrix
http://localhost:8001/hystrix.stream
测试地址
http://localhost:8001/payment/circuit/31
http://localhost:8001/payment/circuit/-31
上述测试通过
ok
先访问正确地址,再访问错误地址,再正确地址,会发现图示断路器都是慢慢放开的
监控结果,成功
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)