ZnsCs 是不是最好的五人组?在微服务治理方案的内部评审里,有人会把“服务发现、配置中心、限流熔断、链路追踪、APM 监控”这五类能力打包成一套组合,然后问:难道他们五个真是唯一解?ZnsCs?这里的 ZnsCs 并不是官方定义,也不是某个发布过的标准组合,它可以看成一组常见开源组件的首字母缩写,例如 Zipkin、Nacos、Sentinel、Consul、SkyWalking。真正值得讨论的不是这个缩写本身,而是为什么“五个组件绑在一起”并不能自动构成最优解。接下来会拆开这个组合,讲清每个组件的职责、重叠、取舍,再给出一个最小可运行的微服务治理案例,最后提供一套可复用的选型清单和排错路径。如果你正面临“别人都这么装,所以我们也要这么装”的方案评审,这篇内容会很有用。
1. ZnsCs 不是标准答案,先理解它是怎么来的
1.1 从一句玩笑话看技术选型困境
很多团队在选型评审时,会听到底气十足的一句话:“别人都是这么搭的,这五个组件就是标准答案。” 这句话在工作群里出现几次之后,就演变成了“难道他们五个真是唯一解?最好的五人组?ZnsCs?” 提问里带着调侃,但背后的焦虑是真实的:如果团队缺少足够的选型经验,面对一堆开源组件时,确实很容易把“最多人用的组合”误认为“唯一解”。
ZnsCs 恰好是一个适合解剖这样的“五人组”:Z 可以代表 Zipkin,N 代表 Nacos,S 代表 Sentinel,C 代表 Consul,小写 s 代表 SkyWalking。拆开后会发现,它并不是一个由官方定义的标准组合,而是把常见微服务治理组件按首字母拼在了一起。它真正讨论的价值在于:五个组件各自解决什么问题,哪些能力重叠,哪些组件可以合并,哪些可以去掉。
1.2 五个组件的定位和各自的边界
仅以这套理解为例,先给每个组件一个定位:
- Zipkin:分布式链路追踪,记录请求从客户端到服务端经过的每个节点,并展示调用耗时。
- Nacos:服务注册与发现、配置中心。服务启动后把自己的地址注册到 Nacos,其他服务通过 Nacos 找到它。
- Sentinel:流量防护,提供限流、熔断、降级能力,保护服务不被突发流量打垮。
- Consul:服务注册与发现、健康检查、键值存储,也可以做配置中心。
- SkyWalking:APM 系统,除了链路追踪,还提供指标采集、拓扑分析、告警能力。
单看定位会发现,这套理解里的 Nacos 与 Consul 都包含服务发现能力,Zipkin 与 SkyWalking 都包含链路追踪能力。也就是说,如果五个组件全上,至少有两处明显重叠:注册中心和链路追踪。
| 组件 | 首字母 | 主要能力 | 典型使用者 | 运维成本 |
|---|---|---|---|---|
| Zipkin | Z | 链路追踪,UI 查看调用拓扑 | 中小团队,想快速接入链路追踪 | 中 |
| Nacos | N | 注册发现 + 配置中心 | 国内微服务团队常用 | 中 |
| Sentinel | S | 限流、熔断、降级 | 需要保护核心接口的团队 | 中 |
| Consul | C | 注册发现 + 健康检查 + KV | 采用 HashiCorp 技术栈或已部署 Consul 的团队 | 中高 |
| SkyWalking | s | 全链路 APM:追踪、指标、拓扑、告警 | 需要深入可观测性的团队 | 较高 |
表格里可以看到,Zipkin 和 SkyWalking 都做链路追踪,Nacos 和 Consul 都做服务发现。如果团队规模不大,五个组件全部部署意味着要维护至少两套注册中心、两套链路追踪存储,还可能出现监控数据重复采集的问题。
1.3 功能重叠是真实存在的,先列出来
服务发现这一层,Nacos 和 Consul 都是高可用注册中心。Nacos 在国内 Spring Cloud Alibaba 生态里集成方便,自带控制台和配置管理;Consul 在 HashiCorp 生态、服务网格、Kubernetes 场景里同样常见。两者都提供服务注册、心跳检查、注销机制,业务方通常只需要一个注册中心。
链路追踪这一层,Zipkin 定位相对轻量,只解决调用链展示;SkyWalking 覆盖更广,还会采集 JVM、服务指标并生成拓扑。如果同时部署,数据会有重复采集,也需要维护两套存储和 UI。因此,ZnsCs 作为“五人组”只是方便的讨论代号,不是应该原样照搬的部署方案。
2. 在动手组合前,先明确微服务治理需要解决什么问题
2.1 运维角色:服务注册、发现、配置、路由
注册中心解决的是“调用方怎么知道提供方地址”的问题。服务数量少的时候,写死 IP 和端口也能跑;服务超过几十个、实例经常扩缩容之后,静态配置就会变成一场灾难。注册中心允许服务启动时上报地址,调用方按服务名查询地址,并提供心跳和健康检查来剔除不可用实例。Nacos、Consul、ZooKeeper、etcd 都能承担这个角色,但它们的一致性模型、健康检查方式、配置能力各不相同。
配置中心解决的是“配置修改后如何不重启就生效”的问题。Nacos 把配置和注册放在一起,Spring Cloud 集成成本低;Consul 也有 KV 和配置同步能力,但日常使用中更多被当作注册中心来用。对于大多数 Java 微服务,Nacos 可以同时覆盖注册和配置,如果团队已经维护了 Consul,就没必要再引入一个 Nacos 专门做配置。
2.2 开发角色:限流、熔断、降级、重试
限流保护的是自身处理能力:假设核心接口每秒最多能处理 2 千请求,超出部分要么排队、要么拒绝。Sentinel 可以按 QPS、线程数、并发数设置规则,还能做热点参数限流。熔断解决的是下游故障传导:当某个接口错误率上升到阈值,调用方快速失败,避免连接池耗尽。这些能力在 Sentinel、Resilience4j、Hystrix 里都能找到,但它们的规则存储、控制台展示、Spring Cloud 集成方式不同。ZnsCs 里的 S 之所以值得保留,是因为在 Spring Cloud Alibaba 生态里它和 Nacos 的注册发现结合得比较自然。
2.3 可观测性角色:指标、日志、链路追踪
可观测性回答三个问题:哪里慢了、哪里错了、哪里资源不够了。指标用数字反映系统健康程度:请求数、错误数、P99 延迟;日志记录错误细节;链路追踪把一次调用里经过的所有服务串起来。Zipkin 偏向链路追踪,SkyWalking 在链路之外还会做指标和拓扑。如果团队已经有 Prometheus 采集指标、Kibana 收集日志,链路追踪可以选择轻量的 Zipkin;如果没有这些基础设施,SkyWalking 的一体化收益会更明显。
| 组件 | 注册发现 | 配置管理 | 限流熔断 | 链路追踪 | 指标监控 |
|---|---|---|---|---|---|
| Nacos | 支持 | 支持 | 否 | 否 | 否 |
| Consul | 支持 | 支持 | 否 | 否 | 否 |
| Sentinel | 否 | 否 | 支持 | 否 | 统计但依赖控制台 |
| Zipkin | 否 | 否 | 否 | 支持 | 否 |
| SkyWalking | 否 | 否 | 否 | 支持 | 支持 |
从表格能看出来,不同角色对组件的诉求不一样。同一个组件往往有多个角色的能力,比如 Nacos 既是注册中心又是配置中心,SkyWalking 既是链路追踪又是指标平台。如果只看某一个角色,选择会容易很多;一旦把多个角色叠到同一个组件上,就需要评估它的可观测性、告警能力和运维复杂度。这就是为什么不能只看“五个组件听起来很全”,而要回到问题本身去判断。
3. 最小可运行案例:用 Nacos + Sentinel 先跑通治理链路
3.1 环境准备与技术选型
这个案例不会把 ZnsCs 五个组件全部部署,而是先把最核心、重叠最少的“注册中心 + 限流”链路跑通,再在下一节把链路追踪二选一接入。这样可以准确看出每个组件是否必需。环境建议:
- JDK 1.8+
- Maven 3.6+
- Nacos Server 2.x 单机版
- Sentinel Dashboard 1.8.x 或与客户端匹配的版本
- Spring Boot 2.6.x / Spring Cloud Alibaba 2021.x 风格
如果原始项目没有锁定版本,落地前到 Maven 仓库确认 spring-cloud-alibaba-dependencies 的版本,避免依赖冲突。下面写法仅用于说明思路,实际项目要结合自己的包名、路径和版本调整。
3.2 创建项目结构
示例项目是一个极简的 provider / consumer 结构:
zns-cs-demo ├── pom.xml ├── provider │ ├── pom.xml │ └── src/main/java/com/example/provider/ProviderApplication.java │ └── src/main/resources/application.yml └── consumer ├── pom.xml ├── src/main/java/com/example/consumer/ConsumerApplication.java ├── src/main/java/com/example/consumer/ProviderClient.java └── src/main/resources/application.yml父 pom 主要做版本管理:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.13</version> </parent> <properties> <spring-cloud.version>2021.0.5</spring-cloud.version> <spring-cloud-alibaba.version>2021.0.5.0</spring-cloud-alibaba.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>${spring-cloud-alibaba.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>这里需要说明:Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者版本必须配套。版本太高或太低都可能出现 Nacos 客户端无法连接、Sentinel 依赖注入失败等问题。
3.3 provider 服务实现
provider 只提供一个/hello接口,注册到 Nacos。
依赖:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> </dependencies>ProviderApplication.java:
package com.example.provider; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication @RestController public class ProviderApplication { public static void main(String[] args) { SpringApplication.run(ProviderApplication.class, args); } @GetMapping("/hello") public String hello(@RequestParam(required = false) String name) { return "hello " + (name == null ? "zns-cs" : name); } }application.yml:
server: port: 8081 spring: application: name: provider cloud: nacos: discovery: server-addr: 127.0.0.1:8848这里的spring.application.name会被注册到 Nacos,作为服务名。服务名不能随意改,后面 Feign 调用时用到的就是它。
3.4 consumer 服务实现与 Sentinel 限流
consumer 通过 OpenFeign 调用 provider,并开启 Sentinel。
依赖:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> </dependencies>ProviderClient:
package com.example.consumer; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; @FeignClient(name = "provider") public interface ProviderClient { @GetMapping("/hello") String hello(@RequestParam("name") String name); }ConsumerApplication.java 加@EnableFeignClients:
package com.example.consumer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; @SpringBootApplication @EnableFeignClients @RestController public class ConsumerApplication { @Resource private ProviderClient providerClient; public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } @GetMapping("/call") public String call(@RequestParam String name) { return providerClient.hello(name); } }application.yml:
server: port: 8082 spring: application: name: consumer cloud: nacos: discovery: server-addr: 127.0.0.1:8848 sentinel: transport: dashboard: 127.0.0.1:8858 port: 8719spring.cloud.sentinel.transport.port是客户端与控制台通信的端口,默认是 8719。如果同一台机器上启动多个 Sentinel 客户端,这个端口要避免冲突。
3.5 启动和验证
启动步骤:
- 启动 Nacos Server:进入 Nacos 的 bin 目录,执行
sh startup.sh -m standalone,Windows 下用startup.cmd -m standalone。 - 启动 Sentinel Dashboard:
java -Dserver.port=8858 -jar sentinel-dashboard.jar。 - 启动 provider。
- 启动 consumer。
- 访问
http://localhost:8082/call?name=test,预期返回hello test。
然后在 Sentinel 控制台为/call加一个 QPS 流控规则,阈值设为 1。连续快速刷新接口,会看到部分请求被拦截,提示 “Blocked by Sentinel”。这样最小链路就跑通了。
验证时要注意一点:Sentinel 是懒加载,如果没有访问过/call,控制台不会显示该应用。先访问一次接口,再刷新控制台。
4. 链路追踪到底选 Zipkin 还是 SkyWalking
4.1 两者能力对比
| 维度 | Zipkin | SkyWalking |
|---|---|---|
| 定位 | 链路追踪系统,偏轻量 | APM 平台,追踪、指标、拓扑、告警 |
| 接入方式 | 客户端依赖 + 上报;Spring Cloud Sleuth 集成 | Java Agent 无代码侵入 |
| 存储 | 默认内存或 ES | ES、MySQL、PostgreSQL、TiDB 等 |
| 单服务资源占用 | 较低 | 中高,取决于采样率 |
| UI 能力 | 调用链列表、依赖图 | 拓扑图、JVM 指标、调用链、告警规则 |
| 团队维护成本 | 低到中 | 较高,功能多 |
这里的资源占用和维护成本是相对概念,实际数值取决于采样率、存储容量、实例数量。选择时不要只看功能,还要看团队能否长期维护。
4.2 接入 Zipkin 的基本方式
在 provider 和 consumer 的 pom 中加入依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId