【熔断限流组件resilience4j和hystrix】

本文涉及的产品
云数据库 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
相关文章
|
16天前
服务熔断器-Hystrix
服务熔断器-Hystrix
21 2
|
6月前
|
Java 机器人 Maven
【Java用法】微服务之间的相互调用方式之一,通过FeignClient客户端调用其他微服务的方法包含熔断器(Hystrix)
【Java用法】微服务之间的相互调用方式之一,通过FeignClient客户端调用其他微服务的方法包含熔断器(Hystrix)
81 0
|
7月前
|
开发框架 负载均衡 Java
Spring Cloud 介绍及负载均衡Ribbon、服务容错Hystrix 组件使用详解
Spring Cloud 介绍及负载均衡Ribbon、服务容错Hystrix 组件使用详解
118 0
|
3月前
|
监控 Java Sentinel
springcloud4-服务熔断hystrix及sentinel
springcloud4-服务熔断hystrix及sentinel
25 0
|
8月前
Ribbon、Feign、Hystrix超时&重试&熔断问题
在使用Ribbon、Feign、Hystrix组合时,因为配置的问题出现以下现象,让我的大脑CPU烧的不行不行(拿我老家话说就是“脑子ran滴奥”)
103 0
|
4月前
|
监控 负载均衡 数据可视化
SpringCloud - Hystrix断路器-服务熔断与降级和HystrixDashboard
SpringCloud - Hystrix断路器-服务熔断与降级和HystrixDashboard
29 0
|
4月前
|
监控 数据可视化 Java
【使用Hystrix实现服务容错和熔断】—— 每天一点小知识
【使用Hystrix实现服务容错和熔断】—— 每天一点小知识
|
7月前
|
负载均衡 Java 调度
Hystrix和Sentinel熔断降级设计理念
Hystrix和Sentinel熔断降级设计理念
67 0
|
9月前
|
监控 Java 微服务
手把手教你搭建SpringCloud项目(十一)集成Hystrix之服务熔断
手把手教你搭建SpringCloud项目(十一)集成Hystrix之服务熔断
|
5月前
|
缓存 运维 监控
微服务技术系列教程(22) - SpringCloud- 服务保护机制Hystrix
微服务技术系列教程(22) - SpringCloud- 服务保护机制Hystrix
60 0