赞
踩
说明:虽然该技术已近停更了,但是由于之后的一些服务降级技术和熔断机制都是借鉴了hystrix的设计理念,所以我们还是连接一下hystrix的设计思想和理念。 hystrix+eureka
服务雪崩
hystrix是一个处理分布式系统的延迟和容错的开源库,能够保障再出问题的时候还可以维持高可用性,不会导致整体服务的失败、避免级联故障、以提高分布式系统的弹性
基本设计的思想:当发生服务调用故障时,向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长期的等待或者抛出异常,这样就会保证了服务调用方法的线程不会被长期的、不必要的占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
作用:
什么是服务降级
触发服务降级的情况
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.6.5</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>2021.0.1</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2021.0.1.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
<dependencies> <!--hystrix--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> <version>2.2.10.RELEASE</version> </dependency> <!--eureka客户端--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </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> <version>2.6.5</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <version>2.6.5</version> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
server: port: 8003 spring: application: name: cloud-provide-payment eureka: #注册中心 client: register-with-eureka: true service-url: defaultZone: http://localhost:7001/eureka instance: instance-id: payment8003 prefer-ip-address: true lease-renewal-interval-in-seconds: 2 lease-expiration-duration-in-seconds: 4 management: endpoints: web: exposure: include: "*"
加上@EnableCircuitBreaker 开启服务降级和熔断功能的注解
package com.qiumin; import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.context.annotation.Bean; @SpringBootApplication @EnableEurekaClient //开启eureka客户端 @EnableCircuitBreaker //开启服务降级和熔断功能 public class payment8003Application { public static void main(String[] args) { SpringApplication.run(payment8003Application.class, args); } }
@HystrixCommand @HystrixProperty 两个注解指定服务降级策略
package com.qiumin.service; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty; import org.springframework.stereotype.Service; import org.springframework.web.bind.annotation.PathVariable; import java.util.UUID; import java.util.concurrent.TimeUnit; @Service public class paymentHystrixService { //=========服务降级================ @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = { @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "3000") }) public String paymentInfo_ok(Integer id){ return "线程池: "+Thread.currentThread().getName()+" paymentInfo_ok,id: "+id+"\t"+"哈哈"; } @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = { @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "2000") }) public String paymentInfo_TimeOut(Integer id){ try { TimeUnit.MILLISECONDS.sleep(3000); //3000>2000即响应时间大于最久等待时间 触发服务降级 } catch (InterruptedException e) { e.printStackTrace(); } return "线程池: "+Thread.currentThread().getName()+" paymentInfo_TimeOut,id: "+id+"\t"+"哈哈"+"耗时(毫秒)"; } public String paymentInfo_TimeOutHandler(Integer id){ return "线程池:"+Thread.currentThread().getName()+"paymentInfo_TimeOutHandler,id: "+id+"\t"+"服务降级!!!"; } //==================================================================================================== //=========用于后面服务熔断测试================ @HystrixCommand(fallbackMethod = "paymentCircuitBreakerFallback",commandProperties = { @HystrixProperty(name="circuitBreaker.enabled",value = "true"), //开启熔断器 @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"), //10次请求 @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"),//时间范围 @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"),//请求次数中有60%错误,熔断器打开 }) public String paymentCircuitBreaker(@PathVariable("id") int id){ if(id<0){ throw new RuntimeException("****不能为负数****"); } String uuid = UUID.randomUUID().toString(); return Thread.currentThread().getName()+"\t"+"调用成功!!! 流水号为:"+uuid; } public String paymentCircuitBreakerFallback(@PathVariable("id") int id){ return "id 不能为负数,请稍后再试!!!"+id; } }
核心:为可能会出现超时或者异常的方法编写一个服务降级的方法并调用该方法 fallbackMethod =“兜底的方法” 。
注意:兜底的方法必须与其服务降级的方法,格式上必须一模一样,例如:返回值类型、参数类型等等。
package com.qiumin.controller; import com.qiumin.service.paymentHystrixService; 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; @RestController @Slf4j public class paymentHystrixController { @Autowired paymentHystrixService paymentHystrixService; @GetMapping("/payment/hystrix/ok/{id}") public String paymentInfo_ok(@PathVariable("id") Integer id){ return paymentHystrixService.paymentInfo_ok(id); } @GetMapping("/payment/hystrix/timeout/{id}") public String paymentInfo_TimeOut(@PathVariable("id") Integer id){ return paymentHystrixService.paymentInfo_TimeOut(id); } //用于服务熔断测试 @GetMapping("/payment/circuit/{id}") public String paymentCircuitBreaker(@PathVariable("id") int id){ return paymentHystrixService.paymentCircuitBreaker(id); } }
由于如果给每个方法的写一个兜底的方法会导致代码量的激增,所以可以定义一个默认的兜底的方法,如果没有个别定制兜底方法就用默认的兜底方法。
客户端可以有自己的服务降级和熔断策略,
由于我们使用openfeign客户端远程调用,为了解耦和代码量,我们可以在客户端的接口上做服务降级
具体实现:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>springcloud2022</artifactId> <groupId>com.qiu</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-consumer-hystrix-openfeign-order8888</artifactId> <properties> <maven.compiler.source>15</maven.compiler.source> <maven.compiler.target>15</maven.compiler.target> </properties> <dependencies> <!--hystrix--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> <version>2.2.10.RELEASE</version> </dependency> <!--开启openfeign--> <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> <!--web依赖--> <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> <version>2.6.5</version> </dependency> <!--热部署--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <version>2.6.5</version> <scope>runtime</scope> <optional>true</optional> </dependency> <!--lombok依赖--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!--test--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> </project>
server: port: 8888 spring: application: name: cloud-consumer-order eureka: client: register-with-eureka: true service-url: defaultZone: http://localhost:7001/eureka feign: circuitbreaker: enabled: true #开启服务降级
package com.qiumin.client; import com.qiumin.pojo.CommonResult; import com.qiumin.pojo.Payment; import com.qiumin.service.FallbackService; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; @Component @FeignClient(value = "CLOUD-PROVIDE-PAYMENT",fallback = FallbackService.class) //fallback指向我们写的服务 降级需调用方法的类 public interface paymentClient { @RequestMapping("/payment/query/{id}") CommonResult<Payment> query(@PathVariable("id") int id); @RequestMapping("/payment/create") CommonResult<Payment> create(Payment payment); }
package com.qiumin.service; import com.qiumin.client.paymentClient; import com.qiumin.pojo.CommonResult; import com.qiumin.pojo.Payment; import org.springframework.stereotype.Component; @Component public class FallbackService implements paymentClient { @Override public CommonResult<Payment> query(int id) { return new CommonResult(444,"请求获取数据超时或错误,进行了服务降级操作!!!",null); } @Override public CommonResult<Payment> create(Payment payment) { return new CommonResult(444,"请求获取数据超时或错误,进行了服务降级操作!!!",null); } }
package com.qiumin.controller; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.qiumin.client.paymentClient; import com.qiumin.pojo.CommonResult; import com.qiumin.pojo.Payment; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller @Slf4j public class OrderController { @Autowired paymentClient paymentClient; @RequestMapping("/consumer/payment/query/{id}") @ResponseBody public CommonResult<Payment> query(@PathVariable("id") int id){ log.info("进入了该方法!!"); return paymentClient.query(id); } }
什么是服务熔断,【类似保险丝】
服务熔断机制:当出现故障时,在固定时间窗口内,接口调用超时比率达到一个阈值,会开启熔断。,熔断一旦开启,这时就会拒绝请求包括正确的请求,直接执行本地的默认方法,达到服务降级的效果,熔断不会一直开启,熔断后一段时间,会继续放一批请求,这是处于半熔断状态,当进去的请求得到成功执行时,这时会认为服务故障已经清除,熔断就会关闭,服务正常进行。
Hystrix设计了三种状态:
图示:
涉及到熔断器三个重要的参数: 快照时间窗
、 请求总数阈值
、错误百分比阈值
qiumin
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。