Dubbo 生产环境部署与调优

Dubbo 生产部署 📖 前置阅读:本文是 Dubbo 系列的终篇,假设读者已经掌握前五篇的全部内容(核心架构、SpringBoot 集成、集群容错与负载均衡、注册中心、Dubbo 3.x 新特性)。 一、⚡ 问题切入:单机开发的配置能上生产吗? 前五篇的页面配置——超时 1 秒、重试 2 次、单注册中心、无限流无监控——只能用来学习。生产环境: 单点/隐患 后果 一个 Nacos 实例 Nacos 挂了 → 新 Consumer 启动不了 → 新 Provider 无法注册 没设并发限制 一个 Provider 被大量请求打爆 → 线程池满 → 所有请求排队或失败 超时设太短 Provider 还在处理,Consumer 已断开 → 重复调用 没有监控 Provider 变慢了、Success Rate 下降了——完全不知道 无优雅上下线 重启 Provider → 正在处理的请求全部失败 生产最低配:Nacos 集群(至少 3 台) + Provider 并发限制 + Consumer 合理超时 + Dubbo Admin 监控 + 优雅上下线。 ...

十一月 24, 2022 · 6 分钟 · 1139 字 · yaomingye

Dubbo 3.x 新特性:Triple 协议与应用级服务发现

Dubbo 3.x 新特性 📖 前置阅读:本文假设读者已掌握 Dubbo 的基本 RPC 开发、注册中心使用(Nacos)和 dubbo 协议。如果还不熟悉,建议先阅读 Dubbo 核心架构与 RPC 模型 和 注册中心:Nacos 与 Zookeeper。 一、⚡ 问题切入:dubbo 协议有什么不够用的? 第一篇说了 dubbo 协议的优点——TCP 长连接 + Hessian2 二进制序列化,性能极佳。但它的设计产生于 2011 年: dubbo 协议的局限 为什么是问题 私有二进制协议 浏览器和 curl 调不了——非 HTTP,无法穿透通用 HTTP 网关 Java 中心 协议体是 Java 特有的——Go/Node.js/Python 客户端需要单独实现协议栈 服务网格不友好 Istio/Envoy 基于 HTTP/1.1 和 HTTP/2 做流量治理——私有协议无法被 Sidecar 理解 接口级服务发现 注册中心存储的是接口粒度数据(org.example.OrderService:getOrderById)——微服务有几百个接口时,注册数据爆炸 Dubbo 3.x 的两大革新直接解决这些问题: Triple 协议——基于 HTTP/2 + Protobuf,解决私有协议问题 应用级服务发现——从接口粒度改为应用粒度,解决注册数据爆炸 二、Triple 协议 —— HTTP/2 能力 + RPC 性能 2.1 Triple 协议的本质 Triple 协议 = 将 gRPC 的传输协议(HTTP/2 + Protobuf)搬到 Dubbo 上,同时保留 Dubbo 的服务治理能力(注册中心、负载均衡、集群容错)。 ...

十一月 23, 2022 · 5 分钟 · 944 字 · yaomingye

Dubbo 注册中心:Nacos 与 Zookeeper

Dubbo 注册中心 📖 前置阅读:本文假设读者已掌握 Dubbo 的基本 RPC 开发和集群容错配置。如果还不熟悉,建议先阅读 SpringBoot Dubbo 全操作指南 和 集群容错与负载均衡。 一、⚡ 问题切入:Registry 到底是什么? 前面三篇反复提到"注册中心"——Provider 向它注册,Consumer 从它订阅。但注册中心不只是一个"存地址的地方": 注册中心的职责 具体行为 服务注册 Provider 启动时把"IP:Port + 接口名 + 元数据"写入 Registry 服务发现 Consumer 启动时从 Registry 拉取"接口名 → 地址列表"的映射 健康检查 检测 Provider 是否存活——不存活就剔除 变更推送 Provider 地址列表变化时,主动通知 Consumer 配置管理(Nacos) 动态下发配置——不需要重启应用 关键点:Registry 只在服务发现阶段起作用。Consumer 拿到 Provider 地址后——直连调用,不经过 Registry。这是和 MQ 的 Broker 最本质的区别。 二、服务注册与发现的全链路 2.1 详细流程 flowchart TD classDef startEnd fill:#701a4c,stroke:#e11d48,stroke-width:2px,color:#fce7f3,font-weight:bold; classDef process fill:#1e1e24,stroke:#6b7280,stroke-width:1.5px,color:#e5e7eb; 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; subgraph REGISTER [注册阶段] P1[Provider 启动] --> P2["向 Nacos 注册\nServiceName: order-provider\nIP: 192.168.1.10\nPort: 20880\nInterface: org.example.OrderService\nMethods: getOrderById, createOrder..."] end subgraph DISCOVER [发现阶段] C1[Consumer 启动] --> C2["向 Nacos 订阅\nInterface: org.example.OrderService"] C2 --> C3["Nacos 返回\n[192.168.1.10:20880\n 192.168.1.11:20880\n 192.168.1.12:20880]"] C3 --> C4[Consumer 存到本地缓存] C4 --> C5["选一个 Provider\n建立 TCP 长连接"] end subgraph HEARTBEAT [心跳阶段] P2 --> H1["Provider 每 5s\n向 Nacos 发心跳"] H1 --> H2{Nacos 15s\n没收到心跳?} H2 -- "是" --> H3["标记为不健康\n30s 后剔除"] H3 --> H4["推送变更通知\n给所有订阅的 Consumer"] H4 --> C6[Consumer 更新\n本地地址缓存] end class P1,C1,C5 startEnd; class P2,C2,C3,C4,C6,H1,H3,H4 process; class H2 highlight; 2.2 注册中心的存储结构 以 Nacos 为例,注册的数据长这样: ...

十一月 22, 2022 · 4 分钟 · 845 字 · yaomingye

Dubbo 集群容错、负载均衡与异步调用

Dubbo 集群容错 📖 前置阅读:本文假设读者已掌握 SpringBoot Dubbo 的基本 RPC 操作(@DubboService / @DubboReference)。如果还不熟悉,建议先阅读 SpringBoot Dubbo 全操作指南。 一、⚡ 问题切入:RPC 调用失败了怎么办? 上一篇文章的 @DubboReference 默认配置了三件事: Provider 有多个实例时——随机选一个(负载均衡) 调用失败时——自动重试 2 次(集群容错) 调用超过 1 秒——抛超时异常 默认策略覆盖了 80% 的场景,但剩下的 20% 需要精确控制——这就是 Dubbo 服务治理的核心:集群容错(怎么处理失败)、负载均衡(怎么选择节点)、调用模式(同步还是异步)。 二、集群容错 —— 失败了怎么办 2.1 六种集群容错策略 Dubbo 的 Cluster 接口定义了容错行为。Consumer 侧配置: 策略 配置值 行为 适用场景 Failover(默认) failover 失败后自动切换其他 Provider 重试,默认重试 2 次(共 3 次调用) 幂等的读操作 Failfast failfast 失败后立即报错——不重试 非幂等写操作(创建订单、扣库存) Failsafe failsafe 失败后吞掉异常——返回 null / 忽略错误 非关键操作(日志、通知) Failback failback 失败后记录到后台线程——定时重试 最终一致性场景(数据同步) Forking forking 同时调用所有 Provider——取第一个成功返回的 高可用低延迟,但浪费资源 Broadcast broadcast 逐个调用所有 Provider——任何一个失败就报错 通知所有实例(缓存刷新、配置更新) // 消费端配置集群容错策略 @DubboReference( cluster = "failfast", // 写操作——不重试 retries = 0 // 即使 Failover 模式,也可以设 retries=0 关掉重试 ) private OrderService orderService; 2.2 Failover 原理与幂等陷阱 Failover 是默认策略——它的逻辑是: ...

十一月 21, 2022 · 6 分钟 · 1164 字 · yaomingye

SpringBoot Dubbo 全操作指南

SpringBoot Dubbo 📖 前置阅读:本文假设读者已理解 Dubbo 的核心概念(Registry、Provider、Consumer、RPC 调用链路)。如果还不熟悉,建议先阅读 Dubbo 核心架构与 RPC 模型。 🎯 第一步:目标说明 上一篇用原生 Dubbo + Spring XML 写了 <dubbo:service>、<dubbo:reference>、<dubbo:registry>。SpringBoot 时代不需要那些 XML——dubbo-spring-boot-starter 用两个注解 + 一套 yml 配置替代全部 XML。 读完这篇会掌握: dubbo-spring-boot-starter 环境搭建——依赖 + yml 配置 @DubboService——暴露服务,替代 <dubbo:service> @DubboReference——引用远程服务,替代 <dubbo:reference> dubbo 协议 vs triple 协议——什么时候用哪个 Hessian2 / Fastjson2 序列化配置 Nacos 注册中心的 SpringBoot 集成 📋 第二步:前置条件 前置项 具体要求 验证命令 JDK 17+(8+ 也兼容) java -version SpringBoot 3.x(文中用 3.2) mvn dependency:tree | grep spring-boot Nacos 2.3.0(单机即可) docker ps | grep nacos 前置知识 Registry/Provider/Consumer 概念 — 确认 Nacos 在跑: ...

十一月 20, 2022 · 6 分钟 · 1213 字 · yaomingye

Dubbo 核心架构与 RPC 模型

Dubbo 核心架构 📖 前置阅读:本文假设读者理解基本的网络通信概念(TCP/IP、HTTP、序列化)。不需要预先了解 RPC——本文从零讲起。 一、⚡ 问题切入:HTTP REST 调用有什么"不够用"的? 互联网公司典型的微服务架构中,服务间通信最常见的方式是 HTTP REST: 订单服务 (OrderService) → 商品服务 (ProductService) GET /api/products/10001 → 返回 JSON {"id": 10001, "name": "iPhone", "stock": 50} POST /api/orders → 返回 JSON {"orderId": 20001, "status": "created"} 这套方案在服务数量少、调用量低时完全够用。但当公司扩张到几十个微服务、每秒上万次调用时,REST 的短板就暴露了: 痛点 REST 的具体表现 连接开销 每次请求都需要建 TCP 连接(HTTP/1.1 支持复用但仍是短连接语义)——高并发时 CPU 和内存吃紧 序列化冗余 JSON 文本格式——字段名重复传输,体量大、解析慢。对比二进制序列化,JSON 的带宽占用高出 3~10 倍 弱类型契约 没有强类型接口定义——调用方靠"文档"或"口头约定"知道参数和返回值类型。商品服务改了字段名,订单服务的代码编译通过、运行时崩 路由单一 URL 路由——无法按业务特性做灰度发布、权重分配、同机房优先路由 缺乏治理 没有内置的负载均衡、熔断、限流——需要额外引入 Spring Cloud、Sentinel 等组件 这时候再看 Dubbo 的设计思路——不是把 HTTP 做得更好,而是用另一套协议和模型来替代 HTTP: ...

十一月 19, 2022 · 6 分钟 · 1089 字 · yaomingye

Kafka 生产环境部署与调优

Kafka 生产环境实战 📖 前置阅读:本文是 Kafka 系列的终篇,假设读者已经掌握前五篇的全部内容(核心架构、SpringBoot 集成、Producer 深入、Consumer 深入、Kafka Streams)。 一、⚡ 问题切入:单节点 Docker 的瓶颈 第一篇搭的单节点 KRaft Kafka(一个 Controller + Broker 合体进程)只能用来学习——生产环境中: 单点 后果 唯一的 Broker 挂了 所有消息不可发送/消费——整个 Kafka 瘫痪 无副本 Broker 磁盘损坏 → 消息永久丢失 JVM 内存不足 Full GC 频繁 → 消息延迟抖动 → Producer 超时失败 磁盘写满 Partition 无法写入 → Producer 阻塞或报错 生产最低配:3 台 Broker + 3 台 Controller(或 3 台合体节点,controller + broker 混合模式),每个 Topic 至少 2 副本。 二、KRaft 三节点集群搭建 2.1 架构设计 第一篇用的 KRaft 模式是单节点(Controller 和 Broker 运行在一个进程中)。生产环境拆开: ...

十一月 18, 2022 · 8 分钟 · 1529 字 · yaomingye

Kafka Streams 与高级特性

Kafka Streams 流处理实战 📖 前置阅读:本文假设读者已掌握 Kafka Consumer/Producer 的使用和 Offset/Partition 概念。如果还不熟悉,建议先阅读 Consumer 深入:位移管理与 Rebalance。 一、⚡ 问题切入:用 Consumer + Producer 写流处理有什么毛病? 假设需要统计每个商品的近 5 分钟销量。用 Consumer + Producer 写: // 用 Consumer + Producer 实现滑动窗口计数——代码量爆炸 Map<String, List<Long>> windowCache = new HashMap<>(); // 还需要处理:窗口过期清理、状态持久化、故障恢复、乱序数据... 这个需求正是流处理引擎的用武之地。Kafka Streams 是 Kafka 官方的流处理库——它不是另一个需要部署的服务(不像 Flink/Spark Streaming),而是一个 Java 库,跑在你的应用进程里。 Kafka Streams 本质:Consume → 计算 → Produce,全部走 Kafka,中间状态存在 Kafka 的本地 RocksDB 实例中。 flowchart LR classDef startEnd fill:#701a4c,stroke:#e11d48,stroke-width:2px,color:#fce7f3,font-weight:bold; classDef process fill:#1e1e24,stroke:#6b7280,stroke-width:1.5px,color:#e5e7eb; 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; INPUT[("input-topic\n原始数据")] -->|"consume"| KS[Kafka Streams\n计算引擎\n+ RocksDB 本地状态] KS -->|"produce"| OUTPUT[("output-topic\n处理结果")] KS -->|"备份"| CHANGELOG[("changelog-topic\n状态变更日志")] class INPUT,OUTPUT data; class CHANGELOG process; class KS highlight; 二、Kafka Streams 核心概念 2.1 KStream vs KTable Kafka Streams 有两个核心抽象,理解它们的区别是正确使用的前提: ...

十一月 17, 2022 · 6 分钟 · 1176 字 · yaomingye

Kafka Consumer 深入:位移管理与 Rebalance

Kafka Consumer 深入 📖 前置阅读:本文假设读者已掌握 SpringBoot Kafka 的基本消费操作(@KafkaListener)。如果还不熟悉,建议先阅读 SpringBoot Kafka 全操作指南。 一、⚡ 问题切入:消费者重启后,怎么知道上次读到哪了? RabbitMQ 的答案是"消息消费后就删了,不需要记位置"。RocketMQ 的答案是"Broker 帮你记 offset"。Kafka 的答案是——消费者自己记,记在一个叫 __consumer_offsets 的内部 Topic 里: 消费者在 Partition-2 上消费到 offset=1500 ↓ 提交 offset __consumer_offsets Topic: Key: (order-consumer-group, order-topic, 2) Value: offset=1500 消费者重启 ↓ ↓ 读取 offset 从 offset=1501 继续消费 这个设计是 Kafka 和 RabbitMQ/RocketMQ 最核心的消费端差异——Kafka 的消费者对自己的消费进度负全责。如果消费者忘记提交 offset,重启后就会从上次提交的位置重新消费,产生重复消息。 二、Offset 提交机制 2.1 自动提交 vs 手动提交 Kafka 提供了两种 Offset 提交方式: 提交方式 配置 行为 风险 自动提交 enable-auto-commit: true 每隔 auto.commit.interval.ms(默认 5s)自动提交 poll 返回的最大 offset 消息可能没处理完就提交了——进程挂了会丢消息 手动提交 enable-auto-commit: false + ack-mode: manual 消费者处理完消息后显式调用 ack.acknowledge() 消息可能处理完了但没提交——重启后重复消费 flowchart TD classDef startEnd fill:#701a4c,stroke:#e11d48,stroke-width:2px,color:#fce7f3,font-weight:bold; classDef condition fill:#2a1147,stroke:#a855f7,stroke-width:1.5px,color:#ede9fe,font-weight:bold; classDef process fill:#1e1e24,stroke:#6b7280,stroke-width:1.5px,color:#e5e7eb; classDef highlight fill:#450a0a,stroke:#dc2626,stroke-width:1.5px,color:#fecaca,font-weight:bold; POLL([poll 拉取消息]) --> PROCESS[处理消息] PROCESS --> MODE{提交模式} MODE -- "自动提交" --> AUTO["每隔 auto.commit.interval.ms\n自动提交最后一次 poll 的 offset"] MODE -- "手动提交" --> MANUAL["业务处理成功后\n显式调用 ack.acknowledge()"] AUTO --> RISK1["风险:消息还没处理完\n但 offset 已提交\n→ 进程挂了丢消息"] MANUAL --> RISK2["风险:消息已处理完\n但 offset 没提交\n→ 重启后重复消费"] class POLL startEnd; class MODE condition; class AUTO,MANUAL highlight; class RISK1,RISK2 process; 手动提交比自动提交更安全——至少你知道什么时候提交了。消息重复消费可以用幂等解决,但消息丢失无法恢复。 ...

十一月 16, 2022 · 6 分钟 · 1173 字 · yaomingye

Kafka Producer 深入:分区、ACK 与幂等

Kafka Producer 深入 📖 前置阅读:本文假设读者已掌握 SpringBoot Kafka 的基本发送操作(KafkaTemplate.send)。如果还不熟悉,建议先阅读 SpringBoot Kafka 全操作指南。 一、⚡ 问题切入:消息到底发到了哪个 Partition? 上一篇用 kafkaTemplate.send("order-topic", key, msg) 发送消息时,生产者背后发生了三件事: Partitioner 决定消息进入哪个 Partition 消息攒批——在内存 Buffer 中等待 batch.size 或 linger.ms 条件触发 根据 acks 配置决定什么时候认为发送成功 当你看到日志里 partition=1, offset=0 时,背后是这三个步骤的协作。每个步骤都有配置项可以调整——它们直接影响消息顺序、可靠性、吞吐量。 二、分区策略 —— 消息路由的第一个环节 2.1 默认分区策略 Kafka Producer 的 Partitioner 接口决定了每条消息进入哪个 Partition。默认实现是 DefaultPartitioner: // DefaultPartitioner 的逻辑(简化版) public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) { int numPartitions = cluster.partitionsForTopic(topic).size(); if (keyBytes == null) { // Key 为 null → 使用 Sticky 分区(粘性分区) // 不是轮询!是把一批消息都发到同一个 Partition,等 batch 满了才换下一个 return stickyPartition(topic, numPartitions); } else { // Key 不为 null → 用 murmur2 哈希 % Partition 数量 return Utils.murmur2(keyBytes) % numPartitions; } } 规则一:Key 为 null → Sticky Partition。Kafka 2.4 之前是轮询(Round Robin——每条消息换一个 Partition),2.4+ 改为 Sticky——把一批消息"粘"在同一个 Partition 上,等这个 Batch 满了或时间到了才切到下一个 Partition。这减少了网络请求次数——把同一批的消息打成一个请求发给同一个 Broker。 ...

十一月 15, 2022 · 6 分钟 · 1274 字 · yaomingye
Cat Radio