Lecture03-SpringCloud-Nacos服务注册与发现

文章目录

    [toc]

    单词词性音标含义
    registerv./ˈredʒɪstə(r)/注册(服务启动时把自己(IP+端口)登记到注册中心)
    discoverv./dɪˈskʌvə(r)/发现(客户端从注册中心查询服务实例列表)
    instancen./ˈɪnstəns/实例(一个运行中的服务进程,一个服务可多个实例)
    heartbeatn./ˈhɑːtbiːt/心跳(实例定期向注册中心报告"我还活着")
    healthn./helθ/健康(注册中心根据心跳/健康检查判断实例是否可用)
    providern./prəˈvaɪdə(r)/服务提供者(对外提供服务接口的一方)
    consumern./kənˈsjuːmə(r)/服务消费者(调用别人接口的一方)

    Lecture03-SpringCloud-Nacos服务注册与发现

    目标:先解决上一讲"IP 直连"暴露的痛点——启动 Nacos 注册中心,让"服务提供者注册、消费者发现",并让 Spring Boot 4 应用接入 Nacos,按服务名而不是 IP 调用。

    1、为什么要注册中心

    实例的 IP/端口会变(扩容、重启、迁移),不能写死在代码里。注册中心解决"服务在哪里":

    • 服务提供者启动时注册:把 服务名 + IP + 端口 上报给注册中心。
    • 消费者需要时发现:按服务名查到当前可用的实例列表,再挑一个调用。
    • 心跳续约:实例定期发心跳,宕机了注册中心就把它踢下线。

    nacos-discovery

    一句话:注册中心 = 服务的"通讯录"。服务上下线,通讯录自动增删。


    2、启动 Nacos(单机)

    1、下载解压

    cd /opt
    # 下载 Nacos(版本以 3.1.x 为准)
    sudo wget https://github.com/alibaba/nacos/releases/download/3.1.1/nacos-server-3.1.1.tar.gz
    sudo tar -zxvf nacos-server-3.1.1.tar.gz
    sudo mv nacos /usr/local/nacos
    

    2、必做:配置鉴权三件套(Nacos 3.x 强制)

    Nacos 3.x 起,鉴权相关的三项配置不允许留空,缺一个就启动失败,而且是逐项报错——补完一个会接着报下一个:

    # 第一次启动报:
    The initial key used to generate JWT tokens (the original string must be over 32 characters and Base64 encoded).
    `nacos.core.auth.plugin.nacos.token.secret.key` is missing, please set with Base64 string:
    
    # 补上密钥再启动,又报:
    `nacos.core.auth.server.identity.key` is missing, please set:
    

    所以一次性把三项都配好:

    配置项要求用途
    nacos.core.auth.plugin.nacos.token.secret.keyBase64 字符串,解码后原始长度 ≥ 32 字符签发 JWT 的密钥
    nacos.core.auth.server.identity.key非空、自定义服务端之间调用的身份标识
    nacos.core.auth.server.identity.value非空、自定义与上面配对使用
    cd /usr/local/nacos/conf
    
    # 生成三项随机值(token 需 Base64;identity 用十六进制即可,保证够长)
    KEY=$(openssl rand -base64 48 | tr -d '\n')
    IDK=$(openssl rand -hex 16)
    IDV=$(openssl rand -hex 16)
    
    # 先清掉可能存在的旧配置行,再一次性追加
    sudo sed -i '/^nacos.core.auth.plugin.nacos.token.secret.key=/d' application.properties
    sudo sed -i '/^nacos.core.auth.server.identity./d' application.properties
    {
      echo "nacos.core.auth.plugin.nacos.token.secret.key=$KEY"
      echo "nacos.core.auth.server.identity.key=$IDK"
      echo "nacos.core.auth.server.identity.value=$IDV"
    } | sudo tee -a application.properties
    
    # 确认:三行都要独立成行,且行首必须是 nacos(不能是 #)
    grep -n "^nacos.core.auth" application.properties
    

    三个坑(都有人踩过):

    • 别抄网上的示例密钥。网上流传的那些"Nacos 示例密钥"是公开值,复制过来等于没设。
    • 追加前先确认文件末尾有换行符。若最后一行没有换行,tee -a 会把新配置粘到上一行末尾;一旦上一行以 # 开头(如 #nacos.k8s.sync.kubeConfig=...),整行就变成注释——配置看着写进去了,实际完全不生效。上面用 grep -n "^nacos.core.auth"(^ 锚定行首)就是为了立刻发现这种情况。
    • 将来搭集群时,各节点的这三项必须完全一致,否则会报 403 invalid token。

    3、单机启动

    cd /usr/local/nacos
    # 目录属主是 root,用 sudo 启动,否则日志写不进去
    # ⚠️ 必须加 -m standalone!不加默认是集群模式,会去找外部 MySQL
    sudo sh bin/startup.sh -m standalone
    # 若报 "[[" 符号找不到,改用 bash 执行:sudo bash bin/startup.sh -m standalone
    # 查看日志确认启动成功
    tail -f /usr/local/nacos/logs/start.out
    

    为什么必须加 -m standalone:启动参数 nacos.standalone 的默认值是 false,即 Nacos 不加参数时默认按集群模式启动。集群模式需要外置 MySQL(读 db.num、db.url.0 等配置),单机学习环境没有这些配置,日志里就会抛:

    Caused by: java.lang.IllegalArgumentException: db.num is null
    

    加上 -m standalone 后走内置 Derby 数据库,不需要任何数据库配置。

    日志里出现 Nacos started successfully in stand alone mode 即为成功。

    启动后:

    • 控制台地址:http://localhost:8080/ (⚠️ Nacos 3.x 起控制台端口为 8080,且不再有 /nacos 前缀;8848/nacos 是 2.x 时代的地址)
    • 默认账号:nacos / nacos

    端口说明:8848 = HTTP API 端口;9848 = gRPC 端口(Nacos 2.x 起新增,防火墙需放行);8080 = 控制台端口(3.x 起独立)。

    与课程其它端口不冲突:网关用 80、user-service 8081、product-service 8082、order-service 8083、inventory-service 8084,Nacos 控制台独占 8080。

    生产环境 Nacos 需要集群部署。


    3、服务提供者接入 Nacos

    1、加依赖

    <!-- user-service/pom.xml 增加 -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    

    2、配置 Nacos 地址

    spring:
      application:
        name: user-service        # 服务名:注册/发现都靠它
      cloud:
        nacos:
          server-addr: 192.168.100.100:8848   # 注册中心地址(客户端通信端口,不是控制台的 8080)
          discovery:
            ip: 127.0.0.1               # 注册的 IP(生产用真实网卡 IP),就是给消费端访问的地址
    

    ⚠️ 端口别写错:是 8848,不是 8080

    server-addr 填的是 Nacos 的客户端通信端口 8848,写成控制台端口 8080 会导致服务启动直接失败:

    Caused by: com.alibaba.nacos.api.exception.NacosException:
        Client not connected, current status:STARTING
            at com.alibaba.nacos.common.remote.client.RpcClient.request(RpcClient.java:647)
    

    原因:客户端以 server-addr 的端口为主端口,并在其基础上 +1000 推导 gRPC 端口。写 8848 → 连 9848(正常);写 8080 → 去连 9080(根本没这个服务),连接建不起来(状态停在 STARTING),注册随之失败。上一条报错 Failed to start bean 'webServerStartStop' 也是这么被连锁触发的。

    记忆口诀:8848 给程序用,8080 给浏览器用。

    补充:Nacos 3.x 默认只开启控制台鉴权(浏览器登录 nacos/nacos),客户端鉴权 nacos.core.auth.enabled 默认是 false,所以 Spring Boot 这边暂时不需要配 username/password。


    3、写一个接口并启动

    @RestController
    public class UserController {
    
        @GetMapping("/user/{id}")
        public String getUser(@PathVariable Long id) {
            return "user-" + id + " @ user-service";
        }
    }
    

    启动应用,刷新 Nacos 控制台 服务管理 → 服务列表,可以看到 user-service 已注册。

    验证:curl http://localhost:8081/user/1 能返回结果;Nacos 里能看到实例(含 IP/端口/健康状态)。

    操作完成后,在 Nacos 管理控制台看到如下效果说明注册成功:

    image-20261014143736876


    4、服务消费者接入 Nacos

    再建一个 order-service,加同样的 nacos-discovery 依赖与 Nacos 配置,然后按服务名发现实例、用 RestTemplate 发起调用。

    // ① 先在 order-service 里准备一个 RestTemplate Bean(放配置类中)
    @Configuration
    public class RestTemplateConfig {
    
        @Bean
        public RestTemplate restTemplate() {
            return new RestTemplate();
        }
    }
    
    // ② 消费者按服务名发现实例,再用 RestTemplate 发起调用
    @RestController
    public class OrderController {
    
        @Autowired
        private DiscoveryClient discoveryClient;   // Spring Cloud 提供
    
        @Autowired
        private RestTemplate restTemplate;         // 用来发起 HTTP 调用
    
        @GetMapping("/order/call")
        public String callUser(@RequestParam Long id) {
            // 1. 按服务名发现所有实例
            List<ServiceInstance> list =
                    discoveryClient.getInstances("user-service");
            // 2. 取第一个实例的地址
            ServiceInstance ins = list.get(0);
            String url = "http://" + ins.getHost() + ":" + ins.getPort()
                    + "/user/" + id;
            // 3. 用 RestTemplate 发起 HTTP 调用:
            //    getForObject 会自动把响应体反序列化成 String 返回
            return restTemplate.getForObject(url, String.class);
        }
    }
    

    生产里不用手写 RestTemplate + 手工挑实例——第 05 讲 OpenFeign + 第 06 讲 LoadBalancer 会把这块全部自动化。这里先演示底层原理。


    5、健康检查与上下线

    概念说明
    心跳续约实例每 5 秒发一次心跳,默认 15 秒内没收到判为不健康
    主动下线服务正常关闭(kill/停机)时,Nacos 会及时摘除实例
    临时实例默认类型,宕机自动摘除(微服务常用)
    持久实例手动管理,适合特殊场景(本课程不展开)

    演示:停掉 user-service,刷新 Nacos,实例列表里 user-service 消失;再启动又出现。


    6、课堂练习

    1. 启动 Nacos,打开控制台(http://localhost:8080/,nacos/nacos 登录),确认能正常进入。
    2. 让 user-service 注册成功,在控制台确认实例健康。
    3. 让 order-service 用 DiscoveryClient 按服务名找到并调用 user-service 的接口。
    4. (挑战)把 user-service 用不同端口开两个实例(如 8081、8091),看控制台实例变两个,服务调用仍可用。

    7、本讲小结

    • Nacos = 服务注册中心,服务启动注册、消费者按名发现、心跳保证健康。
    • 接入三步:加 discovery 依赖 → 配 server-addr → 启动注册。
    • spring.application.name 就是服务名,注册与发现都靠它。
    • 手写发现+调用是"原理演示",真实项目用 Feign + LoadBalancer(后续两讲)。

    8、本节验证

    • 理解注册中心的作用与心跳续约机制 ✅
    • 会配置鉴权三件套(token 密钥 + identity)并启动 Nacos ✅
    • 知道启动必须加 -m standalone(不加默认走集群模式,会抛 db.num is null)✅
    • 知道 server-addr 要填客户端端口 8848(填成控制台端口 8080 会报 Client not connected)✅
    • 会打开 Nacos 3.x 控制台(8080,无 /nacos 前缀)✅
    • 会让服务提供者/消费者接入 Nacos(加依赖、配地址、注册)✅
    • 会查看健康状态与实例上下线 ✅
    end