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

OpenFeign 生产实战——Nacos + Sentinel + 性能调优

生产实战:Nacos + Sentinel + 性能调优 📖 前置阅读:本文假设读者已掌握 OpenFeign 的配置和容错机制。如果还不熟悉,建议先阅读 OpenFeign 核心概念与快速上手 和 进阶——配置、拦截器与容错。 一、⚡ Feign 调通了——但生产环境的三块拼图还缺着 前两篇搞定了 Feign 的基本用法、超时、重试、拦截器、Fallback。但生产环境还有三件事必须做: ① Feign + Nacos —— 不再写死 URL——服务自动发现、负载均衡 ② Feign + Sentinel —— 被调服务慢/挂了——熔断降级保护调用方 ③ Feign + 性能调优 —— Gzip 压缩、连接池、异步并发 二、🧩 Feign + Nacos 服务发现——零 URL 硬编码 2.1 依赖 <dependencies> <!-- OpenFeign --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!-- Nacos 服务发现 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!-- LoadBalancer——Feign 自动集成——不需要显式引入 --> </dependencies> 2.2 配置 spring: application: name: order-service cloud: nacos: discovery: server-addr: localhost:8848 namespace: production # 命名空间——隔离环境 group: ORDER_GROUP # 分组 // Feign 接口——只声明服务名,不写 URL @FeignClient(name = "user-service") // Nacos 中有 user-service 这个服务 public interface UserClient { @GetMapping("/api/users/{userId}") User getUser(@PathVariable("userId") Long userId); } 2.3 负载均衡策略 Feign 默认用 Spring Cloud LoadBalancer——轮询策略。想换成随机或加权策略: ...

十二月 12, 2022 · 7 分钟 · 1285 字 · yaomingye

OpenFeign 进阶——配置、拦截器与容错

进阶指南:配置、拦截器与容错 📖 前置阅读:本文假设读者已掌握 OpenFeign 的基本用法——@FeignClient、注解映射。如果还不熟悉,建议先阅读 OpenFeign 核心概念与快速上手。 一、⚡ 调通了——但第二天线上就出问题 Feign 的基本调用 5 分钟搞定。但一上生产——问题一个接一个: 问题 ①:用户服务偶尔慢 2 秒——订单服务的 Feign 一直等——线程全卡死 → 需要超时配置 问题 ②:网络抖动——请求偶尔失败——直接抛异常给用户 → 需要重试机制 问题 ③:用户服务需要 Token 鉴权——每次调 Feign 都要手动传 Header → 需要拦截器自动注入 问题 ④:用户服务挂了——Feign 调不通——订单服务的线程池被占满 → 需要 Fallback 降级 问题 ⑤:排查问题——Feign 到底发了什么请求?返回了什么? → 需要日志 这一篇把以上每个问题都给出具体配置和代码。 二、⏱️ 超时与重试——Feign 最容易被忽略的配置 2.1 默认的超时太长了 Feign 底层用 Ribbon(老版本)或 LoadBalancer(新版本)做负载均衡。默认超时: 参数 默认值 说明 connect-timeout 1s 建立 TCP 连接的超时——默认还好 read-timeout 60s 等响应的超时——太长了!一个慢请求能卡 60 秒 # application.yml——Feign 超时配置 spring: cloud: openfeign: client: config: # ① 全局配置——对所有 FeignClient 生效 default: connect-timeout: 3000 # 建连接最多等 3s read-timeout: 5000 # 等响应最多等 5s logger-level: BASIC # ② 按服务配置——针对特定服务 user-service: # 这个名字和 @FeignClient(name="user-service") 对应 connect-timeout: 2000 read-timeout: 3000 # 用户服务是核心——超时设短点 product-service: connect-timeout: 5000 read-timeout: 10000 # 商品服务偶尔慢——多给点时间 2.2 重试——哪些请求能重试,哪些不能 spring: cloud: openfeign: client: config: default: retryer: com.example.feign.DefaultRetryer # 自定义重试器 // 自定义重试策略 @Configuration public class FeignRetryConfig { @Bean public Retryer feignRetryer() { // 参数:period(初始间隔), maxPeriod(最大间隔), maxAttempts(最多尝试次数) // 下面 = 初始等 100ms → 每次乘 1.5 → 最多重试 3 次(总共 4 次) return new Retryer.Default(100, 1500, 3); } } 重试的时间线: 第 1 次请求 → 失败 → 等 100ms 第 2 次请求 → 失败 → 等 250ms 第 3 次请求 → 失败 → 等 625ms 第 4 次请求 → 成功 → 返回 如果第 4 次也失败 → 抛异常 ⚠️ 新手提示:POST 请求不要重试!如果创建订单的 POST 请求超时——Feign 自动重试——用户被扣了两次钱。GET 可以重试(幂等),POST/PUT/DELETE 绝不重试。要控制这个——用 @FeignClient 的 configuration 属性对不同接口用不同的重试策略。 ...

十二月 11, 2022 · 6 分钟 · 1144 字 · yaomingye

OpenFeign 核心概念与快速上手

核心概念与快速上手 一、⚡ 微服务拆了——然后呢? 你把单体应用拆成了用户服务和订单服务。数据库拆了、代码拆了、团队也拆了。然后订单服务需要查用户信息: // 拆分之前——同一个 JVM,直接调方法 User user = userService.getUserById(userId); // 拆分之后——跨 JVM、跨机器 // 你必须写一堆 HTTP 调用代码 RestTemplate restTemplate = new RestTemplate(); String url = "http://user-service:8081/api/users/" + userId; User user = restTemplate.getForObject(url, User.class); 这段代码有四个问题: 问题 RestTemplate 写法 怎么解决 URL 硬编码 "http://user-service:8081/api/users/" 服务名代替 IP:Port——自动发现 参数拼接繁琐 url + userId 手动拼 声明参数——自动放到 URL 上 返回值无类型保障 getForObject(url, User.class) 手动指定 接口方法声明返回类型——编译器检查 和本地调用差距太大 完全不同的调用方式——学习成本 写法完全和本地方法一样 OpenFeign 解决的就是这个问题——让你调远程服务和调本地方法一样,而且完全不侵入被调方的接口。 二、🧩 OpenFeign 是什么——一句话 OpenFeign 是一个声明式 HTTP 客户端。你只需要写一个 Java 接口 + 注解,Feign 自动帮你生成 HTTP 调用的实现。 ...

十二月 10, 2022 · 4 分钟 · 842 字 · 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

Gateway 生产实战——鉴权、限流、熔断与部署

Gateway 生产实战 📖 前置阅读:本文假设读者已掌握 Gateway 的 Route/Predicate/Filter 全操作。如果还不熟悉,建议先阅读前三篇:核心概念、Predicate 全解、Filter 全操作。 一、⚡ 路由和 Filter 都调通了——但你能上线吗? 开发环境一切正常——localhost 上 Gateway 跑得稳稳的。但上线之前你至少还要解决: ① 认证——用户登录后的 JWT Token 在网关统一校验 ② 限流——防止恶意刷接口——一个 IP 一秒最多 10 次 ③ 熔断——后端挂了——网关直接降级返回而不是把 500 抛给前端 ④ 跨域——前端从不同域名调网关——浏览器会拦截 ⑤ 监控——请求量、错误率、延迟——全部看不到就是盲飞 ⑥ TraceId——一个请求穿过网关到后端多个服务——怎么串联日志? 这一篇把以上每个问题都给出可直接使用的配置和代码。 二、🔐 JWT 鉴权——全局统一校验 2.1 为什么在网关做鉴权? 每个后端服务都自己解析 JWT——重复代码、分散维护、容易漏掉。在网关统一做——后端只信任网关传过来的 Header 就行了: 浏览器带 JWT → 网关解析 → Header 中放 userId + role → 后端直接用 2.2 完整的 JWT 鉴权 GlobalFilter @Component @Order(-100) public class JwtAuthGlobalFilter implements GlobalFilter { // 白名单——不需要 Token 的接口 private static final List<String> WHITE_LIST = List.of( "/api/public/login", "/api/public/register", "/api/public/health" ); // 从配置中心拿——这里简化为常量 private static final String SECRET_KEY = "your-256-bit-secret-key"; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { String path = exchange.getRequest().getURI().getPath(); // ① 白名单放行 if (isWhiteListed(path)) { return chain.filter(exchange); } // ② 提取 Token String authHeader = exchange.getRequest() .getHeaders().getFirst(HttpHeaders.AUTHORIZATION); if (authHeader == null || !authHeader.startsWith("Bearer ")) { return unauthorized(exchange, "缺少认证 Token"); } String token = authHeader.substring(7); // ③ 解析 JWT try { Claims claims = Jwts.parser() .setSigningKey(SECRET_KEY) .parseClaimsJws(token) .getBody(); // 检查是否过期 if (claims.getExpiration().before(new Date())) { return unauthorized(exchange, "Token 已过期"); } // ④ 把用户信息写入请求头——后端直接用 ServerHttpRequest mutatedRequest = exchange.getRequest().mutate() .header("X-User-Id", String.valueOf(claims.get("userId"))) .header("X-User-Name", claims.get("userName", String.class)) .header("X-User-Role", claims.get("role", String.class)) .build(); // ⑤ 用修改后的 Request 继续 return chain.filter(exchange.mutate().request(mutatedRequest).build()); } catch (JwtException e) { return unauthorized(exchange, "Token 无效: " + e.getMessage()); } } private boolean isWhiteListed(String path) { return WHITE_LIST.stream().anyMatch(path::startsWith); } private Mono<Void> unauthorized(ServerWebExchange exchange, String message) { exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); exchange.getResponse().getHeaders() .setContentType(MediaType.APPLICATION_JSON); // 返回 JSON 错误信息 byte[] body = ("{\"code\":401,\"message\":\"" + message + "\"}").getBytes(); DataBuffer buffer = exchange.getResponse() .bufferFactory().wrap(body); return exchange.getResponse().writeWith(Mono.just(buffer)); } } 2.3 后端如何信任网关 后端服务应该只信任从网关来的请求——加一个内部 Token 做服务间认证: ...

十二月 5, 2022 · 8 分钟 · 1502 字 · yaomingye
Cat Radio