【 Java微服务 】Spring Cloud Alibaba :Nacos 注册中心与配置中心全攻略(含服务发现、负载均衡与动态配置)

文章目录

1.版本管理以及构建规范

1.1版本管理

参考SpringClooud Alibaba:https://github.com/alibaba/spring-cloud-alibaba/blob/2023.x/README-zh.md

1.2如何构建

1.3本文案例演示版本

shell 复制代码
JDK:17
Nacos:2.4.3
SpringBoot:3.3.4
SpringCloud:2023.0.3
SpringCloud Alibaba:2023.0.3.2

2.Nacos快速开始

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service的⾸字⺟简称,⼀个更易于构

建云原⽣应⽤的动态服务发现、配置管理和服务管理平台。

2.1安装

Nacos快速开始地址:https://nacos.io/docs/next/quickstart/quick-start/

根据官网地址即可完成对于Nacos的安装

2.2启动

访问地址: http://localhost:8848/nacos

3.项目结构介绍

本文的样式项目是一个基于Maven的多模块项目,下图为本示例项目的项目结构:

2个服务:生产者服务和消费者服务

4.注册中心

3.1依赖引入

pom.xml文件

xml 复制代码
<!--       注册中心-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
    </dependencies>

3.2 生产者服务注册到Nacos

3.2.1 编写配置文件

application.yml

yaml 复制代码
spring:
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 # Nacos地址
  application:
    name: service-product # 服务名称

server:
  port: 9002 # web端口

3.2.2 开启Nacos发现注解

一般都是添加到启动类上

java 复制代码
package cn.varin.service.product;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@EnableDiscoveryClient
@SpringBootApplication
public class ServiceProductApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceProductApplication.class, args);
    }
}

3.3消费者服务注册到Nacos

3.3.1 编写配置文件

application.yml

yaml 复制代码
spring:
  application:
    name: service-consumer
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
server:
  port: 9001

3.3.2 开启Nacos发现注解

一般都是添加到启动类上

java 复制代码
package cn.varin;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@EnableDiscoveryClient
@SpringBootApplication
public class ServiceConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceConsumerApplication.class, args);
    }
}

3.4启动服务运行效果

5.建立消费者微服务集群

建立方式:采用同一套方法使用不同的启动端口配置,具体配置参考如下

  1. 点击Edit Configurations
  1. 选择Consumer服务并且点击复制按钮
  1. 复制完成后,点击复制服务的modify options选项并且勾选**add JVM options**
  1. 此时页面会多出来一个jvm option 输入框

填写内容:-DServer.port = 端口

  1. 多重复几次,并且将复制的服务一起启动,即可在Nacos注册中心看到多个实例

6.IDEA中services控制台开启

注意:有时候idea编辑器会自动提示是否开启Services面板管理微服务模块,就无需配置。

如果并没有的话,可以根据以下配置完成对于Services面板的开启

  1. 找到.idea目录中的workSpace.xml文件,并在文件中添加以下内容

注意:给配置需要添加在 标签中。

xml 复制代码
<component name="RunDashboard">
    <option name="configurationTypes">
      <set>
        <option value="SpringBootApplicationConfigurationType" />
      </set>
    </option>
  </component>
  1. 再重新启动各个服务就可以看到services面板了

7.注册中心-服务发现

SpringCloud 和SpringCloud Alibaba 都提供了服务发现的功能

7.1DisconveryClient

java 复制代码
package cn.varin.service.consumer.test;


import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.client.discovery.DiscoveryClient;

@SpringBootTest
@Slf4j
public class DiscoveryClientTest {
    @Resource
    private DiscoveryClient discoveryClient;
    @Test
    public void discover() {
        discoveryClient.getServices().forEach(serviceName -> {
            System.err.println(serviceName);
            log.info("discover serviceName={}", serviceName);
            discoveryClient.getInstances(serviceName).forEach(serviceInstance -> {
                log.info(serviceName+">>>"+serviceInstance.getHost()+":"+serviceInstance.getPort());
            });
        });

    }

}

运行代码后可以发现Nacos中的两个服务

7.2NacosDiscoveryClient和NacosServiceDiscovery

这两个类都可以获取到注册中心的服务

对比维度 NacosDiscoveryClient NacosServiceDiscovery
核心定位 Spring Cloud 标准服务发现客户端(API层) Nacos 专属的服务发现底层实现(服务层)
实现接口 实现 Spring Cloud 的 DiscoveryClient 接口 无统一接口,是 Nacos 自定义的服务发现实现类
设计目的 遵循 Spring Cloud 规范,给开发者提供统一API 封装 Nacos Server 交互逻辑,给上层API提供支撑
使用场景 开发者日常开发(获取服务实例、自身服务信息) 框架内部依赖、高级定制(如自定义订阅、多命名空间精细控制)
依赖关系 依赖 NacosServiceDiscovery 实现核心功能 依赖 Nacos 原生 SDK(nacos-client
API风格 Spring Cloud 标准化方法(简洁、通用) Nacos 专属方法(细粒度、强针对性)
是否暴露给开发者 是(官方推荐直接使用) 否(默认内部使用,可手动注入定制)
java 复制代码
package cn.varin.service.consumer.test;

import com.alibaba.cloud.nacos.discovery.NacosDiscoveryClient;
import com.alibaba.cloud.nacos.discovery.NacosServiceDiscovery;
import com.alibaba.nacos.api.exception.NacosException;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;

import java.util.List;

@SpringBootTest
@Slf4j
public class NacosDiscoveryClientTest {
    @Resource
    private NacosDiscoveryClient nacosDiscoveryClient;
    @Test
    public void NacosDiscoveryClientTest() {
        // 获取注册中心所有的服务
        List<String> services = nacosDiscoveryClient.getServices();
        services.forEach(System.out::println);
        log.info("======================================");
        // 根据服务名称获取到注册中心的集群服务
        List<ServiceInstance> instances = nacosDiscoveryClient.getInstances("service-consumer");
        instances.forEach(System.out::println);
    }
    @Resource
    private NacosServiceDiscovery nacosServiceDiscovery;
    @Test
    public void NacosServiceDiscoveryTest() {
        try {
            nacosServiceDiscovery.getServices().forEach(servierName->{
                try {
                    nacosServiceDiscovery.getInstances(servierName).forEach(
                            instance->{
                                log.info(servierName+">>>"+instance.getHost()+":"+instance.getPort());

                            }
                    );
                } catch (NacosException e) {
                    throw new RuntimeException(e);
                }
            });
        } catch (NacosException e) {
            throw new RuntimeException(e);
        }
    }
}

运行结果:

8.远程调用

使用RestTemplate

可调用本项目中的其他模块接口

也可调用第三方服务API

缺点:当远程服务器宕机,则服务不可用

  • 测试代码
java 复制代码
package cn.varin.service.consumer.test;

import com.alibaba.cloud.nacos.discovery.NacosDiscoveryClient;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.web.client.RestTemplate;

@SpringBootTest
public class RestTemplateTest {
    @Resource
    private RestTemplate restTemplate;

    @Test
    public void test() {
        RestTemplate template = new RestTemplate();
        // 第三方接口调用
        String baiduApi = template.getForObject("http://www.baidu.com", String.class);
        System.out.println(baiduApi);

        // 调用product模块接口
        String productAPi = template.getForObject("http://localhost:9101/health", String.class);
        System.out.println(productAPi);

    }
}
  • 测试结果

9.负载均衡

解决问题:可解决微服务模块某一台服务宕机后,若存在集群则可自动切换。

9.1 依赖导入

xml 复制代码
<dependency>
 <groupId>org.springframework.cloud</groupId>
 <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

9.2 LoadBalancerClient

使用LoadBalancerClient类实现接口发现负载均衡

  • 测试代码
java 复制代码
package cn.varin.service.consumer.test;

import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.client.RestTemplate;

@SpringBootTest
public class LoadBalancerTest {
    @Resource
    private LoadBalancerClient loadBalancerClient;

    @Test
    public void LoadBalancerClientTest() {
        ServiceInstance choose = loadBalancerClient.choose("service-product");
        if (choose == null) {
            throw new RuntimeException("未找到 service-product 实例");
        }
      //   String uri= "http://127.0.0.1:"+choose.getPort()+"/health";
       String uri= "http://"+choose.getHost()+":"+choose.getPort()+"/health";
        System.out.println(uri);
        RestTemplate template = new RestTemplate();
        String forObject = template.getForObject(uri, String.class);
        System.out.println(forObject);


    }
}
  • 测试结果

9.3 @LoadBalanced注解

  1. 直接在注册RestTemplate时,给它添加上@LoadBalanced
  2. uri就可以写成:http://微服务名称/health

具体步骤:

  • 添加注解
java 复制代码
package cn.varin.service.consumer.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

@Configuration
public class NacosConfig {
    @Bean
    @LoadBalanced // 实现负载均衡
    public RestTemplate restTemplate() {
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setConnectTimeout(30000);
        factory.setReadTimeout(30000);

        return new RestTemplate(factory);
    }
}
  • 测试
java 复制代码
package cn.varin.service.consumer.test;

import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.client.RestTemplate;

@SpringBootTest
public class LoadBalancerTest {
   
    @Autowired
    private RestTemplate restTemplate;

   
    @Test

    public void loadBalancedTest() {
        String uri = "http://service-product/health";
        String forObject = restTemplate.getForObject(uri, String.class);
        System.out.println(forObject);

    }
}
  • 效果

9.4.面试题

  1. 面试题:如果注册中心宕机了,远程调用还会成功吗?
  • 从未调⽤过,如果宕机,调⽤会⽴即失败
  • 调⽤过,如果宕机,因为会缓存名单,调⽤会成功
  • 调⽤过,如果注册中⼼和对⽅服务宕机,因为会缓存名单,调⽤会阻塞后失败(Connection

Refused)

核心结论:可能成功,但取决于服务消费者是否已缓存服务提供者的地址,且服务提供者状态正常。

关键原理:Nacos的服务发现与本地缓存机制

Nacos作为注册中心,核心是提供「服务地址注册与查询」能力,而远程调用的核心是「获取服务地址后建立网络连接」,两者独立解耦:

  1. 服务发现流程
    • 服务提供者启动时,向Nacos注册自身地址(IP:端口)、元数据等信息;
    • 服务消费者启动时,向Nacos查询目标服务的地址列表,并将地址缓存到本地内存(默认无持久化,部分版本支持本地文件缓存);
    • 消费者后续调用时,优先从本地缓存获取地址,无需每次查询Nacos。
  2. 注册中心宕机后的影响边界
    • 宕机后,新启动的消费者无法查询服务地址,调用直接失败;
    • 已启动且缓存了地址的消费者,只要缓存未失效、服务提供者正常运行,就能通过缓存地址完成调用。

补充细节

  1. 缓存有效期 :Nacos客户端默认会定期(默认30秒)向注册中心刷新服务列表(主动拉取),宕机后刷新失败,缓存不会主动失效,直到:
    • 消费者重启(内存缓存丢失);
    • 服务提供者下线(但注册中心宕机后,提供者下线状态无法同步给消费者,可能出现"缓存地址已失效"的调用失败)。
  2. 高可用优化
    • 生产环境中Nacos需部署集群(至少3节点),避免单点宕机;
    • 消费者可开启本地文件缓存(spring.cloud.nacos.discovery.naming-cache-persist=true),重启后仍能复用缓存地址;
    • 结合负载均衡组件(如Ribbon、Spring Cloud LoadBalancer),即使部分提供者下线,消费者也能通过缓存中的其他地址重试。

10.配置中心

10.1 依赖导入

xml 复制代码
<!--        配置中心-->
<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

10.2 配置文件

consumer模块实例

yaml 复制代码
spring:
  application:
    name: service-consumer
  cloud:
    nacos:
      config:

        server-addr: 127.0.0.1:8848
        file-extension: yaml
      discovery:
        server-addr: 127.0.0.1:8848
        service: ${spring.application.name}
  config:
    # 完整写法:data-id + group + namespace + 扩展参数
   #  import: nacos:${data-id}:${group}:${namespace}?${参数1}&${参数2}
    import: nacos:service-consumer
server:
  port: 9001

各字段含义

10.3 Nacos平台配置

service-consumer

10.4 验证配置是否生效

在代码中通过 @Value 或 @ConfigurationProperties 注入配置,验证是否能获取到 Nacos 中的值

本文就展示:ConfigurationProperties

java 复制代码
package cn.varin.service.consumer.pojo;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Data
@Component
@ConfigurationProperties(prefix = "consumer")
public class PropertiesData {
    private String name;
}
java 复制代码
package cn.varin.service.consumer.controller;

import cn.varin.service.consumer.pojo.PropertiesData;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/properties")
public class PropertiesController {

    @Resource
    private PropertiesData propertiesData;
    @GetMapping("/getData")
    public String getData() {
        System.out.println(propertiesData.getName());
        System.out.println(propertiesData.toString());
        return propertiesData.getName();
    }

}

访问路径:http://localhost:9001/properties/getData

10.5 导入多个 Nacos 配置集(多环境 / 多业务配置)

如果需要导入多个配置文件(如公共配置 + 业务配置),用数组形式指定:

yaml

yaml 复制代码
spring:
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yaml
  config:
    import:
      - nacos:common-prod.yaml:common-group:prod-namespace-id  # 公共配置(如数据库、Redis 地址)
      - nacos:user-service-prod.yaml:user-group:prod-namespace-id  # 业务配置(用户服务专属配置)

11.注册中心和配置中心总结

相关推荐
Spring AI学习5 小时前
Spring AI深度解析(9/50):可观测性与监控体系实战
java·人工智能·spring
java1234_小锋6 小时前
Spring IoC的实现机制是什么?
java·后端·spring
生骨大头菜6 小时前
使用python实现相似图片搜索功能,并接入springcloud
开发语言·python·spring cloud·微服务
xqqxqxxq6 小时前
背单词软件技术笔记(V2.0扩展版)
java·笔记·python
消失的旧时光-19437 小时前
深入理解 Java 线程池(二):ThreadPoolExecutor 执行流程 + 运行状态 + ctl 原理全解析
java·开发语言
哈哈老师啊7 小时前
Springboot学生综合测评系统hxtne(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
java·数据库·spring boot
4311媒体网7 小时前
帝国cms调用文章内容 二开基本操作
java·开发语言·php
zwxu_7 小时前
Nginx NIO对比Java NIO
java·nginx·nio
可观测性用观测云8 小时前
Pyroscope Java 接入最佳实践
java
气π9 小时前
【JavaWeb】——(若依 + AI)-基础学习笔记
java·spring boot·笔记·学习·java-ee·mybatis·ruoyi