流控三板斧——Sentinel滑动窗口、令牌桶与Dubbo负载均衡

流控三板斧 前三篇讲了一个核心矛盾:分布式系统里——网络和时钟不可靠——所以你必须在一致性和可用性之间做取舍——Raft 用 majority 保证 CP——Nacos Distro 用最终一致性取 AP。 但取舍不只发生在数据一致性层面——流量控制层面同样存在。每个服务有自己的承载上限——超过上限就必须拒绝一部分请求——这就是限流。拒绝哪些请求?以什么粒度计数?桶还是窗口? 📌 前置知识:需要有 Sentinel 基本概念(知道它是限流熔断组件)和 Dubbo 基本用法(知道 @DubboReference 怎么调用远程服务)。如果还没用过 Sentinel 的 Dashboard——建议先对着官方文档跑一遍 Quick Start——不需要深入——但得知道控制台里"流控规则"长什么样。 一、为什么"每秒 100 个请求"这种限流方式有 Bug——固定窗口的边界突刺 对限流最直观的理解:系统处理能力是每秒 100 个——超过就拒绝。实现这个最简单的办法——搞一个计数器——每秒归零。 // 固定窗口计数器——最朴素的想法 class FixedWindowRateLimiter { private long windowStart = System.currentTimeMillis(); private int counter = 0; private final int limit = 100; public synchronized boolean tryAcquire() { long now = System.currentTimeMillis(); if (now - windowStart > 1000) { windowStart = now; // 新窗口——计数器归零 counter = 0; } if (counter < limit) { counter++; return true; // 放行 } return false; // 限流 } } 看起来没毛病——每秒最多通过 100 个——超过就拒绝。问题出在窗口边界: ...

一月 4, 2023 · 4 分钟 · 701 字 · yaomingye

谁说了算——Raft选举、心跳与故障检测在Nacos/Dubbo中的应用

谁说了算 前两篇讲了一个道理:网络和时钟不可靠 → 必须做取舍 → CAP 把取舍定了性。那具体怎么做取舍呢? 如果集群里只有一台机器——不存在一致性问题——所有写操作都在同一块硬盘上——谁先谁后清清楚楚。但只有一台机器的代价是——这台机器宕机——系统全挂。所以需要多台机器——而多台机器就需要一个机制来决定“谁的版本算数”。 这个机制在分布式系统里有一个正式的名字——共识算法(Consensus Algorithm)。Raft 是目前工程界最广泛使用的共识算法——不是因为它理论上最完美——而是因为它可以让人看得懂。 📌 前置知识:建议先读上篇 CAP 定理——理解 CP vs AP 的区别。Raft 是典型的 CP 实现——本文的 Raft 部分主要解释它如何实现 C(一致性)。 一、为什么要有人"说了算"——分布式写操作的困境 先看一个最简单的集群:三台机器——每台都存一份数据——都可以接受写请求。 客户端写入 x=1 → 节点 A 收到——更新本地 x=1 客户端写入 x=2 → 节点 B 收到——更新本地 x=2 (几乎同时——两个客户端连到了两个不同的节点) A 认为 x=1——B 认为 x=2——到底 x 是多少? 两者各自都认为自己的数据正确——没有人有权限说"听我的"——这就是分布式系统里最核心的问题——没有单点权威——写操作需要协调。 flowchart TD start["两个客户端——两个写请求——\n到达两个不同节点"]:::startEnd start --> c1["客户端 1 → 节点 A\nSET x=1"]:::data start --> c2["客户端 2 → 节点 B\nSET x=2"]:::data c1 --> conflict["节点 A:x=1\n节点 B:x=2\n⚡ 冲突——x 到底等于几?"]:::highlight c2 --> conflict conflict --> naive["最简单的方案:\n规定只有一台机器能接受写——\n这台机器叫 Leader"]:::data naive --> next_q["新问题:Leader 宕机了呢?\n谁当新 Leader?\n怎么告诉大家?"]:::condition classDef startEnd fill:#701a4c,stroke:#e11d48,stroke-width:2px,color:#fce7f3,font-weight:bold; classDef data fill:#052e16,stroke:#16a34a,stroke-width:1.5px,color:#bbf7d0,font-weight:bold; classDef highlight fill:#450a0a,stroke:#dc2626,stroke-width:1.5px,color:#fecaca,font-weight:bold; classDef condition fill:#2a1147,stroke:#a855f7,stroke-width:1.5px,color:#ede9fe,font-weight:bold; Raft 要解决的就是这两个问题合在一起:(1) 选出一个大家都认可的 Leader——(2) Leader 挂了以后——自动选出新 Leader。 ...

一月 3, 2023 · 4 分钟 · 844 字 · yaomingye

Nacos 集群与生产部署——中间件整合总览

集群与生产部署——中间件整合总览 📖 前置阅读:本文假设读者已掌握 Nacos 的服务发现和配置中心机制。如果还不熟悉,建议先阅读前三篇:核心概念、服务发现、配置中心。 一、⚡ 单机 Nacos 挂了——整个微服务体系全部变瞎子 单机 Nacos 开发环境跑得挺好——但生产环境下,Nacos 是整个微服务体系的命脉: Nacos 挂了 → 后果: ① 新服务无法注册——滚动更新时新实例注册不上 ② 新调用无法发现——Consumer 拿不到最新实例列表(本地缓存还能撑一会) ③ 配置改不了——Sentinel 规则、Gateway 路由、业务开关全改不了 ④ Dashboard 看不了——不知道哪些服务在线、哪些不在线 虽然本地缓存能兜底——但这是"缓兵之计"不是"长久之计" Nacos 集群是必须的——而且要做高可用 二、🏗️ Nacos 集群架构——三个节点 + MySQL 2.1 为什么需要 MySQL? Nacos 内嵌了一个 Derby 数据库(单机默认)。集群模式下 必须用外部 MySQL——所有节点共享同一个数据源: Nacos 集群架构: ┌──────────┐ ┌──────────┐ ┌──────────┐ │ Nacos 1 │ │ Nacos 2 │ │ Nacos 3 │ ← Nacos 节点(无状态) │ :8848 │ │ :8848 │ │ :8848 │ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │ │ └───────────────┼───────────────┘ │ ┌──────▼──────┐ │ MySQL │ ← 共享数据库——存注册信息和配置 │ (主从/集群) │ └─────────────┘ Nacos 节点之间用 Raft 协议选举 Leader → Leader 负责写 MySQL → Follower 从 MySQL 读数据并同步到内存 → 任何节点收到客户端请求——都能处理读请求 2.2 配置 MySQL -- 初始化 Nacos 数据库 -- 执行 Nacos 安装包中 conf/mysql-schema.sql -- 创建数据库 CREATE DATABASE IF NOT EXISTS nacos_config DEFAULT CHARACTER SET utf8mb4; -- 配置 Nacos 数据源 # nacos/conf/application.properties # ① MySQL 配置 spring.datasource.platform=mysql db.num=1 db.url.0=jdbc:mysql://mysql-cluster:3306/nacos_config?useSSL=false&allowPublicKeyRetrieval=true db.user.0=nacos db.password.0=nacos_password_123 # ② 切换为集群模式 nacos.core.auth.enabled=true 2.3 集群节点配置 # nacos/conf/cluster.conf——每个节点一行 IP:Port # 注意:端口是 Raft 通信端口——默认 7848(不是 8848!) 10.0.1.11:7848 10.0.1.12:7848 10.0.1.13:7848 三、🐳 Docker Compose——一键启动 Nacos 集群 version: '3.8' services: # ===== MySQL——Nacos 共享存储 ===== mysql: image: mysql:8.0 container_name: nacos-mysql environment: MYSQL_ROOT_PASSWORD: root123 MYSQL_DATABASE: nacos_config MYSQL_USER: nacos MYSQL_PASSWORD: nacos_password_123 ports: - "3306:3306" volumes: - mysql-data:/var/lib/mysql - ./mysql-schema.sql:/docker-entrypoint-initdb.d/01-schema.sql healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] interval: 10s retries: 5 # ===== Nacos 集群——3 节点 ===== nacos1: image: nacos/nacos-server:v2.3.0 container_name: nacos1 depends_on: mysql: condition: service_healthy environment: - MODE=cluster - NACOS_SERVERS=nacos1:7848,nacos2:7848,nacos3:7848 - SPRING_DATASOURCE_PLATFORM=mysql - MYSQL_SERVICE_HOST=mysql - MYSQL_SERVICE_PORT=3306 - MYSQL_SERVICE_DB_NAME=nacos_config - MYSQL_SERVICE_USER=nacos - MYSQL_SERVICE_PASSWORD=nacos_password_123 - NACOS_AUTH_ENABLE=true - NACOS_AUTH_IDENTITY_KEY=nacos - NACOS_AUTH_IDENTITY_VALUE=nacos - NACOS_AUTH_TOKEN=SecretKey012345678901234567890123456789012345678901234567890123456789 ports: - "8848:8848" - "7848:7848" volumes: - nacos1-logs:/home/nacos/logs nacos2: image: nacos/nacos-server:v2.3.0 container_name: nacos2 depends_on: mysql: condition: service_healthy environment: - MODE=cluster - NACOS_SERVERS=nacos1:7848,nacos2:7848,nacos3:7848 - SPRING_DATASOURCE_PLATFORM=mysql - MYSQL_SERVICE_HOST=mysql - MYSQL_SERVICE_PORT=3306 - MYSQL_SERVICE_DB_NAME=nacos_config - MYSQL_SERVICE_USER=nacos - MYSQL_SERVICE_PASSWORD=nacos_password_123 - NACOS_AUTH_ENABLE=true - NACOS_AUTH_IDENTITY_KEY=nacos - NACOS_AUTH_IDENTITY_VALUE=nacos - NACOS_AUTH_TOKEN=SecretKey012345678901234567890123456789012345678901234567890123456789 ports: - "8849:8848" - "7849:7848" nacos3: image: nacos/nacos-server:v2.3.0 container_name: nacos3 depends_on: mysql: condition: service_healthy environment: - MODE=cluster - NACOS_SERVERS=nacos1:7848,nacos2:7848,nacos3:7848 - SPRING_DATASOURCE_PLATFORM=mysql - MYSQL_SERVICE_HOST=mysql - MYSQL_SERVICE_PORT=3306 - MYSQL_SERVICE_DB_NAME=nacos_config - MYSQL_SERVICE_USER=nacos - MYSQL_SERVICE_PASSWORD=nacos_password_123 - NACOS_AUTH_ENABLE=true ports: - "8850:8848" - "7850:7848" # ===== Prometheus——采集 Nacos 指标 ===== prometheus: image: prom/prometheus:v2.48.0 ports: - "9090:9090" volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml volumes: mysql-data: nacos1-logs: prometheus.yml global: scrape_interval: 15s scrape_configs: - job_name: 'nacos' metrics_path: '/nacos/actuator/prometheus' static_configs: - targets: - 'nacos1:8848' - 'nacos2:8848' - 'nacos3:8848' 四、📊 Nacos 监控——Prometheus + Grafana Nacos 内置了 Prometheus 指标暴露——访问 http://nacos:8848/nacos/actuator/prometheus 即可获取。 ...

十二月 16, 2022 · 5 分钟 · 951 字 · yaomingye

Nacos 配置中心全操作

配置中心:从接入到动态刷新 📖 前置阅读:本文假设读者已掌握 Nacos 的基本概念和 @RefreshScope。如果还不熟悉,建议先阅读 Nacos 核心概念与快速上手。 一、⚡ 数据库密码改了——5 个服务 15 个实例要一个个改? 先来看看没有配置中心时的情况: 场景:MySQL 主库切换——数据库地址从 mysql-master-1 变成 mysql-master-2 没有配置中心: ① 改 5 个服务的 application.yml——每个服务改一次 ② 重新打包/重启 15 个实例——顺序不能错(先启 DB 相关的) ③ 改到一半发现有个服务漏了——生产故障 耗时:30 分钟 + 心跳加速 有了 Nacos 配置中心: ① 在 Nacos Dashboard 改一个配置——mysql.host ② 点发布——15 个实例自动收到推送 ③ @RefreshScope 的 Bean 自动重建——新配置生效 耗时:1 分钟 + 淡定 配置中心的价值就是一句话:改一次——推所有——不用重启。 二、🧩 配置的三级组织——shared-configs / extension-configs / 本地 Nacos 配置有三层——从最共享到最专属: shared-configs(共享配置) ← 所有服务通用的配置 ↓ 可以被覆盖 extension-configs(扩展配置) ← 一组服务共享的配置 ↓ 可以被覆盖 ${spring.application.name}-${profile}.${ext}(服务专属配置) ← 每个服务自己的配置 ↓ 兜底 application.yml(本地配置) ← 开发环境兜底——生产通常不放关键配置 2.1 三层配置在 yml 中怎么配 # bootstrap.yml(早于 application.yml 加载——连 Nacos 必须放这里) spring: application: name: order-service profiles: active: dev cloud: nacos: config: server-addr: localhost:8848 namespace: dev group: DEFAULT_GROUP file-extension: yaml # ① shared-configs——所有服务共享的公共配置 shared-configs: - data-id: common-mysql.yaml # 数据库公共配置——所有服务共用一个 DB 集群 group: DEFAULT_GROUP refresh: true # 允许动态刷新 - data-id: common-redis.yaml # Redis 公共配置 group: DEFAULT_GROUP refresh: true - data-id: common-log.yaml # 日志公共配置 group: DEFAULT_GROUP refresh: false # 日志配置不动态刷新——改日志级别才需要 # ② extension-configs——当前服务专属——比 shared 优先级高 extension-configs: - data-id: order-service-custom.yaml group: DEFAULT_GROUP refresh: true 2.2 Nacos Dashboard 中创建公共配置 Nacos Dashboard → 配置管理 → 配置列表 → 新建配置 ① common-mysql.yaml (DEFAULT_GROUP): spring: datasource: url: jdbc:mysql://mysql-master:3306/ username: app_user password: ${MYSQL_PASSWORD} # ← 密码用环境变量——不写在配置中心 hikari: maximum-pool-size: 20 minimum-idle: 5 connection-timeout: 3000 ② common-redis.yaml (DEFAULT_GROUP): spring: redis: host: redis-cluster.internal port: 6379 lettuce: pool: max-active: 20 max-idle: 10 ③ order-service-dev.yaml (DEFAULT_GROUP): # 订单服务专属配置——覆盖或扩展公共配置 spring: datasource: url: jdbc:mysql://mysql-master:3306/order_db?useSSL=false # 覆盖——订单库 hikari: maximum-pool-size: 50 # 覆盖公共配置——订单服务连接池要大一些 app: order: max-items-per-order: 50 payment-timeout-seconds: 1800 2.3 配置加载的优先级——哪个生效? 当同一个配置在多个地方出现时——后加载的覆盖先加载的: ...

十二月 15, 2022 · 5 分钟 · 953 字 · yaomingye

Nacos 服务发现深度解析

服务发现深度解析 📖 前置阅读:本文假设读者已掌握 Nacos 的核心概念——命名空间/分组/服务/实例。如果还不熟悉,建议先阅读 Nacos 核心概念与快速上手。 一、⚡ 服务注册上去了——但为什么偶尔调不通? Nacos Dashboard 里看到服务是"健康"的——但 Feign 偶尔报 Connection Refused。排查后发现:那个实例 5 分钟前就挂了——Nacos 还没把它剔掉。 服务发现不是"注册上去了就完了"——你要理解它背后的机制:心跳怎么维护?挂了的实例多久被剔除?本地缓存的作用是什么? 二、🔄 服务注册流程全景 sequenceDiagram participant Provider as 服务提供方\nuser-service participant Nacos participant Consumer as 服务调用方\norder-service Provider->>Nacos: ① 注册:我是 user-service,地址 10.0.1.1:8081 Note over Provider,Nacos: Provider 启动时向 Nacos 注册 Provider->>Nacos: ② 心跳:我还活着(每 5s) Note over Provider,Nacos: 健康的实例定时发心跳 Consumer->>Nacos: ③ 订阅:我要找 user-service Nacos-->>Consumer: ④ 返回实例列表:[10.0.1.1:8081, 10.0.1.2:8081] Note over Nacos,Consumer: Consumer 拉一次——后续 Nacos Push 变更 Consumer->>Provider: ⑤ 直连调用——GET /api/users/1 Note over Consumer,Provider: 选一个实例——通过负载均衡 Note over Nacos: 实例不发心跳 15s → 不健康\n30s → 剔除 关键点——Nacos 不参与业务流量:服务注册/发现只在"找地址"阶段经过 Nacos。一旦 Consumer 拿到了实例列表——后续的 RPC 调用是直连 Provider——不经过 Nacos。 ...

十二月 14, 2022 · 4 分钟 · 809 字 · yaomingye

Nacos 核心概念与快速上手

核心概念与快速上手 一、⚡ 服务多了——两个最头疼的问题 微服务写到第 6 个的时候——你会发现两个问题越来越痛: 问题 ①:服务之间怎么找到对方? OrderService 调 UserService——以前一个 IP:Port 写死就行了 现在 UserService 有 3 个实例——10.0.1.1:8081、10.0.1.2:8081、10.0.1.3:8081 明天扩容到 5 个——OrderService 难道重新改配置上线? → 需要"服务发现"——调用方不关心实例在哪——找注册中心问 问题 ②:改了配置怎么让所有服务生效? 数据库连接池从 20 改到 50——5 个服务 × 3 个实例 = 15 个 yml 文件要改 改完还得一个个重启——重启顺序还不能乱 → 需要"配置中心"——一处修改——所有实例自动感知 这两个问题的答案就是 Nacos——一个组件同时搞定服务发现和配置中心。 二、🧩 Nacos 是什么——一句话 Nacos(NAming and COnfiguration Service)= 服务发现 + 配置中心。它是阿里开源的微服务基础设施——Spring Cloud Alibaba 的核心组件。 在 Nacos 之前——Spring Cloud 微服务需要两个组件: 没有 Nacos 的时代: Eureka(服务注册/发现) + Spring Cloud Config(配置中心) + Spring Cloud Bus(配置刷新) 三个组件——三套配置——三种部署方式 有了 Nacos: Nacos 一个组件 = Eureka + Config + Bus 一套配置——一种部署方式——学习成本砍一半 三、🏗️ 部署 Nacos Server——3 分钟跑起来 # 方式一:Docker——最快 docker run -d \ --name nacos \ -p 8848:8848 \ -p 9848:9848 \ -e MODE=standalone \ nacos/nacos-server:v2.3.0 # 方式二:直接运行——下载后解压 # https://github.com/alibaba/nacos/releases # 解压后 cd nacos/bin # Windows: startup.cmd -m standalone # Linux/Mac: sh startup.sh -m standalone # 访问 http://localhost:8848/nacos # 用户名/密码:nacos/nacos 两个端口的作用: ...

十二月 13, 2022 · 3 分钟 · 636 字 · yaomingye

Sentinel 系统规则与生产部署

Sentinel 生产部署 📖 前置阅读:本文假设读者已掌握 Sentinel 流控和熔断规则。如果还不熟悉,建议先阅读前三篇:核心概念、流控规则、熔断降级。 一、⚡ 流控和熔断都配了——但整个机器的 CPU 飙到 95% 了 流控规则保护的是单个接口——“getUser 每秒最多 100 个”。“熔断规则保护的是接口自身故障——“getUser 50% 慢调用就熔断”。 但这些规则不保护整个机器——如果 20 个接口各自都没超过自己的 QPS 阈值,但加起来把机器的 CPU 打满了——所有接口不可用。 系统规则(System Rule)解决的就是这个问题——从整个应用的层面做自适应保护。 二、🧬 系统自适应保护——不配具体 QPS,配"健康指标” 2.1 五种系统规则 // 系统规则——对整个服务生效——不是针对某个资源 SystemRule systemRule = new SystemRule(); // ① Load 保护——系统负载(仅 Linux)超过阈值时限流 systemRule.setHighestSystemLoad(4.0); // CPU 核数——如 4 核 CPU // 系统 Load > 4.0 时——所有入口 QPS 自动降到 (Load / 当前Load) * 当前QPS // ② CPU 使用率保护 systemRule.setHighestCpuUsage(0.8); // CPU 使用率 > 80% 时——拒绝新的入口请求 // ③ 平均 RT 保护 systemRule.setAvgRt(100); // 所有入口的平均 RT > 100ms 时——限流 // ④ 最大并发线程数 systemRule.setMaxThread(200); // 并发线程数 > 200——拒绝新请求 // ⑤ 入口 QPS——这个最直接 systemRule.setQps(500); // 所有入口(不管是哪个资源)——总 QPS > 500 系统规则 指标 阈值建议 适用场景 Load 系统 Load ≤ CPU 核数 Linux 环境——最推荐 CPU 使用率 CPU usage ≤ 80% 跨平台——和 Load 二选一 平均 RT 所有入口平均 RT ≤ 正常值 × 2 服务变慢时自动降 QPS 并发线程数 并发线程数 ≤ 线程池大小 防止线程池满 入口 QPS 总 QPS ≤ 压测值 × 80% 简单粗暴——兜底方案 2.2 系统规则的最佳组合 @Component public class SystemRuleInitializer implements ApplicationRunner { @Override public void run(ApplicationArguments args) { List<SystemRule> rules = new ArrayList<>(); // 规则 1:Load 保护——系统负载过高时自动降 QPS SystemRule loadRule = new SystemRule(); loadRule.setHighestSystemLoad(4.0); rules.add(loadRule); // 规则 2:平均 RT 保护——接口变慢时自动减速 SystemRule rtRule = new SystemRule(); rtRule.setAvgRt(200); rules.add(rtRule); // 规则 3:并发线程数保护——防止线程池满 SystemRule threadRule = new SystemRule(); threadRule.setMaxThread(300); rules.add(threadRule); SystemRuleManager.loadRules(rules); } } 系统规则是整个 JVM 级别的——不需要指定资源名。它的作用范围是所有入口(所有经过 Sentinel 保护的入口流量的汇总)。 ...

十二月 9, 2022 · 5 分钟 · 1042 字 · yaomingye

Sentinel 熔断降级规则

Sentinel 熔断降级 📖 前置阅读:本文假设读者已掌握 Sentinel 流控规则和 blockHandler/fallback 的基本用法。如果还不熟悉,建议先阅读 Sentinel 核心概念与快速上手 和 Sentinel 流控规则全解。 一、⚡ 限流是自己控制的——但接口突然变慢是意外 你给 getUser 接口配了 QPS 限流 = 100——这是主动控制。但有一天数据库查询从 50ms 涨到了 5s——不是流量大,是接口本身出问题了。 这 5s 的查询会带来一连串的后果: getUser 每次耗时 5s → 调它的 100 个请求都在等——线程池 100 个线程全占满 → 其他接口没线程可用——跟着一起 502 → 上游调 getUser 的 10 个服务全超时——各自线程池也满 → 整个系统雪崩 限流解决不了这个问题——100 QPS 还是 100 QPS,只是每个请求都慢到 5s。熔断降级解决的就是"接口自己出问题"——检测到异常主动切断对故障接口的调用——等它恢复了再放行。 二、🔄 熔断器状态机——每个熔断器都一样 不管是 Sentinel、Hystrix 还是 Resilience4j,熔断器的状态机都是一样的——三个状态: stateDiagram-v2 [*] --> CLOSED : 初始状态 CLOSED --> OPEN : 失败率达到阈值 OPEN --> HALF_OPEN : 熔断时间窗口结束 HALF_OPEN --> CLOSED : 试探请求成功 HALF_OPEN --> OPEN : 试探请求失败 note right of CLOSED : 正常——请求正常通过\n持续统计指标 note right of OPEN : 熔断——请求直接拒绝\n不调后端——直接 fallback note right of HALF_OPEN : 半开——放一个试探请求\n看它能不能成功 状态 行为 进入条件 CLOSED(关闭) 正常通过——统计指标 初始状态——或 HALF_OPEN 试探成功 OPEN(打开) 直接拒绝——不走后端——直接调 fallback 指标达到阈值 HALF_OPEN(半开) 放一个试探请求——其他拒绝 OPEN 持续一段时间后自动进入 三、🧬 Sentinel 的三种熔断策略 Sentinel 支持三种熔断策略——比 Hystrix(只支持异常比例)更精细: ...

十二月 8, 2022 · 4 分钟 · 672 字 · yaomingye

Sentinel 流控规则全解

Sentinel 流控规则 📖 前置阅读:本文假设读者已掌握 Sentinel 的核心概念——资源/规则/Entry 类型。如果还不熟悉,建议先阅读 Sentinel 核心概念与快速上手。 一、⚡ “每秒 100 个 QPS"只是流控的冰山一角 上一篇说了 FlowRule —— “QPS 超过 100 就拒绝”。但真实的需求远比这复杂: 场景 ①:新服务刚启动——JIT 还没热身——扛不住满负荷流量 → 需要"预热"——先 10 QPS,逐步升到 100 QPS 场景 ②:不想直接拒绝请求——让请求排队等着 → 需要"排队等待"——请求等 500ms 能排上就处理,超时就拒绝 场景 ③:支付接口出问题——我不想限流它——但我想限流"调用了支付接口"的接口 → 需要"关联限流"——支付接口 QPS 高了——限流订单创建接口(让压力源头降流量) 场景 ④:同一个 URL 被两个入口调用——我只想限流其中一个入口 → 需要"链路限流"——只限制从某个入口进来的流量 这四个场景对应 Sentinel 流控的三种效果 × 三种策略: 每条 FlowRule 有两个关键选择: ① 效果(ControlBehavior)——超过阈值后怎么办? ② 策略(Strategy)——针对谁来限流? 二、📊 两种 Grade —— 限制了"什么” 2.1 QPS 模式(FLOW_GRADE_QPS) 每秒请求次数——最常用: FlowRule rule = new FlowRule(); rule.setResource("getUser"); rule.setGrade(RuleConstant.FLOW_GRADE_QPS); // 基于 QPS rule.setCount(100); // 阈值:每秒 100 个 时间线(1 秒内): 第 1~100 个请求 → 通过 第 101 个请求 → 被拒绝(BlockException) 下一秒重新计数 QPS 限流的核心是滑动窗口计数器——统计最近 1 秒(可配)的请求数。 ...

十二月 7, 2022 · 4 分钟 · 819 字 · yaomingye

Sentinel 核心概念与快速上手

Sentinel 核心概念 一、⚡ 流量控制——三个让你睡不着的问题 你写了三个微服务——用户服务、订单服务、商品服务。跑得挺稳——直到: 问题 ①:大促的流量是平时的 10 倍——订单服务扛不住了 → 一个服务挂了 → 调用它的服务跟着挂 → 整个系统雪崩 → 你想给订单接口限流——每秒最多处理 1000 个请求——多的直接拒绝 问题 ②:商品服务的"查价格"接口突然变慢了——耗时从 50ms 涨到 5s → 调它的线程全在等响应——线程池满了 → 你想检测到 RT 异常时——先熔断掉这个接口——让它别拖死整个系统 问题 ③:不同的调用方重要程度不一样 → 订单支付的接口 > 浏览订单的接口 → 你想在流量高峰时——优先保证支付接口不被限流 这三个问题对应了 Sentinel 的三个核心能力:限流(Flow Control)、熔断降级(Circuit Breaking)、系统自适应保护(System Protection)。 二、🤔 Sentinel 是什么——以及为什么不是 Hystrix Sentinel 是阿里巴巴开源的“流量防卫兵”——它以流量为切入点,从流量控制、熔断降级、系统负载保护三个维度保护服务的稳定性。 Hystrix 和 Sentinel 对比: 维度 Hystrix Sentinel 维护状态 停止维护——只修 Bug 不加新功能 ✅ 活跃维护——阿里巴巴 + 社区 隔离策略 信号量 + 线程池——二选一 信号量(默认)——更轻量 限流粒度 接口级别——比较粗糙 ✅ 可按 QPS/线程/调用方/链路——精细化 熔断策略 按异常比例 ✅ 按慢调用比例 + 异常比例 + 异常数 规则动态修改 需要改代码——不够灵活 ✅ Dashboard 实时改——不需要重启 系统自适应 不支持 ✅ 根据 Load/CPU/RT 自动限流 规则持久化 Archaius(不推荐) ✅ 推/拉模式——Nacos/Apollo/ZK 等 Hystrix 停更后——Sentinel 和 Resilience4j 是两大替代。Resilience4j 更"云原生"(轻量、函数式),Sentinel 更"企业级"(Dashboard 控制台、丰富的规则面板)。如果你的团队用了 Spring Cloud Alibaba——Sentinel 是默认选择。 ...

十二月 6, 2022 · 4 分钟 · 756 字 · yaomingye
Cat Radio