分布式所面临的问题
Hystris是什么
Hystris能干什么
服务降级(fallback)
服务器忙,请稍后再试,不让客户等待并立刻返回一个友好提示,fallback
哪些情况下会触发降级
①程序运行异常
②超时
③服务熔断触发服务降级
④线程池/信号量打满也会导致服务降级
服务熔断(break)
就是保险丝.就好比是保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示
接近实时的监控
服务限流(flowlimit)
秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行
Hystrix服务降级案例
1 新建一个带有Hystrix熔断框架的微服务提供者
在pom.xm中添加依赖,和以往的pom文件的主要区别是引入了hystrix依赖
<dependencies>
<!--新增hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
2配置application.yml
server:
port: 8001
eureka:
client:
register-with-eureka: true #表识不向注册中心注册自己
fetch-registry: true #表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
service-url:
# defaultZone: http://eureka7002.com:7002/eureka/ #设置与eureka server交互的地址查询服务和注册服务都需要依赖这个地址
defaultZone: http://eureka7001.com:7001/eureka/
# server:
# enable-self-preservation: false
spring:
application:
name: cloud-provider-hystrix-payment
# eviction-interval-timer-in-ms: 2000
3写主启动类
@SpringBootApplication
@EnableEurekaClient
public class PaymentHystrixMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8001.class,args);
}
}
4编写业务类
直接写业务实现类了(开发过程中应该写接口调用实现类)
service里的包含两个方法,一个代表正常的业务,一个模拟响应很慢的业务(不健康的);
controller中的两个方法分别调用service的方法
@Service
public class PaymentService {
/**
* 模拟正常访问的业务
* @param id
* @return
*/
public String payemntInfo_OK(Integer id){
return "线程池: "+Thread.currentThread().getName()+"paymentInfo_OK,id: "+id+"\t"+"😄";
}
/**
* 模拟超时业务
* @param id
* @return
*/
public String payemntInfo_TimeOut(Integer id){
int timeNumber=3;
try {
TimeUnit.SECONDS.sleep(timeNumber);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "线程池: "+Thread.currentThread().getName()+"paymentInfo_TimeOut,id: "+id+"\t"+"😄"+"耗时(s):"+timeNumber;
}
}
@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.payemntInfo_OK(id);
log.info("**********result:"+result);
return result;
}
@GetMapping("/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
String result = paymentService.payemntInfo_TimeOut(id);
log.info("**********result:"+result);
return result;
}
}
5启动Eureka注册中心以及刚刚写好的服务提供者,观察现象
分别访问正确的业务和超时的业务(不健康的),可以发现,一个响应速度很快,一个需要等待3秒后也可以正确响应.
现在模拟有很大的并发量访问那个不健康的业务,此时在访问正常相应的业务时会发现也出现了卡顿情况.这是因为这两个业务在同一个微服务中,当大量请求访问了同一个业务时,系统资源会集中处理这个业务,拖慢了其他业务.
其中需要等待的业务可以想象成生产过程中某个业务的调用可能出现了网路延迟、死机等情况,那么应该给消费端一个响应,不应该让消费者等待时间过长,在这种情况下,就需要Hystrix的熔断降级了.
其实上面还只是直接访问服务提供者,如果外界消费者再访问的话,消费者访问正常的业务也会出现相同的问题,
有时还会出现超时报错.因为tomcat线程池里面的工作线程被挤占完毕了。
此时就需要用Hystrix的熔断降级了
注:一般Hystrix用在消费端
6出现问题后的解决方案
对方服务提供者超时了,调用者不能一直卡死等待,必须有服务降级;
对方服务提供者down机了,调用者不能一直卡死等待,必须有服务降级;
对方服务提供者ok,调用者自己出现故障或者自我要求(自己的等待时间设置的值小于服务提供者消耗的时间),自己处理降级;
7服务降级配置
7.1给服务提供者配置服务降级
①假设不健康的业务需要执行5秒钟,Hystrix配置等待3秒,若没有相应执行兜底方法(预先设置号的方法);
②配置好后还没有生效,需要在主启动类上添加@EnableCircuitBreaker
注解激活配置
不只是超时,如果程序运行异常直接报错,也会执行兜底方案payemntInfo_TimeOutHandler
上面是对服务端自身设置的服务降级,但一般Hystrix放在客户端进行配置,下面是对消费端配置服务降级
注:热部署方式对java代码的改动明显,但对@HystrixCommand
内属性的修改建议重启微服务
7.2给消费者配置服务降级
8.用@DefaultProperties(defaultFallback="")注解配置全局通用的服务降级兜底方法
解决代码膨胀问题
①之前每个方法配置一个服务降级方法.技术上没问题,但是看上去很傻x
②除了个别重要核心业务有专属的服务降级方法外,其他普通的可以通过@DefaultProperties(defaultFallback="")
统一跳转到统一的处理结果页面
通用的和独享的格子分开,避免了代码膨胀,合理减少了代码量
使用方法简单的理解总结:
1️⃣在需要处理服务降级的方法上添加@HystrixCommand
注解,如果不加该注解,那么这个方法报错就会正常抛异常,正常的逻辑流程走,例如:
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
// @HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod",commandProperties = {
// @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1500") //3秒钟以内就是正常的业务逻辑
// })
@HystrixCommand
public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
int a=10/0;
String result = paymentHystrixService.paymentInfo_TimeOut(id);
return result;
}
2️⃣写一个通用的fallback方法(降级兜底方法),例如:
//全局的fallback方法
public String payment_Global_FallbackMethod(){
return "Global异常处理信息,请稍后";
}
3️⃣在类上添加@DefaultProperties(defaultFallback="")
注解,其中defaultFallback
的值为兜底的方法名,例如:
@DefaultProperties(defaultFallback="payment_Global_FallbackMethod")
public class OrderHystrixController {
...
...
}
如果访问的方法开启了需要处理服务降级,在未指定服务降级兜底方法的情况下,将执行全局的payment_Global_FallbackMethod方法;如果访问的方法自己指定了兜底方法,当需要服务降级时就执行自己指定的方法;
9.写接口统一实现服务降级
这样写的好处是解耦
一般的在消费者端通过Feign调用服务提供者,必定会有FeignClient接口,controller访问服务提供者肯定会经过这个接口,所以在这个Feign接口上做文章,新建一个类实现该接口(FeignClient的接口),通过在FeignClient的接口上设置fallback属性值来实现服务降级方法的指定:
@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = PaymentFallbackService.class)
public interface PaymentHystrixService {
@GetMapping("/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id);
}
@Component
public class PaymentFallbackService implements PaymentHystrixService {
@Override
public String paymentInfo_OK(Integer id) {
return "--------PaymentFallbackService,---paymentInfo_OK---fallback";
}
@Override
public String paymentInfo_TimeOut(Integer id) {
return "PaymentFallbackService,-----paymentInfo_TimeOut,----fallback-";
}
}
需要在yml文件中添加配置:
feign:
hystrix:
enabled: true
Hystrix服务熔断
以服务提供者为例
在server中配置:
在controller中:写调用上面的server的代码来演示server中出现异常情况
@GetMapping("/payment/circuit/{id}")
public String paymentCircuitBreaker(@PathVariable("id") Integer id){
String result = paymentService.paymentCircuitBreaker(id);
log.info("**************result: "+result);
return result;
}
}
启动服务,当传入的id值为整数时服务运行正常,如果传入错误时会抛运行时异常
此时仅仅是服务降级,当连续访问失败的次数达到设定的值时,就会触发熔断机制,表现的现象是再传入正确的参数也会执行降级的处理方法;
短路器在什么情况下起作用:
短路器开启和关闭的条件:
短路器打开之后:
ALL配置:
Hystrix服务限流
Hystrix工作流程
服务监控HystrixDashboard
概述:
构建微服务监控程序–HystrixDashboard图形化监控
①新建module,在pom中添加依赖:
<dependencies>
<!--新增hystrix dashboard-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
②在application.yml中指定端口号:
server:
port: 9001
③所有需要监控的微服务提供者pom文件中都需要添加监控依赖配置:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
④访问:localhost:9001/hystrix,会看到豪猪哥页面