【熔断限流组件resilience4j和hystrix】

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 【熔断限流组件resilience4j和hystrix】



起因

阿里Sentinel团队不稳定,超过了一半年的时间没有更新了,虽然认为一个成熟产品没必要老更新,在特定的阶段能完成他的历史使命就好,并且sentinel开源这块依旧还在维护开发中。但是它是为了kpi开放出来的,生怕哪天不维护了,总感觉阿里是在培养用户习惯之后,让你绑定阿里的生态,再到阿里云割韭菜,开源嘛,本来也是无偿用,人家没有义务像商业软件那样永久更新,之前开源社区好几个作者就是因为不更新或者更新慢被别人喷不搞了,所以个人觉得有必要多掌握几个限流熔断组件。

resilience4j落地实现

pom.xml依赖

如果你的springboot版本是2的使用以下依赖:

<!-- 熔断限流resilience4j-->
<dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-spring-boot2</artifactId>
    <version>2.2.0</version>
</dependency>

如果你的springboot版本是3的使用以下依赖:

<!-- 熔断限流resilience4j-->
<dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-spring-boot3</artifactId>
    <version>2.2.0</version>
</dependency>

application.yml配置

resilience4j:
  retry: # 重试策略机制配置
    instances: # 定义多个重试策略实例
      retryApi: # 第一个重试策略重试名称
        max-attempts: 3 # 操作失败最大重试次数为3
        wait-duration: 1s # 每次重试等待时间1秒
  circuitbreaker:
    instances: # 定义多个断路器实例
      circuitBreakerApi: # 第一个断路器实例名称
        registerHealthIndicator: true # 配置健康指示器
        slidingWindowSize: 10 # 滑动窗口大小
        minimumNumberOfCalls: 5 # 最小调用次数
        permittedNumberOfCallsInHalfOpenState: 3 # 半开状态下允许的调用次数
        slidingWindowType: TIME_BASED # 滑动窗口类型
        automaticTransitionFromOpenToHalfOpenEnabled: true # 是否自动从开启状态转换为半开状态
        waitDurationInOpenState: 1s # 开启状态下等待时间
        failureRateThreshold: 20 # 失败率阈值,失败率20%时,断路器打开
        eventConsumerBufferSize: 10 # 事件消费者缓冲区大小
        ignoreExceptions: # 忽略的异常列表
          - java.io.IOException
  ratelimiter:
    instances: # 定义多个限流策略实例
      rateLimiterApi: # 第一个限流策略实例名称
        limitForPeriod: 10000 # 限制周期内的请求数量10000
        limitRefreshPeriod: 10s # 限制刷新周期,10秒一个周期
        timeoutDuration: 500ms # 超时时间为0.5秒,请求超过限制客户端立即收到超时响应,不等待
        subscribeForEvents: AFTER_SUCCESS # 订阅事件类型
        eventConsumerBufferSize: 10 # 事件消费者缓冲区大小

接口使用

@Retry(name = "retryApi",fallbackMethod = "fallbackRedPackage")
@CircuitBreaker(name = "circuitBreakerApi",fallbackMethod = "fallbackRedPackage")
@RateLimiter(name = "rateLimiterApi",fallbackMethod = "fallbackRedPackage")
@PostMapping(value = "/redPackage")
public Result redPackage(@RequestBody RedPackegeRainVo redPackage) {
    //红包唯一标识\红包的key
    String redPackageKey = redPackage.getRedPackageKey();
    //用户id\用户token
    String userId = redPackage.getUserId();
    //使用StringBuilder拼接字符串,作为红包的键(key)
    String redAppend = StringUtil.StringAppend(RedPackageRainConstant.RED_PACKAGE_KEY, redPackageKey);
    //从redis缓存中获取红包池中的红包
    String partRedPackage = redisUtil.leftPop(redAppend);
    //判断是否为空,不为空进入后续流程;为空直接返回
    if (StringUtils.isNotEmpty(partRedPackage)) {
        //将红包的key和用户的id作为存储redis缓存已抢红包池的键(key)
        String redConsumeAppend = StringUtil.StringAppend(RedPackageRainConstant.RED_PACKAGE_CONSUME_KEY, redPackageKey,":", RedPackageRainConstant.RED_PACKAGE_USER_ID, userId);
        //存入redis缓存并且设置过期时间(使用了redis事务,保证原子性,因为操作简单、依赖关系简单,使用使用redis事务比使用 Lua 脚本更适合当前场景)
        redisUtil.multiStr(redConsumeAppend,partRedPackage,1,TimeUnit.DAYS);
        //自适应(根据当前机器的线程数适配核心线程数和最大线程数)全局线程池
        ThreadPoolExecutor.getThreadPoolExecutor().execute(new Runnable() {
            @Override
            public void run() {
                ConcurrentHashMap concurrentHashMap =  pool.acquire(); // 从对象池中获取一个ConcurrentHashMap实例
                concurrentHashMap.put(RedPackageRainConstant.RED_PACKAGE_KEY,redPackageKey);
                concurrentHashMap.put(RedPackageRainConstant.RED_PACKAGE_USER_ID,userId);
                concurrentHashMap.put(RedPackageRainConstant.RED_PACKAGE_VALUE,partRedPackage);
                //将ConcurrentHashMap对象转换成字符串。
                String convertToString = StringUtil.convertToString(concurrentHashMap);
                //释放对象
                pool.release(concurrentHashMap);
                //发送MQ消息 发送字符串比发送对象的网络传输更小。这是因为字符串可以被序列化为字节数组,而对象需要被序列化为字节数组,并包含对象的类信息和其他序列化数据。因此,发送字符串可以节省网络传输的带宽。
                messageProducer.sendMessage(RedPackageRainConstant.TOPIC,convertToString);
            }
        });
        return Result.build(ResultCodeEnum.SUCCESS.getCode(), ResultCodeEnum.SUCCESS);
    }
    return Result.error(ResultCodeEnum.RED_PACKAGE_FINISHED.getCode(),ResultCodeEnum.RED_PACKAGE_FINISHED.getMessage());
}
public Result fallbackRedPackage(Throwable throwable) {
    log.error("fallback RedPackage info:",throwable.getMessage());
    return Result.error(ResultCodeEnum.RED_PACKAGE_ERROR.getCode(),ResultCodeEnum.RED_PACKAGE_ERROR.getMessage());
}

hystrix 落地实现

pom.xml依赖

<!-- hystrix -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

启动类上添加注解

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableDiscoveryClient
// 开启断路器,开启Hystrix容错能力
@EnableCircuitBreaker
public class SpringCloudHystrixDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringCloudHystrixDemoApplication.class, args);
    }
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

接口上使用

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.hystrix.contrib.javanica.conf.HystrixPropertiesManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@RestController
@RequestMapping("/hystrix")
public class HystrixController {
    @Autowired
    private RestTemplate restTemplate;
    /**
     * HystrixCommand 开启Hystrix命令 当方法执行失败时,使用Hystrix逻辑处理
     * fallbackMethod 当方法执行失败时,调用此方法。
     */
    @GetMapping("/getNacosConfigure")
    @HystrixCommand(fallbackMethod = "defaultFallbackMethod")
    public String getNacosConfigure() {
        return restTemplate.getForObject("http://nacos-config/getNacosConfigure", String.class);
    }
    private String defaultFallbackMethod() {
        return "方法执行失败啦,Hystrix起作用了";
    }
    // 信号量隔离
    @HystrixCommand(
            commandProperties = {
                    // 超时时间,默认1000ms
                    @HystrixProperty(name = HystrixPropertiesManager.
                            EXECUTION_ISOLATION_THREAD_TIMEOUT_IN_MILLISECONDS, value = "5000"),
                    // 信号量隔离
                    @HystrixProperty(name = HystrixPropertiesManager.
                            EXECUTION_ISOLATION_STRATEGY, value = "SEMAPHORE"),
                    // 信号量最大并发
                    @HystrixProperty(name = HystrixPropertiesManager.
                            EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value = "5")
            },//回退方法
            fallbackMethod = "defaultFallbackMethod"
    )
    @GetMapping("/semaphoreIsolation")
    public String semaphoreIsolation() {
        return "信号量隔离" + Thread.currentThread().getName();
    }
    // 线程池隔离
    @HystrixCommand(groupKey = "productService", // 服务名称,相同名称使用同一个线程池
            commandKey = "selectById",              // 接口名称,默认为方法名
            threadPoolKey = "productServiceListPool",    // 线程池名称,相同名称使用同一个线程池
            commandProperties = {
                    // 超时时间,默认1000ms
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000")
            },
            threadPoolProperties = {
                    // 线程池大小
                    @HystrixProperty(name = "coreSize", value = "5"),
                    // 等待队列长度(最大队列长度,默认值-1)
                    @HystrixProperty(name = "maxQueueSize", value = "100"),
                    // 线程存活时间,默认1min
                    @HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),
                    // 超出等待队列阈值执行拒绝策略
                    @HystrixProperty(name = "queueSizeRejectionThreshold", value = "100")
            },//回退方法
            fallbackMethod = "defaultFallbackMethod"
    )
    @GetMapping("/threadPoolIsolation")
    public String threadPoolIsolation() {
        return "线程池隔离" + Thread.currentThread().getName();
    }
    // 服务熔断
    @HystrixCommand(
            commandProperties = {
                    // 请求数阈值:在快照时间窗口内,必须满足请求阈值数才有资格熔断。打开断路器的最少请求数,默认20个请求。
                    //意味着在时间窗口内,如果调用次数少于20次,即使所有的请求都超时或者失败,断路器都不会打开
                    @HystrixProperty(name = HystrixPropertiesManager.
                            CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD, value = "10"),
                    // 错误百分比阈值:当请求总数在快照内超过了阈值,且有一半的请求失败,这时断路器将会打开。默认50%
                    @HystrixProperty(name = HystrixPropertiesManager.
                            CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE, value = "50"),
                    // 快照时间窗口:断路器开启时需要统计一些请求和错误数据,统计的时间范围就是快照时间窗口,默认5秒
                    @HystrixProperty(name = HystrixPropertiesManager.
                            CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS, value = "5000")
            },
            fallbackMethod = "defaultFallbackMethod"
    )
    @GetMapping("/serviceFuse")
    public String serviceFuse() {
        return "服务熔断" + Thread.currentThread().getName() + 
        LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_TIME);
    }
    // 服务降级
    @HystrixCommand(fallbackMethod = "defaultFallbackMethod")
    @GetMapping("/serviceDegradation ")
    public String serviceDegradation() {
        return "服务降级" + Thread.currentThread().getName() + 
        LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_TIME);
    }
}



相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
7月前
服务熔断器-Hystrix
服务熔断器-Hystrix
61 2
|
6月前
springCloud之服务降级熔断Hystrix、OpenFeign
springCloud之服务降级熔断Hystrix、OpenFeign
370 0
|
3月前
|
存储 NoSQL 调度
|
3月前
|
XML 监控 Java
Spring Cloud全解析:熔断之Hystrix简介
Hystrix 是由 Netflix 开源的延迟和容错库,用于提高分布式系统的弹性。它通过断路器模式、资源隔离、服务降级及限流等机制防止服务雪崩。Hystrix 基于命令模式,通过 `HystrixCommand` 封装对外部依赖的调用逻辑。断路器能在依赖服务故障时快速返回备选响应,避免长时间等待。此外,Hystrix 还提供了监控功能,能够实时监控运行指标和配置变化。依赖管理方面,可通过 `@EnableHystrix` 启用 Hystrix 支持,并配置全局或局部的降级策略。结合 Feign 可实现客户端的服务降级。
197 23
|
4月前
|
缓存 监控 负载均衡
一文讲明Hystrix熔断器
这篇文章详细阐述了Hystrix熔断器的原理和应用,解释了分布式系统中服务雪崩的问题,并展示了如何在Spring Cloud框架中使用Hystrix进行熔断和降级处理。
一文讲明Hystrix熔断器
|
7月前
|
监控 微服务
Hystrix熔断器设计思想(学习笔记)附(服务监控hystrixDashboard识图)
Hystrix熔断器设计思想(学习笔记)附(服务监控hystrixDashboard识图)
60 0
|
7月前
|
监控 Java Sentinel
springcloud4-服务熔断hystrix及sentinel
springcloud4-服务熔断hystrix及sentinel
72 0
|
7月前
|
监控 负载均衡 数据可视化
SpringCloud - Hystrix断路器-服务熔断与降级和HystrixDashboard
SpringCloud - Hystrix断路器-服务熔断与降级和HystrixDashboard
69 0
|
缓存 运维 监控
微服务技术系列教程(22) - SpringCloud- 服务保护机制Hystrix
微服务技术系列教程(22) - SpringCloud- 服务保护机制Hystrix
110 0
|
7月前
|
监控 Java API
Spring cloud Hystrix 、Dashboard、API(zuul)相关报错
Spring cloud Hystrix 、Dashboard、API(zuul)相关报错
84 2