【Spring Cloud】服务熔断

2024-05-27 18:28
文章标签 java 服务 spring cloud 熔断

本文主要是介绍【Spring Cloud】服务熔断,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • 服务雪崩效应
    • 服务雪崩效应形成的原因及应对策略
    • 小结
  • Hystrix
    • 介绍
    • Hystrix可以做什么
      • 1.资源隔离
      • 2.请求熔断
      • 3.服务降级
    • 小结
  • Hystrix实现服务降级
    • 方式一:@HystrixCommand注解方式
      • 1.服务提供者
        • 1.1业务接口和业务实现中添加方法hystrixTimeout
        • 1.2控制器中处理/provider/hystrix/timeout/{id}请求
      • 2.服务消费者
        • 2.1demo-user-consumer添加依赖
        • 2.2在启动类上添加@EnableHystrix
        • 2.3在UserFeignClient接口创建远程调用的方法hystrixTimeout
        • 2.4在RPC调用方法上加上注解@HystrixCommand
      • 3.添加ribbon配置
      • 4.yml添加配置
      • 5.测试
        • 5.1场景一服务正常调用
        • 5.2场景二当被调服务停止运行时
        • 5.3场景三当调取服务超时时
        • 5.4场景四发生异常
      • 改进服务消费者-定义全局服务降级
    • 方式二:定义统一fallback接口
      • 1.创建降级(兜底)处理类UserFeignClientFallBack
      • 2.为接口指定容错处理
      • 3.测试
    • 方式三:定义fallbackFactory
      • 1.创建接口UserFeignClientWithFactory继承UserFeignClient接口
      • 2.HystrixClientFactory类实现FallbackFactory接口
      • 3.并使用内部匿名方法类,实现UserFeignClient接口中的方法
      • 4.测试:发生异常

服务雪崩效应

  • 在微服务架构中通常会有多个服务调用,基础服务的故障可能会导致级联故障,进而造成整个系统不可用的情况,这种现象被称为服务雪崩效应。
  • 服务雪崩效应是一种因“服务提供者”的不可用导致“服务消费者”的不可用,并将不可用逐渐放大的过程。
    在这里插入图片描述

服务雪崩效应形成的原因及应对策略

原因描述应对策略
硬件故障如服务器宕机,机房断电,光纤被挖断等多机房容灾、异地多活等。
程序Bug如程序逻辑导致内存泄漏,JVM长时间FullGC等修改程序bug、及时释放资源等。
缓存击穿一般发生在应用重启,所有缓存失效时,以及短时间内大量缓存失效时。大量的缓存不命中,使请求直击后端服务,造成服务提供者超负荷运行,引起服务不可用。缓存预加载、缓存异步加载等。
流量激增如异常流量,用户重试,代码逻辑重试加大流量等。服务自动扩容、流量控制(服务限流、关闭重试)等。
同步等待服务间采用同步调用模式,同步等待造成的资源耗尽。资源隔离、MQ解耦、不可用服务调用快速失败等。资源隔离通常指不同服务调用采用不同的线程池;不可用服务调用快速失败一般通过熔断器模式结合超时机制实现。

小结

  • 如果一个应用不能对来自依赖的故障进行隔离,那该应用本身就处在被拖垮的风险中。
  • 因此,为了构建稳定、可靠的分布式系统,我们的服务应当具有自我保护能力,当依赖服务不可用时,当前服务启动自我保护功能,从而避免发生雪崩效应。
  • 在SpringCloud中,将重点介绍使用Hystrix解决同步等待的雪崩问题。

Hystrix

介绍

  • Hystrix 是由Netflix发布,旨在应对复杂分布式系统中的延时和故障容错,基于Apache License 2.0协议的开源的程序库,目前托管在GitHub上。
  • 在分布式系统中,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
  • 官网:https://github.com/Netflix/Hystrix
  • Hystrix的中文含义是豪猪, 因其背上长满了刺,而拥有自我保护能力。

Hystrix可以做什么

1.资源隔离

  • 货船为了进行防止漏水和火灾的扩散,会将货仓分隔为多个, 这种资源隔离减少风险的方式被称为:Bulkheads(舱壁隔离模式)。
  • Hystrix将同样的模式运用到了服务调用者上.如下图所示:
    在这里插入图片描述- 资源隔离:在Hystrix中, 主要通过线程池来实现资源隔离. 通常在使用的时候我们会根据调用的远程服务划分出多个线程池。
  • 在一个高度服务化的系统中,我们实现的一个业务逻辑通常会依赖多个服务,比如: 商品详情展示服务会依赖商品服务, 价格服务和商品评论服务 如图所示:
    在这里插入图片描述
  • 调用三个依赖服务会共享商品详情服务的线程池。如果其中的商品评论服务不可用, 就会出现线程池里所有线程都因等待响应而被阻塞, 从而造成服务雪崩。如图所示:
    在这里插入图片描述
  • Hystrix通过将每个依赖服务分配独立的线程池进行资源隔离, 从而避免服务雪崩。如下图所示, 当商品评论服务不可用时, 即使商品服务独立分配的20个线程全部处于同步等待状态,也不会影响其他依赖服务的调用。
    在这里插入图片描述

2.请求熔断

  • “断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
  • 当Hystrix Command请求后端服务失败数量超过一定比例(默认50%), 断路器会切换到开路状态(Open)。这时所有请求会直接失败而不会发送到后端服务。
  • 断路器保持在开路状态一段时间后(默认5秒), 自动切换到半开路状态(HALF-OPEN)。这时会判断下一次请求的返回情况, 如果请求成功, 断路器切回闭路状态(CLOSED), 否则重新切换到开路状态(OPEN)。
  • Hystrix的断路器就像我们家庭电路中的保险丝, 一旦后端服务不可用, 断路器会直接切断请求链, 避免发送大量无效请求影响系统吞吐量, 并且断路器有自我检测并恢复的能力.
    在这里插入图片描述

3.服务降级

  • 服务降级是当我们的某个微服务响应时间过长,或者不可用了,讲白了也就是那个微服务调用不了了,我们不能把错误信息返回出来,或者让他一直卡在那里,所以要在准备一个对应的策略(一个方法),当发生这种问题的时候我们直接调用这个方法来快速返回这个请求,不让他一直卡在那 。
  • Fallback相当于是降级操作. 对于查询操作, 我们可以实现一个fallback方法, 当请求后端服务出现异常的时候, 可以使用fallback方法返回的值. fallback方法的返回值一般是设置的默认值或者来自缓存,告知后面的请求服务不可用了,不要再来了。
    在这里插入图片描述

小结

  • 服务降级:服务器忙,请稍后再试,不让客户端等待并立刻返回一个友好提示。会出现服务降级的情况:程序运行异常、超时、服务熔断触发服务降级、线程池/信号量打满也会导致服务降级。
  • 服务熔断:类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示。
  • 接近实时的监控:服务的降级->进而熔断->恢复调用链路
  • 服务限流:秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行。

Hystrix实现服务降级

方式一:@HystrixCommand注解方式

1.服务提供者

1.1业务接口和业务实现中添加方法hystrixTimeout
public interface UserService {boolean checkUser() throws Exception;String findFeignTimeOut() throws Exception;String hystrixTimeout(Integer id) throws Exception;
}
@Service
public class UserServiceImpl implements UserService {@Overridepublic boolean checkUser() throws Exception {return true;}@Overridepublic String findFeignTimeOut() throws Exception {//模拟当前业务,当前业务的执行需要3秒钟TimeUnit.SECONDS.sleep(3);return "模拟当前业务,当前业务的执行需要3秒钟";}@Overridepublic String hystrixTimeout(Integer id) throws Exception {//TimeUnit.SECONDS.sleep(6);//return "当前线程的名称:"+Thread.currentThread().getName()+"===,id:"+id;throw new Exception("发生了异常:" + id);}
}
1.2控制器中处理/provider/hystrix/timeout/{id}请求
@RestController
@Slf4j
public class UserController {@Resourceprivate UserService userService;@PostMapping("/provider/checkUser")public boolean checkUser() throws Exception{return userService.checkUser();}@PostMapping("/provider/findFeignTimeOut")public String findFeignTimeOut() throws Exception{return userService.findFeignTimeOut();}@GetMapping("/provider/hystrixTimeout/{id}")public String hystrixTimeout(@PathVariable("id") Integer id) throws Exception{return userService.hystrixTimeout(id);}
}

2.服务消费者

2.1demo-user-consumer添加依赖
        <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-hystrix</artifactId></dependency>
2.2在启动类上添加@EnableHystrix
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@EnableHystrix
public class DemoUserConsumerApplication {public static void main(String[] args) {SpringApplication.run(DemoUserConsumerApplication.class, args);}
}
2.3在UserFeignClient接口创建远程调用的方法hystrixTimeout
@FeignClient(name = "DEMO-USER-PROVIDER")
public interface UserFeignClient {/*** 远程调用DEMO-USER-PROVIDER中的/provider/checkUser服务* 调用的路径名称必须和服务提供者保证高度一致* @return* @throws Exception*/@PostMapping("/provider/checkUser")boolean checkUser(@RequestParam String id) throws Exception;@PostMapping("/provider/findFeignTimeOut")String findFeignTimeOut() throws Exception;@GetMapping("/provider/hystrixTimeout/{id}")String hystrixTimeout(@RequestParam @PathVariable("id") Integer id) throws Exception;
}
2.4在RPC调用方法上加上注解@HystrixCommand

注解:@HystrixCommand(fallbackMethod = “hystrixTimeoutFallback”),这就指定了该方法的降级方法是哪个。

@Service
public class LocalUserServiceImpl implements LocalUserService {@Resourceprivate UserFeignClient userFeignClient;@Overridepublic boolean doLogin(String id) throws Exception {//通过http协议调用的服务return userFeignClient.checkUser(id);}@Overridepublic String queryFeignTimeOut() throws Exception {return userFeignClient.findFeignTimeOut();}/*** 当远程调用provider的服务时,如果服务提供者正确响应,获取provider的返回值即可* 当远程调用provider的服务时,如果服务提供者响应失败或出现异常,则返回fallbackMethod中的降级方法* fallbackMethod属性指定的就是降级的方法* @param id* @return* @throws Exception*/@HystrixCommand@Overridepublic String hystrixTimeout(Integer id) throws Exception {return userFeignClient.hystrixTimeout(id);}public String fallbackHystrixTimeout(Integer id){return "服务降级,服务器繁忙,请耐心等待";}
}

3.添加ribbon配置

@Configuration
public class RibbonConfiguration {@Beanpublic IClientConfig ribbonClientConfig() {DefaultClientConfigImpl config = new DefaultClientConfigImpl();return config;}@Beanpublic IRule myRule(IClientConfig config) {config.setProperty(IClientConfigKey.Keys.ConnectTimeout,5000);// 设置连接超时为5000毫秒config.setProperty(IClientConfigKey.Keys.ReadTimeout,5000);// 设置读取超时为5000毫秒return new RoundRobinRule();}
}

4.yml添加配置

在application. yml中添加配置开启容错

server:port: 8085
spring:application:name: demo-user-consumer
eureka:client:service-url:defaultZone: http://localhost:7776/eureka/
logging:level:cn.kgc.feignclient: debug
feign:hystrix:enabled: true
hystrix:command:default:execution:isolation:thread:timeoutInMilliseconds: 5000

5.测试

5.1场景一服务正常调用

在这里插入图片描述

5.2场景二当被调服务停止运行时

在这里插入图片描述

5.3场景三当调取服务超时时

在这里插入图片描述

在这里插入图片描述

5.4场景四发生异常

在这里插入图片描述在这里插入图片描述

改进服务消费者-定义全局服务降级

  • 每一个业务方法都有一个对应的兜底方法,代码会膨胀
  • 和业务代码混在了一起,容易混乱在业务实现类添加注解@DefaultProperties(defaultFallback = “hystrixTimeOutFallback”)
  • 在方法上加注解@HystrixCommand
@Service
@DefaultProperties(defaultFallback = "fallbackHystrixTimeout")
public class LocalUserServiceImpl implements LocalUserService {@Resourceprivate UserFeignClient userFeignClient;@Overridepublic boolean doLogin(String phone,String password) throws Exception {return userFeignClient.checkUser(phone, password);}@Overridepublic List<String> searchUser(Map<String, Object> map) throws Exception {return userFeignClient.findUserList(map);}@HystrixCommand@Overridepublic String queryFeignTimeOut() throws Exception {return userFeignClient.findFeignTimeOut();}/*** 当远程调用provider的服务时,如果服务提供者正确响应,获取provider的返回值即可* 当远程调用provider的服务时,如果服务提供者响应失败或出现异常,则返回fallbackMethod中的降级方法* fallbackMethod属性指定的就是降级的方法* @param id* @return* @throws Exception*/@HystrixCommand@Overridepublic String hystrixTimeout(Integer id) throws Exception {return userFeignClient.hystrixTimeout(id);}public String fallbackHystrixTimeout(){return "服务降级,服务器繁忙,请耐心等待";}
}

方式二:定义统一fallback接口

1.创建降级(兜底)处理类UserFeignClientFallBack

  • 实现UserFeignClient接口,返回预先处理好的默认值。
@Component
public class UserFeignClientFallback implements UserFeignClient {/*** 当调用checkUser方式时,会调用DEMO-USER-PROVIDER的/provider/checkUser API接口* 调用的路径要求DEMO-USER-PROVIDER中路径保持一致* 如果调用的方法包含参数,需要和DEMO-USER-PROVIDER中的参数保持一致** @param phone* @param password* @return* @throws Exception*/@Overridepublic boolean checkUser(String phone, String password) throws Exception {return false;}@Overridepublic String findFeignTimeOut() throws Exception {return "服务调用失败,服务降级测试";}@Overridepublic String hystrixTimeout(Integer id) throws Exception {return "服务调用失败,服务降级测试";}
}

2.为接口指定容错处理

在这里插入图片描述

  • demo-user-consumer-hystrix项目的UserFeignClient接口中,通过@FeignClient注解的fallback属性指定容错处理类。
/*** 通过name或value属性指定远程调用的服务的名字(存在于注册中心)*/
@FeignClient(value = "DEMO-USER-PROVIDER",fallback = UserFeignClientFallback.class)
public interface UserFeignClient {/*** 当调用checkUser方式时,会调用DEMO-USER-PROVIDER的/provider/checkUser API接口* 调用的路径要求DEMO-USER-PROVIDER中路径保持一致* 如果调用的方法包含参数,需要和DEMO-USER-PROVIDER中的参数保持一致* @return* @throws Exception*/@PostMapping("/provider/checkUser")boolean checkUser(@RequestParam String phone, @RequestParam String password) throws Exception;@PostMapping("/provider/findFeignTimeOut")String findFeignTimeOut() throws Exception;@GetMapping("/provider/hystrixTimeout/{id}")String hystrixTimeout(@PathVariable("id") Integer id) throws Exception;
}

3.测试

方式三:定义fallbackFactory

  1. fallback 的方式的缺陷:不能捕获异常打印堆栈信息,不利于问题排查
  2. fallbackFactory 推荐:可以捕获异常信息并返回默认降级结果。可以打印堆栈信息。

1.创建接口UserFeignClientWithFactory继承UserFeignClient接口

public interface UserFeignClientWithFactory extends UserFeignClient {
}

2.HystrixClientFactory类实现FallbackFactory接口

3.并使用内部匿名方法类,实现UserFeignClient接口中的方法

@Component
@Slf4j
public class HystrixClientFactory implements FallbackFactory<UserFeignClient> {@Overridepublic UserFeignClient create(Throwable cause) {log.info("发生了异常:{},异常消息:{}",cause,cause.getMessage());return new UserFeignClientWithFactory() {@Overridepublic boolean checkUser(String phone, String password) throws Exception {return false;}@Overridepublic List<String> findUserList(Map<String, Object> params) throws Exception {return new ArrayList<>();}@Overridepublic String findFeignTimeOut() throws Exception {return "fallbackFactory方式实现服务降级";}@Overridepublic String hystrixTimeout(Integer id) throws Exception {return "fallbackFactory方式实现服务降级";}};}
}

4.测试:发生异常

在这里插入图片描述

这篇关于【Spring Cloud】服务熔断的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1008249

相关文章

JAVA SE包装类和泛型详细介绍及说明方法

《JAVASE包装类和泛型详细介绍及说明方法》:本文主要介绍JAVASE包装类和泛型的相关资料,包括基本数据类型与包装类的对应关系,以及装箱和拆箱的概念,并重点讲解了自动装箱和自动拆箱的机制,文... 目录1. 包装类1.1 基本数据类型和对应的包装类1.2 装箱和拆箱1.3 自动装箱和自动拆箱2. 泛型2

SpringBoot操作MaxComputer方式(保姆级教程)

《SpringBoot操作MaxComputer方式(保姆级教程)》:本文主要介绍SpringBoot操作MaxComputer方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的... 目录引言uqNqjoe一、引入依赖二、配置文件 application.properties(信息用自己

JavaScript中的Map用法完全指南

《JavaScript中的Map用法完全指南》:本文主要介绍JavaScript中Map用法的相关资料,通过实例讲解了Map的创建、常用方法和迭代方式,还探讨了Map与对象的区别,并通过一个例子展... 目录引言1. 创建 Map2. Map 和对象的对比3. Map 的常用方法3.1 set(key, v

Javascript访问Promise对象返回值的操作方法

《Javascript访问Promise对象返回值的操作方法》这篇文章介绍了如何在JavaScript中使用Promise对象来处理异步操作,通过使用fetch()方法和Promise对象,我们可以从... 目录在Javascript中,什么是Promise1- then() 链式操作2- 在之后的代码中使

JAVA虚拟机中 -D, -X, -XX ,-server参数使用

《JAVA虚拟机中-D,-X,-XX,-server参数使用》本文主要介绍了JAVA虚拟机中-D,-X,-XX,-server参数使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有... 目录一、-D参数二、-X参数三、-XX参数总结:在Java开发过程中,对Java虚拟机(JVM)的启动参数进

Java中数组转换为列表的两种实现方式(超简单)

《Java中数组转换为列表的两种实现方式(超简单)》本文介绍了在Java中将数组转换为列表的两种常见方法使用Arrays.asList和Java8的StreamAPI,Arrays.asList方法简... 目录1. 使用Java Collections框架(Arrays.asList)1.1 示例代码1.

Java中使用注解校验手机号格式的详细指南

《Java中使用注解校验手机号格式的详细指南》在现代的Web应用开发中,数据校验是一个非常重要的环节,本文将详细介绍如何在Java中使用注解对手机号格式进行校验,感兴趣的小伙伴可以了解下... 目录1. 引言2. 数据校验的重要性3. Java中的数据校验框架4. 使用注解校验手机号格式4.1 @NotBl

SpringBoot自定义注解如何解决公共字段填充问题

《SpringBoot自定义注解如何解决公共字段填充问题》本文介绍了在系统开发中,如何使用AOP切面编程实现公共字段自动填充的功能,从而简化代码,通过自定义注解和切面类,可以统一处理创建时间和修改时间... 目录1.1 问题分析1.2 实现思路1.3 代码开发1.3.1 步骤一1.3.2 步骤二1.3.3

SpringBoot基于沙箱环境实现支付宝支付教程

《SpringBoot基于沙箱环境实现支付宝支付教程》本文介绍了如何使用支付宝沙箱环境进行开发测试,包括沙箱环境的介绍、准备步骤、在SpringBoot项目中结合支付宝沙箱进行支付接口的实现与测试... 目录一、支付宝沙箱环境介绍二、沙箱环境准备2.1 注册入驻支付宝开放平台2.2 配置沙箱环境2.3 沙箱

使用Java发送邮件到QQ邮箱的完整指南

《使用Java发送邮件到QQ邮箱的完整指南》在现代软件开发中,邮件发送功能是一个常见的需求,无论是用户注册验证、密码重置,还是系统通知,邮件都是一种重要的通信方式,本文将详细介绍如何使用Java编写程... 目录引言1. 准备工作1.1 获取QQ邮箱的SMTP授权码1.2 添加JavaMail依赖2. 实现