各位小伙伴们大家好,欢迎来到这个小扎扎的spring cloud专栏,在这个系列专栏中我对B站尚硅谷阳哥的spring cloud教程进行一个总结,鉴于 看到就是学到、学到就是赚到 精神,这波依然是血赚 ┗|`O′|┛
💡服务调用知识点速览
- 🍹 Hystrix
- 🍸 初识Hystrix
- 🍷 Hystrix是什么?
- 🍹 Hystrix三大概念
- 🍸 服务降级(fallback)
- 🍷 fallback是什么?
- 🍷 服务提供方实现服务降级
- 🍷 服务调用方实现服务降级
- 🍷 服务降级优化
- 🍸 服务熔断(break)
- 🍷 break是什么?
- 🍷 服务提供方实现服务熔断
- 🍸 服务限流(flowlimit)
- 🍷 flowlimit是什么?
- 🍹 Hystrix图形化监控
🍹 Hystrix
🍸 初识Hystrix
🍷 Hystrix是什么?
Java应用程序讲求“高内聚低耦合”,而spring cloud是一种微服务架构理念,将原来的一个应用程序拆分成许多微服务来调用,这样的话就可以满足“低耦合”的要求,但是随之而来的就是“服务雪崩”问题。
所谓的服务雪崩就是指,由于服务提供者不可用导致服务调用者不可用,并且在生产过程中,这种不可用逐渐扩大的现象。想要解决“服务雪崩”问题就需要用到Hystrix(豪猪)
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
"断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
🍹 Hystrix三大概念
🍸 服务降级(fallback)
🍷 fallback是什么?
所谓的服务降级就是当服务出现程序运行异常、超时、服务熔断触发服务降级、线程池/信号量打满等情况,此时应该返回一个符合预期的、可处理的备选响应(fallback),以提高用户的使用体验而不是长时间的等待请求或者返回一个超时异常页面。总而言之,当出现以上问题时,要有一个兜底方案来提高用户的使用体验
服务降级的解决方案可以分为服务提供方和服务调用方,两面都可以实现服务降级
🍷 服务提供方实现服务降级
第一步: 对服务提供方的service接口方法进行加强,主要就是针对可能出现超时等异常情况的接口,新建方法对其进行兜底,如果原接口出现问题则使用兜底方案进行反馈
使用@HystrixCommand注解的fallbackMethod属性指定兜底方法名,使用commandProperties属性的@HystrixProperty注解指定异常类型(超时异常和超时时间)
/**
* 超时访问,设置自身调用超时的峰值,峰值内正常运行,超过了峰值需要服务降级 自动调用fallbackMethod 指定的方法
* 超时异常或者运行异常 都会进行服务降级
* @param id
* @return
*/
@HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
})
public String paymentInfoTimeOut(Integer id) {
int second = 5;
try {
TimeUnit.SECONDS.sleep(second);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "线程池: " + Thread.currentThread().getName() + " paymentInfoTimeOut,id: " + id + "\t"
+ "O(∩_∩)O哈哈~" + " 耗时(秒): " + second;
}
public String paymentInfo_TimeOutHandler(Integer id) {
return "超时异常兜底方案!线程池: " + Thread.currentThread().getName() + " paymentInfo_TimeOutHandler,id: " + id + "\t";
}
第二步: 服务提供方的主启动类上使用@EnableCircuitBreaker注解开启“熔断器”,这样的话前面的配置才能生效
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
public class HystrixPaymentMain8001 {
public static void main(String[] args) {
SpringApplication.run(HystrixPaymentMain8001.class, args);
}
}
除了上述可以自定义超时时间的异常,出现其他运行时异常也会调用兜底方案返回
🍷 服务调用方实现服务降级
第一步: 使用配置文件开启hystrix
feign:
hystrix:
enabled: true
第二步: 服务调用方的controller加强,新建方法对其进行兜底,使用方式与服务提供方一样,主要就是服务调用方和服务提供方两套方案,可以实现两边定制化。
@GetMapping("/payment/hystrix/timeout/{id}")
@HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")
})
public String paymentInfoTimeOut(@PathVariable("id") Integer id) {
String result = paymentHystrixService.paymentInfoTimeOut(id);
return result;
}
public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id) {
return "服务调用方兜底方案!我是消费者80,对方支付系统繁忙请10秒钟后再试或者自己运行出错请检查自己,o(╥﹏╥)o";
}
第三步: 服务调用方的主程序类上使用@EnableHystrix注解开启hystrix
🍷 服务降级优化
解决冗余问题
前面的每一个方法都对应一个兜底方案,这样的话会显得代码十分臃肿,实际上很多的接口都可以使用一个兜底方案,于是我们就可以配置默认的兜底方案,在没有使用@HystrixCommand注解指定的时候,类中的所有接口都会走默认兜底方案
@DefaultProperties注解的defaultFallback属性指定默认兜底方法,如果类中存在@HystrixCommand注解中不使用属性指定特定兜底方案的情况,就说明这个接口使用是默认兜底方案
@RestController
@RequestMapping("consumer")
@Slf4j
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")
public class OrderHystrixController {
@Resource
private PaymentHystrixService paymentHystrixService;
@HystrixCommand
@GetMapping("/payment/hystrix/ex/{id}")
public String paymentInfoException(@PathVariable("id") Integer id) {
int age = 10/0;
String result = paymentHystrixService.paymentInfoException(id);
return result;
}
/**
* hystrix 全局fallback方法
* @return
*/
public String payment_Global_FallbackMethod() {
return "Global异常处理信息,请稍后再试,/(ㄒoㄒ)/~~";
}
}
解决耦合问题
上述操作中,原方案和兜底方案都在controller中定义,想要解决这个耦合问题可以使用一个类实现service接口,然后重写所有的接口方法,然后使用service接口上@FeignClient注解的fallback属性指定接口的实现类为兜底类
@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT", fallback = PaymentFallbackService.class)
public interface PaymentHystrixService {
@GetMapping("/payment/hystrix/ok/{id}")
String paymentInfoOK(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/timeout/{id}")
String paymentInfoTimeOut(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/ex/{id}")
String paymentInfoException(Integer id);
}
@Component
public class PaymentFallbackService implements PaymentHystrixService {
@Override
public String paymentInfoOK(Integer id) {
return "-----PaymentFallbackService fall back-paymentInfo_OK ,o(╥﹏╥)o";
}
@Override
public String paymentInfoTimeOut(Integer id) {
return "-----PaymentFallbackService fall back-paymentInfo_TimeOut ,o(╥﹏╥)o";
}
@Override
public String paymentInfoException(Integer id) {
return "-----PaymentFallbackService fall back-paymentInfoException ,o(╥﹏╥)o";
}
}
🍸 服务熔断(break)
🍷 break是什么?
微服务链路中某个微服务的请求达到最大访问之后,直接拒绝访问,然后调用服务降级的方法返回友好的提示;当检测到该节点微服务调用响应正常之后,还可以恢复链路的正常调用。hystrix会默认在服务5秒内调用失败20次后触发熔断机制,但是也可以使用配置对其进行修改。
🍷 服务提供方实现服务熔断
首先service层使用注解配置服务熔断的相关值和熔断时的备选方案,就以下代码为例:在10S的时间窗口期中,10次请求中失败率达到60%就会触发熔断启动备选方案。
如果在10S的时间窗口期中,请求次数不足10次,那么根本就不可能触发熔断器。当熔断器开启后所有的请求都不会被转发,一段时间窗口期之后(默认5秒,可以自定义配置)断路器切换为半开状态,此时会让其中一个请求进行转发,成功则关闭断路器,反之继续开启
@HystrixCommand(fallbackMethod = "paymentCircuitBreakerFallback", 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 paymentCircuitBreakerFallback(Integer id) {
return Thread.currentThread().getName() + "\t" + "id 不能负数或超时或自身错误,请稍后再试,/(ㄒoㄒ)/~~ id: " + id;
}
controller中调用service方法
@GetMapping("/circuit/{id}")
public String paymentCircuitBreaker(@PathVariable("id") Integer id) {
String result = paymentService.paymentCircuitBreaker(id);
log.info("****result: " + result);
return result;
}
🍸 服务限流(flowlimit)
🍷 flowlimit是什么?
在秒杀等高并发的操作下,为了防止大量请求一块发送过来,采用排队的方式,把请求按照顺序排队发送过来。由于hystrix已经停止更新,而且Alibaba的sentinel在进行服务限流的处理时比hystrix更加优秀,所以说这一部分知识可以放在后面进行学习。
🍹 Hystrix图形化监控
第一步: 新建一个模块用于监控,导入相关依赖
<dependencies>
<!--最主要的依赖,用于引入图形化dashboard-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<!-- 引入自己定义的api通用包 -->
<dependency>
<groupId>com.xiaochen</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.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
第一步: 配置文件配置端口号
server:
port: 9001
第三步: 主启动类开启HystrixDashboard,
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardMain9001 {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboardMain9001.class, args);
}
}
此外,所有的服务提供方也就是被监控的服务都要引入spring-boot-starter-actuator依赖,表示自己可以受监控。然后就是在服务的主启动类上要使用以下代码中的addUrlMappings配置受监控的路径
/**
* 注意:新版本Hystrix需要在主启动类中指定监控路径
* 此配置是为了服务监控而配置,与服务容错本身无关,spring cloud升级后的坑
* ServletRegistrationBean因为springboot的默认路径不是"/hystrix.stream",
* 只要在自己的项目里配置上下面的servlet就可以了
*
* @return ServletRegistrationBean
*/
@Bean
public ServletRegistrationBean getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
// 一启动就加载
registrationBean.setLoadOnStartup(1);
// 添加url
registrationBean.addUrlMappings("/hystrix.stream");
// 设置名称
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
启动监控模块、eureka模块、受监控模块之后,访问以下路径可以通过路径监控指定的服务,设置后点击下面的Monitor Stream按钮即可进入图形化监控界面
图形化界面各处代表的含义如下: