SpringCloud学习记录(二)负载均衡,服务熔断,服务降级,服务限流

    技术2025-07-07  14

    Ribbon实现负载均衡

    负载均衡,英文名称为Load Balance,其含义就是指将负载(工作任务)进行平衡、分摊到多个操作单元上进行运行,例如FTP服务器、Web服务器、企业核心应用服务器和其它主要任务服务器等,从而协同完成工作任务。

    导入pom依赖

    单独导入

    <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> <version>2.2.1.RELEASE</version> </dependency>

    整合依赖spring-cloud-starter-netflix-eureka-client里面也包含了ribbon的依赖

    <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> <version>2.2.1.RELEASE</version> </dependency> 在主启动类所在包的上一级创建一个包,用来创建自定义负载均衡策略类 书写自定义负载均衡策略类,先继承AbstractLoadBalancerRule抽象类,该类是IRule接口的一个抽象实现类,继承它,然后自定义自己的负载均衡策略,注入spring容器即可覆盖原有的轮询策略。 public class HcodeRule extends AbstractLoadBalancerRule { //每个服务,访问5次,然后循环所有服务 private AtomicInteger total = new AtomicInteger(0); //服务被调用的次数 private AtomicInteger currentIndex = new AtomicInteger(0); //当前被调用服务的下标 public Server choose(ILoadBalancer lb, Object key) { if (lb == null) { return null; } Server server = null; while (server == null) { if (Thread.interrupted()) { return null; } //获得活着的服务 List<Server> upList = lb.getReachableServers(); //获得全部服务 List<Server> allList = lb.getAllServers(); int serverCount = allList.size(); if (serverCount == 0) { return null; } // //生成区间随机数 // int index = chooseRandomInt(serverCount); // server = upList.get(index); //从或者的服务中随机获取一个 /*============================================*/ if(total.get()<5){ server = upList.get(currentIndex.get()); getAndIncrement(total); }else{ total.set(0); getAndIncrement(currentIndex); if(currentIndex.get()>=upList.size()){ currentIndex.set(0); } server = upList.get(currentIndex.get()); getAndIncrement(total); } /*==============================================*/ if (server == null) { Thread.yield(); continue; } if (server.isAlive()) { return (server); } // Shouldn't actually happen.. but must be transient or a bug. server = null; Thread.yield(); } return server; } //自旋锁防止高并发情况数据不一致性 private final int getAndIncrement(AtomicInteger atomicInteger){ int current; int next; do { current = atomicInteger.get(); next = current >= 2147483647 ? 0 : current + 1; }while (!atomicInteger.compareAndSet(current,next)); //第一个参数是期望值,第二个参数是修改值是 return next; } protected int chooseRandomInt(int serverCount) { return ThreadLocalRandom.current().nextInt(serverCount); } @Override public void initWithNiwsConfig(IClientConfig clientConfig) { // TODO Auto-generated method stub } @Override public Server choose(Object key) { return choose(getLoadBalancer(), key); } }

    再将该策略类注入IOC容器,当然如果不想写自定义策略类,那就使用原有的IRule的实现类,将其注入IOC容器即可.下面两种二选一

    @Configuration public class MyRule { @Bean //使用自定义的 public IRule HcodeRule(){ return new HcodeRule(); } //============================================================ //AvailabilityFilteringRule : 先过滤掉出现故障的服务器,对剩下的服务进行轮询 //RoundRobinRule 轮询 默认设置 //RandomRule 随机 //WeightedResponseTimeRule 权重 //RetryRule:先按照轮询获取,若获取服务失败,则在指定时间内进行重试 //随机访问,只要将实现类注入到ICO容器,即可覆盖 @Bean //使用原有的实现类 public IRule myRule(){ return new RandomRule(); } } 主启动类上加上注解,让消费端服务能去使用自定义的负载均衡策略 @SpringBootApplication @EnableEurekaClient //在微服务启动的时候就能去加载我们自定义的负载均衡Ribbon类,name为微服务提供者注册到注册中心的服务名 @RibbonClient(name="SPRINGCLOUD-PROVIDER-DEPT",configuration = MyRule.class) public class DeptConsumer_80 { public static void main(String[] args) { SpringApplication.run(DeptConsumer_80.class,args); } }

    相关概念理解

    服务熔断:当某个服务提供者出现问题(出现运行时无法处理的异常或者某些操作时间过长),卡死了,不能让用户一直等待,需要调用备用的响应方法向调用方返回特定响应,防止服务链路某一微服务模块卡死,导致整体服务出现雪崩。(发生熔断一般是某个微服务模块,也就是微服务提供者)服务降级:服务降级一般也是有多种情况,访问量过大,微服务出现异常,或者微服务响应超时等等,一般服务降级触发熔断都在消费者端设置,当某个服务不可用时,直接返回备用响应。服务限流:也是服务降级的一种方式,限流,比如秒杀场景,不能访问用户瞬间都访问服务器,限制一次只可以有多少请求。

    Hystrix 实现服务熔断和服务降级

    微服务提供者导入pom依赖

    单独导入依赖 org.springframework.cloud:spring-cloud-starter-hystrix:2.2.1.RELEASE

    <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> <version>2.2.1.RELEASE</version> </dependency>

    使用eureka整合客户端pom就不用再导入单独的hystrix

    <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> 微服务提供者的controller层 @RestController @DefaultProperties(defaultFallback = "Global_FallbackMethod") //没有配置专属熔断器方法,就走全局熔断器 public class TestController { @HystrixCommand //没加特定方法,走全局 @PostMapping("/global") public String test1(){ return "(●'◡'●)全局熔断器方法未触发"; } //全局响应Fallback方法 服务降级触发熔断 public Object Global_FallbackMethod(){ return "┭┮﹏┭┮全局熔断器方法触发了,呜呜呜!"; } //断路器 @HystrixCommand( fallbackMethod = "CircuitBreaker_fallback" , 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") //失败率到达多少后跳闸 }) @GetMapping("/local") public String test2(){ return "(●'◡'●)特定断路器方法未触发~"; } public String CircuitBreaker_fallback(){ //参数可以跟原接口方法参数一致 return "┭┮﹏┭┮特定限制方法熔断器触发了,呜呜呜~"; } } 微服务提供者的主启动类加上开启断路器的注解 @EnableCircuitBreaker //添加对服务熔断的支持 客户端导入的所需依赖跟提供者差不多,使用openfeign进行服务调用通信。 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>

    然后配置yml,让feign支持hystrix

    feign: hystrix: enabled: true #如果处理自身的容错就开启。开启方式与生产端不一样。

    然后再服务层接口加上注解。

    @FeignClient(value = "hystrix-test-provider",fallback = DeptFallbackService.class) //服务名,备用的响应类 @Component public interface testService { @GetMapping("/global") public String test1(); }

    兜底用的备用响应类

    @Component // 服务降级 public class DeptFallbackService implements testService{ public String test1() { return "┭┮﹏┭┮该服务被降级了,目前无法使用"; } }

    最后,主启动类添加@EnableHystrix

    @EnableHystrix

    Sentinel实现服务熔断,降级,限流

    老样子,hystrix也是停止更新了,所以换新的才是王道~况且阿里巴巴的sentinel就是继承hystrix,两者挺像的。

    官网下载:https://github.com/alibaba/Sentinel/releases特性: 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。

    下载好jar包sentinel-dashboard-1.7.0.jar后,直接命令行运行即可~ 注意:该boot服务默认为8080端口号。启动后,访问http://localhost:8080,账号密码都是sentinel。

    服务提供者

    导入pom <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</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> </dependency> 配置yml server: port: 8666 spring: application: name: cloudalibaba-sentinel-service cloud: nacos: discovery: server-addr: localhost:8848 # nacos注册中心的地址 sentinel: transport: dashboard: localhost:8080 # sentinel的地址 port: 8719 #默认8719,假如被占用了会自动从8719开始依次+1扫描。直至找到未被占用的端口 # 暴露监控 management: endpoints: web: exposure: include: '*' 主启动类添加注解 @EnableDiscoveryClient @SpringBootApplication 控制层针对服务限流的备用方法,至于限流方式,就直接再sentinel控制台直接添加即可,@SentinelResource的value参数就是对应的资源名,里面也可以加fallback调用断路器备用响应方法,使用跟@HystrixCommand差不多。 @RestController @SentinelResource(fallbackClass = "Global_FallbackMethod") public class TestController { @SentinelResource(value = "test",blockHandlerClass = CustomerBlockHandle.class,blockHandler ="deal_test") @GetMapping("/test") public String test(@RequestParam(value = "p") String p){ return "(●'◡'●)没有被限流"; } // public String test(String p,BlockException blockException){ // return "-------------deal list ┭┮﹏┭┮"; // } //全局响应Fallback方法 服务降级触发熔断 public Object Global_FallbackMethod(){ return "┭┮﹏┭┮全局熔断器方法触发了,呜呜呜!"; } }

    统一处理限流的备用方法类

    public class CustomerBlockHandle { public static String deal_test(String p, BlockException blockException){ return "┭┮﹏┭┮被限流了"; } } 启动后,再sentinel控制台并没有发现该服务,但是nacos却已经发现该服务成功注册了,是因为需要发送一次请求,网页访问该服务,sentinel才能监控到(Sentinel采用的是懒加载)。

    微服务消费者(使用feign方式)

    导入pom <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-sentinel</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</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> </dependency> 配置yml server: port: 80 spring: application: name: nacos-test-consumer cloud: nacos: discovery: server-addr: localhost:8848 sentinel: transport: dashboard: localhost:8080 port: 8719 service-url: nacos-user-service: http://cloudalibaba-sentinel-service #对Feign的支持 feign: sentinel: enabled: true 配置接口以及接口的服务降级备用响应的实现类 @FeignClient(value = "cloudalibaba-sentinel-service",fallback = DeptFallbackService.class) @Component public interface TestService { @GetMapping("/test") public String test(); } @Component // 服务降级 public class DeptFallbackService implements DeptService { public String test() { return "┭┮﹏┭┮服务被降级了,不能使用~"; } } controller层服务调用 @Autowired private TestService testService ; @GetMapping(value = "/consumer/test") public String test(){ return testService.test(); } 主启动类 @EnableDiscoveryClient @SpringBootApplication @EnableFeignClients public class test80 { public static void main(String[] args) { SpringApplication.run(test80.class, args); } }
    Processed: 0.015, SQL: 9