【Sentinel】Springboot整合Sentinel、Socket进行熔断限流(生产级熔断限流)

文章目录

更多相关内容可查看

此项目是由socket流为流量总入口,而熔断限流的目的就是在流量入口处进行监听管理,具体思路是抽一个熔断限流的配置文件,首先将对应的限流熔断功能增加开关,以防止后续生产出现流量不可控的情况,通过初始化然后将配置文件加载到项目中,通过监控非业务异常或者需求所明确的异常来进行异常监听管理从而触发熔断限流

Sentinel简介

在当今微服务架构日益普及的环境中,服务间的依赖关系变得异常复杂。一个单一服务的故障很可能沿着调用链迅速传播,最终导致整个系统雪崩式崩溃。面对这一挑战,​​熔断限流技术​​成为保障微服务稳定性的关键手段。阿里巴巴开源的​​Sentinel​​作为一款强大的流量控制组件,在熔断、限流和降级方面展现出卓越能力,相比传统的Hystrix在性能开销、规则配置灵活性和监控体验方面具有明显优势

微服务环境中的稳定性挑战主要体现在以下几个方面:当某个服务因高负载或故障导致响应变慢时,调用方会持续等待响应,从而占用系统资源;若多个服务之间存在循环依赖,故障会在服务间不断传递,最终导致整个系统不可用;在秒杀等高并发场景下,突发流量可能直接压垮后端服务。而Sentinel正是为解决这些问题而设计,它不仅能防止系统因突发流量崩溃,还能在系统负载过高时提供智能保护,确保核心业务的持续可用性

与Hystrix主要围绕线程池展开熔断不同,Sentinel基于并发数进行控制,支持更复杂的场景,且开销更小,适合在保证服务稳定的同时提高系统吞吐量。虽然Sentinel的超时机制是基于5次请求的平均响应时间,不如Hystrix严格(单个超时立即熔断),但对于大多数生产场景而言完全可接受

环境准备

​​JDK 8+​​(如使用Spring Boot 3需要JDK 17+)

​​Spring Boot 2.7+​​(本文示例基于Spring Boot 3.x)

​​Sentinel 1.8.6+​

需求背景

超时限流熔断降级机制配置(通过配置文件,按照产品理解实现,保持现状,文档说明):

(1)服务器端支持配置各交易超时时间。

(2)服务器端支持配置线程池大小和线程队列长度。

(3)服务器端支持配置按交易渠道配置限流策略。

(4)服务器端支持配置交易熔断策略。

(5)服务器端支持配置服务降级策略。

(6)服务器端支持配置故障隔离策略

Sentinel配置文件

Sentinel可支持的策略以及窗口非常的丰富,这里做了简单的描述,具体可查看文件注释

想深入了解可查看对应官网:https://sentinelguard.io/zh-cn/docs/introduction.html

bash 复制代码
#=========================================全局限流+熔断配置
global: {
  "enabled": true,
  #限流资源名
  "resourceName": 'hke',
  #设置限流阈值类型 0:线程策略 1:QPS策略
  "flowGrade": '0',
  #设置限流的阈值 允许的并发线程数或者QPS数量
  "flowCount": "10",
  #设置限流手段 0:快速失败,1:预热 2:匀速排队(令牌桶) 3:预热+匀速排队
  "flowControlBehavior": "1",
  #设置熔断策略 0:慢请求比例 (默认) 1:异常比例 2:异常数量
  "degradeGrade": "2",
  #响应时间阈值,单位毫秒,异常比例时为比例值
  #触发熔断的慢调用请求数阈值:当统计时间窗口内 当请求数 ≥ degradeCount,且慢调用比例超过阈值时触发熔断。
  #触发熔断的最小请求数阈值:当统计时间窗口内 当请求数 ≥ degradeCount,且异常比例超过阈值时触发熔断。
  #触发熔断的异常数量阈值:当统计时间窗口内的异常总数 ≥ degradeCount 时触发熔断。
  "degradeCount": "3",
  #设置熔断的时间窗口大小,单位为秒。例如,如果设置为10秒,则在10秒的时间窗口内触发降级阈值的次数超过了count,服务将被降级处理
  #degradeCount:当 10 秒内异常总数 ≥ 5 时触发熔断。
  #timeWindow:熔断后,5 秒内拒绝所有请求,之后进入半开状态试探恢复。
  "degradeTimeWindow": "10",
  #慢调用比例阈值,即响应时间/时间窗口
  "degradeSlowRatioThreshold": "0.5",
  #熔断触发恢复的最小请求数,半开状态下允许的最小请求数(默认 5)。
  "degradeMinRequestAmount": "5",
  #统计时间窗口,单位毫秒,。它决定了 Sentinel 在多长时间范围内收集指标数据(如异常数量、慢调用比例等),并根据这些数据判断是否触发熔断。
  "degradeStatIntervalMs": "10000"
}
#=========================================系统降级规则
systemRule: {
  "enabled": false,
  "highestSystemLoad": "-1",
  "avgRt": "30000",
  "maxThread": "300",
  "qps": "300",
  "highestCpuUsage": "-1"
}

  #配置系统规则:系统规则优先级高于流量控制规则(FlowRule),触发系统保护时会直接拒绝请求
  #enabled:false默认关闭
  #设置降级策略,cpu使用率,默认不限制
  #system.guard.highestCpuUsage=-1
  #设置降级策略,平均响应时间,毫秒
  #  system.guard.avgRt=10
  #设置降级策略,QPS并发数量
  #  system.guard.qps=100
  #设置降级策略,线程并发数
#  system.guard.maxThread=100
#设置降级策略,系统负载
#  system.guard.highestSystemLoad=1.5


#  参数	说明	优先级
#  highestSystemLoad	系统负载阈值(如 Linux 的 Load1)	最高(最先触发)
#  highestCpuUsage	CPU 使用率阈值(0~1,如 0.8 表示 80%)	次高
#  avgRt	平均响应时间阈值(毫秒)	中
#  maxThread	入口最大并发线程数	低
#  qps	入口总 QPS 阈值	最低

代码实现

pom文件

pom 复制代码
       <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-core</artifactId>
            <version>1.8.8</version>
        </dependency>

初始化熔断限流配置到程序中

java 复制代码
/**
 * 限流配置类。
 */
@Component
public class SentinelConfig {


    @Resource
    private JsonService jsonService;


    Gson gson = new Gson();

    /**
     * 全局限流和熔断配置
     */
    @Getter
    SentinelRule sentinelRuleGlobal = new SentinelRule();

    /**
     * 渠道集合:每个开启限流和熔断规则的渠道会放入该结合中
     */
    @Getter
    private HashSet<String> channelSet = new HashSet<>();


    /**
     * sentinel 初始化,将对应规则加入大Sentinel规则管理器
     */
    public void init() throws xxxException {
        FileSystemResource resource = new FileSystemResource("./xxx/Sentinel.yaml");
        try (InputStream is = resource.getInputStream()) {
            Yaml yaml = new Yaml();
            Map<String, Object> yamlMap = yaml.load(is);
            if (yamlMap == null) {
                throw new IllegalAccessException("[sentinel error] yamlMap is null!");
            }
            //读取全局配置+渠道配置
            Object globalObj = yamlMap.get("global");
            Object channelsObj = yamlMap.get("channels");
            Object systemRule = yamlMap.get("systemRule");

            // 配置限流规则
            initFlowDegradeRules(globalObj, channelsObj);

            // 配置降级规则
            initSystemRules(systemRule);

        } catch (Exception e) {
            //如果找不到Sentinel配置文件不报错,其他错误抛出
            if (!(e instanceof FileNotFoundException)) {
                Logger.systemLogger.error("[sentinel error] init error detail: {}", ExceptionUtil.getStackTreeInfo(e));
                throw new xxxException(ExceptionConstant.ERROR_CODE_860000);
            }
            LoggerManager.systemLogger.warn("[sentinel] not enabled! detail: HKEPrivateConfig/Sentinel.yaml not found!");
        }
    }

    /**
     * 初始化限流+熔断规则
     *
     * @param globalObj   全局配置
     * @param channelsObj 渠道配置
     * @throws xxxException
     */
    public void initFlowDegradeRules(Object globalObj, Object channelsObj) throws xxxException, IllegalAccessException {
        if (globalObj == null) {
            LoggerManager.systemLogger.error("[sentinel error] obj 'global' is null! please check Sentinel.yaml!");
            throw new IllegalAccessException("[sentinel error] obj 'global' is null! please check Sentinel.yaml!");
        }

        try {
            List<FlowRule> flowRules = new ArrayList<>();
            List<DegradeRule> degradeRules = new ArrayList<>();
            //配置对外暴露
            sentinelRuleGlobal = jsonService.getObjectFromJson(jsonService.getJsonFromObject(globalObj), new TypeToken<SentinelRule>() {
            });

            //开启限流和熔断
            if (sentinelRuleGlobal.isEnabled()) {
                //限流配置
                FlowRule flowRuleGlobal = new FlowRule();
                flowRuleGlobal.setResource(sentinelRuleGlobal.getResourceName());
                flowRuleGlobal.setGrade(sentinelRuleGlobal.getFlowGrade());
                flowRuleGlobal.setCount(sentinelRuleGlobal.getFlowCount()); // 每秒允许的QPS数量
                flowRuleGlobal.setControlBehavior(sentinelRuleGlobal.getFlowControlBehavior());
                flowRules.add(flowRuleGlobal);
                //熔断配置
                DegradeRule degradeRuleGlobal = new DegradeRule();
                degradeRuleGlobal.setResource(sentinelRuleGlobal.getResourceName());
                degradeRuleGlobal.setGrade(sentinelRuleGlobal.getDegradeGrade()); // 基于平均响应时间
                degradeRuleGlobal.setCount(sentinelRuleGlobal.getDegradeCount()); // 响应时间阈值200毫秒
                degradeRuleGlobal.setTimeWindow(sentinelRuleGlobal.getDegradeTimeWindow()); // 持续时间窗口10秒
                degradeRuleGlobal.setSlowRatioThreshold(sentinelRuleGlobal.getDegradeSlowRatioThreshold());//设置慢调用比例
                degradeRuleGlobal.setMinRequestAmount(sentinelRuleGlobal.getDegradeMinRequestAmount()); // 最小请求量
                degradeRuleGlobal.setStatIntervalMs(sentinelRuleGlobal.getDegradeStatIntervalMs()); // 统计时间窗口10秒
                degradeRules.add(degradeRuleGlobal);
            }


            if (channelsObj != null) {
                List<Map<String, Object>> channels = (List<Map<String, Object>>) channelsObj;
                if (!channels.isEmpty()) {
                    for (Map<String, Object> itemMap : channels) {
                        if (!itemMap.isEmpty()) {
                            SentinelRule sentinelRuleChannel = gson.fromJson(gson.toJson(itemMap), SentinelRule.class);
                            if (sentinelRuleChannel.isEnabled() && !channelSet.contains(sentinelRuleChannel.getResourceName())) {//渠道开启并且只添加一次
                                channelSet.add(sentinelRuleChannel.getResourceName());
                                //添加开启渠道的限流规则
                                FlowRule flowRuleChannel = new FlowRule();
                                flowRuleChannel.setResource(sentinelRuleChannel.getResourceName());
                                flowRuleChannel.setGrade(sentinelRuleChannel.getFlowGrade());
                                flowRuleChannel.setCount(sentinelRuleChannel.getFlowCount());
                                flowRuleChannel.setControlBehavior(sentinelRuleChannel.getFlowControlBehavior());
                                flowRules.add(flowRuleChannel);
                                //添加开启渠道的熔断规则
                                DegradeRule degradeRuleChannel = new DegradeRule();
                                degradeRuleChannel.setResource(sentinelRuleChannel.getResourceName());
                                degradeRuleChannel.setGrade(sentinelRuleChannel.getDegradeGrade());
                                degradeRuleChannel.setCount(sentinelRuleChannel.getDegradeCount());
                                degradeRuleChannel.setTimeWindow(sentinelRuleChannel.getDegradeTimeWindow());
                                degradeRuleChannel.setSlowRatioThreshold(sentinelRuleChannel.getDegradeSlowRatioThreshold());
                                degradeRuleChannel.setMinRequestAmount(sentinelRuleChannel.getDegradeMinRequestAmount());
                                degradeRuleChannel.setStatIntervalMs(sentinelRuleChannel.getDegradeStatIntervalMs());
                                degradeRules.add(degradeRuleChannel);
                                LoggerManager.systemLogger.info("[sentinel] Channel Rules: flowRule: {}, degradeRule: {}", flowRuleChannel, degradeRuleChannel);
                            }
                        }
                    }
                }
            }

            //添加限流规则到Sentinel管理器
            if (!flowRules.isEmpty()) {
                LoggerManager.systemLogger.info("[sentinel] flowRules is enabled! flowRules: {}", flowRules);
                FlowRuleManager.loadRules(flowRules);
            } else {
                LoggerManager.systemLogger.warn("[sentinel] flowRules not enabled!");
            }
            //添加熔断规则到Sentinel管理器
            if (!degradeRules.isEmpty()) {
                LoggerManager.systemLogger.info("[sentinel] degradeRules is enabled!  degradeRule: {}", degradeRules);
                DegradeRuleManager.loadRules(degradeRules);
            } else {
                LoggerManager.systemLogger.warn("[sentinel] degradeRules not enabled!");
            }

        } catch (Exception e) {
            LoggerManager.systemLogger.error("[sentinel error] initFlowDegradeRules error! {}", e.getMessage(), e);
            throw new HKEException(ExceptionConstant.ERROR_CODE_860001);
        }
    }

    /**
     * 初始化sentinel 系统规则
     *
     * @param systemRuleObj
     */
    private void initSystemRules(Object systemRuleObj) throws IllegalAccessException {
        if (systemRuleObj == null) {
            LoggerManager.systemLogger.error("[sentinel error] obj 'systemRule' is null! please check Sentinel.yaml!");
            throw new IllegalAccessException("[sentinel error] obj 'systemRule' is null! please check Sentinel.yaml!");
        }

        SentinelSystemRule sentinelSystemRule = gson.fromJson(gson.toJson(systemRuleObj), SentinelSystemRule.class);

        if (sentinelSystemRule.isEnabled()) {
            List<SystemRule> rules = new ArrayList<>();
            SystemRule systemRule = new SystemRule();
            //系统负载,这个参数仅对linux有效,是cpu能处理的最大进程数的一个指标值。假如系统最多能承载10个进程,那么
            //有5个进程运行时,load值为0.5,即50%,所有进程能够通畅运行,还有50%空闲空间。
            //有10个进程运行时,load值为1,即100%,所有进程能够通畅运行,没有空闲
            //有20个进程运行时,load值为2,即200%,有10个进程能够通畅运行,还有10个在等待中。
            systemRule.setHighestSystemLoad(sentinelSystemRule.getHighestSystemLoad()); // 设置最高系统负载

            systemRule.setAvgRt(sentinelSystemRule.getAvgRt()); // 设置平均响应时间 ms
            systemRule.setMaxThread(sentinelSystemRule.getMaxThread()); // 设置最大线程数为
            systemRule.setQps(sentinelSystemRule.getQps()); // 设置每秒查询次数为
            systemRule.setHighestCpuUsage(sentinelSystemRule.getHighestCpuUsage()); // 设置最高 CPU 使用率为
            rules.add(systemRule);

            LoggerManager.systemLogger.info("[sentinel] SystemRule is enabled! SystemRule: {}", rules);
            SystemRuleManager.loadRules(rules);
        } else {
            LoggerManager.systemLogger.warn("[sentinel] SystemRule is not enabled!");
        }

    }
}

流量入口处进行监控管理

java 复制代码
/**
     * sentinel限流
     *
     * @param sentinelConfig sentinel配置
     */
    public void runSentinel(SentinelConfig sentinelConfig) throws Throwable {
        Entry entry = null;
        try {
            //是否全局限流
            entry = SphU.entry(sentinelConfig.getSentinelRuleGlobal().getResourceName(), EntryType.IN);
            //业务
            handleByTx(requestXml);
            sendResponseXml();
        } catch (Exception exception) {
            //将指定异常加入限流统计
            if (exception instanceof CannotCreateTransactionException ||
                    exception instanceof CannotGetJdbcConnectionException ||
                    exception instanceof SSLPeerUnverifiedException ||
                    exception instanceof IOException ||
                    exception instanceof SQLTransientConnectionException
            ) {
                Tracer.trace(exception);
                // 打印完整异常类型
                try {
                    LoggerManager.exceptionLogger.error("[Sentinel] Tracer exceptionType: {}, expInfo: {}",
                            exception.getClass().getName(),
                            ExceptionUtil.getStackTreeInfo(exception)
                    );
                } catch (Exception e) {
                    LoggerManager.exceptionLogger.error("[Sentinel] Log print error! detail:{}, exp: {}", e.getMessage(), e);
                }
            }
            //限流处理
            if (exception instanceof BlockException) {
                if (exception instanceof FlowException) {
                    LoggerManager.exceptionLogger.error("[Sentinel] Service is in flow rules! resourceName: {}", sentinelConfig.getSentinelRuleGlobal().getResourceName());
                    throw new HKEException(FepException.ERROR_CODE_818204);
                } else if (exception instanceof DegradeException) {
                    LoggerManager.exceptionLogger.error("[Sentinel] Service is in degrade rules! resourceName: {}", sentinelConfig.getSentinelRuleGlobal().getResourceName());
                    throw new HKEException(FepException.ERROR_CODE_818205);
                } else if (exception instanceof SystemBlockException) {
                    LoggerManager.exceptionLogger.error("[Sentinel] Service is in SystemBlock rules! resourceName: {}", sentinelConfig.getSentinelRuleGlobal().getResourceName());
                    throw new HKEException(FepException.ERROR_CODE_818206);
                } else {
                    LoggerManager.exceptionLogger.error("[Sentinel] Service is in other rules! type:{}, resourceName: {}",
                            exception.getClass(),
                            sentinelConfig.getSentinelRuleGlobal().getResourceName());
                    throw new HKEException(FepException.ERROR_CODE_818207);
                }
            } else {
                throw exception;
            }
        } finally {
            if (entry != null) {
                entry.exit();
            }
        }
    }

限流技术剖析

  • 中间件方案
    • Redis + Lua 实现令牌桶
    • Redis 实现滑动窗口
  • 框架方案
    • Sentinel
    • Resilience4j

核心定位

网关作为分布式系统流量入口,主要分为两类:

  • 流量网关:侧重网络层处理(OSI L3-L4)
  • 应用网关:专注业务层路由(OSI L7)

核心功能矩阵

功能类别 具体能力 实现示例
路由转发 动态路由、负载均衡 Spring Cloud Gateway Predicate
安全防护 认证授权、WAF JWT/OAuth2 集成
流量治理 限流熔断、灰度发布 Sentinel/Redis 集成
协议转换 HTTP 转 gRPC/Dubbo Protocol Buffers 编解码
可观测性 日志监控、链路追踪 Sleuth + Zipkin

流量网关 vs 应用网关

维度 流量网关 应用网关
工作层级 OSI L3-L4 OSI L7
核心能力 负载均衡、网络防护 业务路由、协议转换
典型部署位置 网络边缘(DMZ区) 应用集群内部
性能关注点 吞吐量、并发连接数 业务延迟、QPS
配置更新频率 天/周级别 分钟级
典型代表 Nginx、F5 Spring Cloud Gateway、Kong

主流Java网关对比

网关框架 架构模型 性能表现(RPS) 延迟(P99) 核心优势
Spring Cloud Gateway Reactor异步 15,000 15ms 深度Spring生态集成
Zuul 1.x 阻塞IO 2,000 50ms Netflix老牌方案
Zuul 2.x Netty异步 8,000 25ms 改进版异步模型
Apache ShenYu WebFlux+Netty 20,000+ 10ms 多协议支持、插件化架构
相关推荐
绝无仅有5 小时前
京东面试题解析:String与StringBuilder的区别、装箱拆箱、重载与重写总结
后端·面试·github
非凡ghost5 小时前
WinMute(自动锁屏静音软件) 中文绿色版
前端·javascript·后端
在等晚安么5 小时前
记录自己写项目的第三天,springbot+redis+rabbitma高并发项目
java·spring boot·redis·1024程序员节
唐叔在学习5 小时前
200kb能作甚?mss表示我给你整个截图程序
后端·python
出师未捷的小白5 小时前
[NestJS] 手摸手~工作队列模式的邮件模块解析以及grpc调用
前端·后端
用户8356290780516 小时前
用Python自动化转换PowerPoint幻灯片为图片
后端·python
程序员爱钓鱼6 小时前
Python编程实战 · 基础入门篇 | 推导式(列表推导式 / 字典推导式)
后端·python
无限进步_6 小时前
【C语言】函数指针数组:从条件分支到转移表的优雅进化
c语言·开发语言·数据结构·后端·算法·visual studio
程序员爱钓鱼6 小时前
Python编程实战 · 基础入门篇 | 循环控制:break / continue / else
后端