在微服务架构里,服务间的调用和配置管理是核心问题。这篇文章就从注册中心入手,带你了解常见注册中心特性,再深入 Nacos 的实战操作,包括环境搭建、服务注册,还有 Ribbon 的服务调用与负载均衡,最后聊聊 Nacos 的配置管理,全是干货,跟着做就能上手!

一、微服务的注册中心

咱们先搞懂注册中心是啥 —— 它就像微服务架构里的 “通讯录”,记着服务和服务地址的对应关系。服务启动后会把自己的信息 “登记” 到这儿,要是想调用别的服务,查下这个 “通讯录” 就能找到地址。

1.1 注册中心的主要作用

注册中心可不是只存个地址那么简单,它是微服务的 “协调者”,主要干这三件事:

  • 服务发现:包括服务注册 / 反注册(保存服务提供者和调用者信息)、服务订阅 / 取消订阅(调用者订阅提供者信息,最好能实时推送),有的还支持服务路由(筛选整合服务提供者)。
  • 服务配置:服务能订阅相关配置,注册中心也能主动把配置推给服务,不用一个个改服务配置。
  • 服务健康检测:盯着服务提供者的状态,要是某个服务挂了,能及时发现,避免调用到故障服务。

1.2 常见的注册中心对比

市面上主流的注册中心有 4 个,各有特点,咱们用表格对比下,一目了然:

组件名 开发语言 CAP 一致性算法 服务健康检查 对外暴露接口
Eureka Java AP 可配支持 HTTP
Consul Go CP Raft 支持 HTTP/DNS
Zookeeper Java CP Paxos 支持 客户端
Nacos Java AP Raft 支持 HTTP

这里得提下 Eureka 的情况:Eureka 2.x 已经闭源了,官网说继续用 2.x 分支的代码要 “自负风险”,所以现在很多项目会用 Nacos 替代 Eureka,毕竟 Nacos 不仅是注册中心,还能当配置中心,功能更全。

二、Nacos 简介与实战

Nacos 的定位是 “动态服务发现、配置管理和服务管理平台”,简单说就是 “注册中心 + 配置中心” 二合一,而且是 Spring Cloud Alibaba 的组件,和 Spring 生态兼容性特别好。咱们直接上手实战,把它用起来!

2.1 Nacos 环境搭建

第一步先把 Nacos 跑起来,很简单,就 3 步:

  1. 下载安装包:去 GitHub 下载,地址是Releases · alibaba/nacos · GitHub,选 zip 格式的,下载后解压缩。
  2. 启动 Nacos:打开命令行,切换到 nacos 的 bin 目录,执行startup.cmd -m standalone(单机模式,开发测试用够了),或者直接双击 bin 目录里的 startup.cmd。
  3. 访问 Nacos:打开浏览器,输入http://localhost:8848/nacos,默认账号密码都是 nacos,登录后就能看到控制台了。

2.2 把微服务注册到 Nacos

咱们以商品微服务(shop-product)和订单微服务(shop-order)为例,把它们注册到 Nacos 上,步骤基本一样:

2.2.1 商品微服务注册
  1. 在 shop-product 模块的 pom.xml 里,加 Nacos 的依赖(Spring Cloud Alibaba 的依赖要先配好版本)。
  2. 在微服务的主类上,加@EnableDiscoveryClient注解,告诉 Spring 这是个要注册到服务中心的服务。
  3. 在 application.yml 里,加 Nacos 的地址配置:
spring:
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
  1. 启动商品微服务,再去 Nacos 控制台的 “服务列表” 里看,就能找到 service-product(服务名)了,说明注册成功。
2.2.2 订单微服务注册

和商品微服务步骤一样,改 shop-order 模块:

  1. 加 Nacos 依赖。
  2. 主类加@EnableDiscoveryClient
  3. application.yml 配 Nacos 地址。
  4. 启动服务,去 Nacos 控制台确认,service-order 能显示出来就 OK 了。
  5. 最后改下 OrderController,用DiscoveryClient就能获取注册中心的服务列表,为后续调用做准备。

三、服务调用 Ribbon 入门

现在服务都注册到 Nacos 了,但服务之间怎么调用?比如订单服务要查商品信息,总不能硬写商品服务的地址吧?这时候就需要 Ribbon 了。

3.1 Ribbon 概述

3.1.1 什么是 Ribbon

Ribbon 是 Netflix 开源的负载均衡器,专门帮咱们管理 HTTP 和 TCP 客户端的行为。在 Spring Cloud 里,Nacos 通常和 Ribbon 搭配用 ——Ribbon 从 Nacos 里读服务列表,调用服务时还能做负载均衡,不用咱们自己写逻辑。

3.1.2 Ribbon 的主要作用

Ribbon 就干两件核心事:

  1. 服务调用:它会把从 Nacos 拿到的服务列表,整理成 “服务名 - 请求路径” 的映射,再借助 RestTemplate 发起调用,不用记具体的 IP 和端口。
  2. 负载均衡:要是一个服务有多个实例(比如商品服务开了 8081 和 8082 两个端口),Ribbon 会按算法选一个实例调用,避免某个实例压力太大。

3.2 基于 Ribbon 实现订单调用商品服务

3.2.1 坐标依赖

不用额外加 Ribbon 的依赖!因为 Spring Cloud 的服务发现依赖(比如 nacos-discovery)里已经包含 Ribbon 了,直接用就行。

3.2.2 工程改造

主要改服务消费者(这里是订单服务),服务提供者(商品服务)只要正常提供接口就行(比如写个根据 ID 查商品的接口,控制台打印下查询信息,方便看效果)。

订单服务改造就 1 步:在创建 RestTemplate 的方法上,加@LoadBalanced注解,告诉 Spring,这个 RestTemplate 要走 Ribbon 的负载均衡逻辑。

然后调用的时候,不用写 IP 和端口,直接用服务名就行,比如:

restTemplate.getForObject("http://service-product/product/1", Product.class);

这里的service-product就是商品服务在 Nacos 里的服务名,Ribbon 会自动把服务名换成具体的地址。

四、服务调用 Ribbon 高级(负载均衡实战)

前面提到 Ribbon 能做负载均衡,这部分咱们深入下,看看负载均衡的原理和 Ribbon 的具体用法。

4.1 负载均衡概述

4.1.1 什么是负载均衡

简单说,负载均衡就是把请求 “分摊” 给多个服务实例。比如商品服务开了 3 个实例,来了 100 个请求,每个实例大概处理 30 多个,这样不会有实例扛不住,系统稳定性更高。

它的核心逻辑是:前面有个 “负载均衡器”(这里是 Ribbon),按指定算法把流量分配到后端的服务集群上,实现并行扩展。

4.1.2 客户端负载均衡与服务端负载均衡

负载均衡分两种,咱们得搞清楚区别:

  • 服务端负载均衡:请求先发给负载均衡服务器(比如 Nginx),服务器按算法选一个服务实例转发过去,逻辑在服务器端。
  • 客户端负载均衡:客户端自己手里有服务列表,发请求前自己按算法选一个实例,逻辑在客户端(Ribbon 就是这种)。

Ribbon 是客户端负载均衡,好处是不用额外搭负载均衡服务器,客户端自己就能处理,效率更高。

4.2 基于 Ribbon 实现负载均衡

4.2.1 搭建多服务实例

咱们以商品服务为例,开两个实例(8081 和 8082 端口),步骤如下:

  1. 在 IDEA 里,复制商品服务的启动配置(比如叫 ProductApplication2)。
  2. 在 VM options 里加-Dserver.port=8082,指定第二个实例的端口。
  3. 分别启动商品服务的 8081 和 8082 实例,去 Nacos 控制台看,service-product 会显示两个实例。

然后订单服务多调几次商品接口,看商品服务的控制台 —— 会发现 8081 和 8082 轮流打印日志,这就是 Ribbon 的默认负载均衡算法(轮询)在起作用。

4.2.2 负载均衡策略

Ribbon 内置了 7 种常用的负载均衡策略,核心接口是com.netflix.loadbalancer.IRule,咱们一个个说:

  1. 随机策略(RandomRule):纯随机选一个实例,简单粗暴。
  2. 轮询策略(RoundRobinRule):默认策略,按顺序轮流选,比如 1→2→3→1→2→3,底层用 CAS + 自旋锁保证线程安全。
  3. 重试策略(RetryRule):选一个实例调用,要是失败了,在指定时间内重试,相当于 “重试 + 其他策略”(比如重试轮询)。
  4. 权重策略(WeightedResponseTimeRule):响应快的实例权重高,被选中的概率大。刚启动时用轮询,等积累了响应时间数据后,再按权重来。
  5. 空闲策略(BestAvailableRule):选并发量最小的实例(过滤掉故障实例后,看过去 30 分钟的并发),刚启动时用轮询。
  6. 下限策略(AvailabilityFilteringRule):基于轮询,但会过滤掉故障实例和并发量过高的实例,直到选到符合条件的。
  7. 自主策略(ZoneAvoidanceRule):考虑 “机房大区(Zone)” 和实例可用性,优先选同一个 Zone 里可用、并发低的实例,适合多机房场景。
自定义负载均衡策略

想改策略很简单,有两种方式:

方式 1:全局设置(所有服务调用都用这个策略)
在 Spring 的配置类里,定义一个 IRule 的 Bean,比如用随机策略:

@Bean
public IRule randomRule() {
    return new RandomRule();
}

方式 2:局部设置(只对某个服务生效)
在 application.yml 里配置,比如只让订单服务调用商品服务(service-product)时用随机策略:

service-product:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

五、Nacos 配置管理

Nacos 不只是注册中心,还是个优秀的配置中心。要是你有几十上百个微服务实例,改配置总不能一个个重启吧?用 Nacos 配置管理,能集中管理配置,还能实现热更新(改配置不用重启服务)。

5.1 统一配置管理

5.1.1 在 Nacos 中添加配置文件

先在 Nacos 控制台加配置文件,步骤如下:

  1. 登录 Nacos,进入 “配置管理→配置列表”,点击 “+” 新建配置。
  2. 填写关键信息:
    • Data ID:格式是 “服务名 - 环境简称。文件后缀”,比如商品服务的开发环境配置,就是service-product-dev.yaml
    • Group:默认用 DEFAULT_GROUP 就行,不用改。
    • 配置格式:选 YAML(或 Properties,看你习惯)。
    • 配置内容:写需要集中管理的配置,比如数据库连接、服务端口等(基本不变的配置还是存在本地,只把需要热更新的放 Nacos)。

举个例子,配置内容可以是:

server:
  port: 8081
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql:///shop?serverTimezone=UTC
    username: root
    password: root
5.1.2 从微服务拉取配置

微服务要拉 Nacos 的配置,得解决一个问题:微服务启动时,先读本地配置还是先读 Nacos 配置?要是本地没 Nacos 地址,怎么拉 Nacos 的配置?

Spring 的解决方案是:引入bootstrap.yaml文件 —— 它的加载优先级比application.yaml高,会先加载,用来配置 Nacos 地址等 “启动必需的配置”。

具体步骤:

  1. 加依赖:在微服务的 pom.xml 里,加 nacos-config 依赖:
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

  1. 新建 bootstrap.yaml:把原来的 application.yaml 里的 Nacos 地址等配置,移到 bootstrap.yaml 里,比如:
spring:
  application:
    name: service-product  # 服务名,要和Nacos里的Data ID对应
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848  # Nacos配置中心地址
        file-extension: yaml  # 配置文件格式,和Nacos里的一致
  profiles:
    active: dev  # 环境标识,和Data ID里的“dev”对应
  1. 启动微服务:微服务会先读 bootstrap.yaml,拿到 Nacos 地址,再去 Nacos 拉对应的配置,和本地配置合并后启动,不用改本地配置就能生效。

5.2 配置热更新(改配置不用重启服务)

咱们希望改 Nacos 里的配置后,微服务不用重启就能生效,这就是热更新。有两种实现方式,都很简单。

5.2.1 方式一:用 @RefreshScope 注解

在需要动态读取配置的类上(比如 Controller),加@RefreshScope注解,然后用@Value注入配置就行。

举个例子:

@RestController
@RefreshScope  // 关键注解,开启热更新
public class NacosConfigController {
    // 注入Nacos里的配置(比如config.appName)
    @Value("${config.appName}")
    private String appName;

    @GetMapping("/nacos-config-test1")
    public String nacosConfigTest1() {
        return appName;  // 改Nacos里的config.appName,这里返回值会变
    }
}
5.2.2 方式二:用 ConfigurableApplicationContext

不用加注解,直接注入ConfigurableApplicationContext,通过它的环境对象获取配置,也能实现热更新。

例子:

@RestController
public class NacosConfigController {
    @Autowired
    private ConfigurableApplicationContext applicationContext;

    @GetMapping("/nacos-config-test2")
    public String nacosConfigTest2() {
        // 从环境中获取配置,支持热更新
        return applicationContext.getEnvironment().getProperty("config.appName");
    }
}

5.3 配置共享(避免重复配置)

很多配置是多个服务共用的(比如数据库连接、Nacos 注册中心地址),总不能每个服务都写一遍吧?Nacos 支持配置共享,分两种场景。

5.3.1 同服务内配置共享

同一个服务的不同环境(比如 dev、test),可能有很多公共配置(比如服务端口格式、日志配置)。解决方案是:

  1. 新建一个以 “服务名.yaml” 命名的配置文件(比如service-product.yaml),把公共配置放这里。
  2. 再建环境专属配置(比如service-product-dev.yamlservice-product-test.yaml),放环境独有的配置(比如 dev 环境的数据库地址、test 环境的数据库地址)。

微服务启动时,会自动合并 “服务名.yaml” 和 “服务名 - 环境.yaml” 的配置,环境配置会覆盖公共配置里的相同项。

比如公共配置service-product.yaml里写:

config:
  appName: product-service

dev 环境配置service-product-dev.yaml里写:

config:
  env: dev  # 独有的环境标识

然后在 Controller 里注入config.env,就能拿到 dev,改环境也不用动公共配置。

5.3.2 不同微服务共享配置

多个服务共用的配置(比如所有服务的数据库连接、Nacos 注册地址),可以建一个公共配置文件,让所有服务都引用它。

步骤:

  1. 在 Nacos 里新建一个公共配置,比如 Data ID 是all-service.yaml,内容是共用配置:
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql:///shop?serverTimezone=UTC
    username: root
    password: root
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848  # 所有服务共用的Nacos注册地址
  1. 在每个服务的 bootstrap.yaml 里,加两行配置,指定要引用的公共配置:
spring:
  cloud:
    nacos:
      config:
        shared-dataids: all-service.yaml  # 要引用的公共配置Data ID
        refreshable-data


3. 启动服务验证:不管是商品服务还是订单服务,都能读取到`all-service.yaml`里的数据库配置和Nacos注册地址,不用每个服务重复写了。

        配置共享的优先级 当Nacos里的配置和服务本地配置出现相同属性时,谁的优先级更高?记住这个顺序: **Nacos中的“服务名-profile.yaml”(环境专属配置) > Nacos中的“服务名.yaml”(同服务公共配置) > 服务本地配置,比如本地配置和Nacos里的“服务名-dev.yaml”都有`server.port`,最终会用Nacos里“服务名-dev.yaml”的端口,这样能保证线上配置统一可控。

         总结: 其实这些组件的核心目的都是为了解决微服务架构中的“协作问题”:注册中心让服务找到彼此,Ribbon让服务调用更均衡稳定,Nacos配置管理让配置变更更高效。跟着文中的步骤实操一遍,就能快速掌握这些微服务开发的核心技能,后续再结合实际项目优化调整就行~

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐