LOADING

加载过慢请开启缓存 浏览器默认开启

Nacos笔记

Nacos注册中心实战笔记:从入门到精通

在微服务架构中,服务注册与发现是基础核心组件。Nacos作为阿里巴巴开源的服务发现、配置管理和服务管理平台,已经成为Spring Cloud生态中的重要成员。本文将详细介绍Nacos的使用方法,帮助你快速掌握这一强大的服务治理工具。

什么是Nacos?

Nacos(Dynamic Naming and Configuration Service)是阿里巴巴开源的一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。它提供了一组简单易用的特性,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。

Nacos的核心功能:

  • 服务发现与服务健康监测
  • 动态配置管理
  • 动态DNS服务
  • 服务及其元数据管理
  • 负载均衡

Nacos vs Eureka vs Consul

在选择微服务注册中心时,我们通常会考虑Nacos、Eureka和Consul这三种主流方案。下面是它们的简要对比:

特性 Nacos Eureka Consul
一致性协议 CP+AP AP CP
健康检查 TCP/HTTP/MYSQL/Client Beat Client Beat TCP/HTTP/gRPC/Cmd
负载均衡 权重/metadata/Selector Ribbon Fabio
雪崩保护
自动注销实例 支持 支持,但实例需要等待3个心跳周期才能被清除 支持
访问协议 HTTP/DNS/RPC HTTP HTTP/DNS
监听支持 支持 支持 支持
多数据中心 支持 支持 支持
跨注册中心 支持 不支持 支持
SpringCloud集成 支持 支持 支持
Dubbo集成 支持 不支持 不支持
K8S集成 支持 不支持 支持

Nacos的优势在于:

  • 同时支持CP和AP模式切换
  • 支持服务注册与发现和配置管理
  • 支持多语言客户端
  • 支持Dubbo和Spring Cloud生态
  • 界面友好,操作简单

安装与启动Nacos Server

下载安装

  1. Nacos GitHub Release下载最新稳定版
  2. 解压到指定目录,如D:\nacos

单机模式启动

进入bin目录,执行以下命令启动Nacos:

Windows:

startup.cmd -m standalone

Linux/Mac:

sh startup.sh -m standalone

访问控制台

启动成功后,通过浏览器访问:http://localhost:8848/nacos
默认账号密码:nacos/nacos

Docker方式启动

docker run --name nacos-standalone -e MODE=standalone -p 8848:8848 -d nacos/nacos-server:latest

Nacos集群部署

在生产环境,为保证高可用,通常需要部署Nacos集群。以下是简要步骤:

  1. 准备3台及以上服务器

  2. 配置cluster.conf
    conf目录下创建cluster.conf文件,添加以下内容:

       192.168.16.101:8848
       192.168.16.102:8848
       192.168.16.103:8848
  3. 配置数据库
    默认情况下,Nacos使用内嵌数据库Derby存储数据,集群模式下需要使用MySQL。

    • 创建数据库,执行conf/nacos-mysql.sql脚本
    • 修改conf/application.properties文件:
           spring.datasource.platform=mysql
           db.num=1
           db.url.0=jdbc:mysql://localhost:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
           db.user=root
           db.password=password
  4. 启动集群
    在每台服务器上启动Nacos:

       sh startup.sh

Spring Cloud集成Nacos

1. 添加依赖

pom.xml中添加依赖:

<dependencies>
    <!-- Spring Cloud Alibaba Nacos Discovery -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    
    <!-- Spring Cloud Alibaba Nacos Config (如需配置中心) -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>
</dependencies>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>2021.0.1.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

2. 配置服务注册

application.yml中配置:

spring:
  application:
    name: service-provider
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        # 以下是可选配置
        namespace: public # 命名空间ID
        group: DEFAULT_GROUP # 分组
        cluster-name: DEFAULT # 集群名称
        weight: 1 # 权重
        metadata:
          version: v1 # 版本
          env: dev # 环境

3. 启用服务注册与发现

在启动类上添加注解:

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

4. 服务发现与负载均衡

使用@LoadBalanced注解,整合RestTemplate实现负载均衡:

@Configuration
public class RestConfig {
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

使用示例:

@RestController
public class TestController {
    @Autowired
    private RestTemplate restTemplate;
    
    @GetMapping("/test")
    public String test() {
        // 直接使用服务名调用
        return restTemplate.getForObject("http://service-provider/api/hello", String.class);
    }
}

Nacos作为配置中心

Nacos不仅可以作为注册中心,还能作为配置中心使用。

1. 添加配置中心依赖

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

2. 创建bootstrap.yml

从Spring Cloud 2020版本开始,bootstrap.yml不会自动加载,需要添加依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>

然后创建bootstrap.yml

spring:
  application:
    name: service-provider
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yaml # 配置文件格式
        # 以下是可选配置
        namespace: public # 命名空间ID
        group: DEFAULT_GROUP # 分组
        refresh-enabled: true # 是否开启自动刷新
        # 扩展配置
        extension-configs:
          - data-id: common-mysql.yaml # 配置文件名
            group: COMMON_GROUP # 所在分组
            refresh: true # 是否动态刷新
          - data-id: common-redis.yaml
            group: COMMON_GROUP
            refresh: true

3. 在Nacos控制台添加配置

  1. 登录Nacos控制台
  2. 进入”配置管理” -> “配置列表”
  3. 点击”+”按钮添加配置
    • Data ID: service-provider.yaml (与spring.application.name和file-extension组合)
    • Group: DEFAULT_GROUP
    • 配置内容:
           server:
             port: 8081
           
           user:
             name: nacos-user
             age: 18

4. 读取配置

使用@RefreshScope注解实现配置自动刷新:

@RestController
@RequestMapping("/config")
@RefreshScope
public class ConfigController {

    @Value("${user.name}")
    private String name;
    
    @Value("${user.age}")
    private Integer age;
    
    @GetMapping("/info")
    public Map<String, Object> getConfig() {
        Map<String, Object> map = new HashMap<>();
        map.put("name", name);
        map.put("age", age);
        return map;
    }
}

也可以使用@ConfigurationProperties绑定配置:

@Component
@ConfigurationProperties(prefix = "user")
@RefreshScope
@Data
public class UserConfig {
    private String name;
    private Integer age;
}

Nacos高级特性

1. 命名空间和分组

Nacos提供了命名空间(Namespace)和分组(Group)的概念,用于隔离不同环境或业务线的配置和服务。

  • Namespace:用于环境隔离,如开发、测试、生产环境
  • Group:用于业务隔离,如不同的业务模块

创建新的命名空间:

  1. 在Nacos控制台点击”命名空间”
  2. 点击”新建命名空间”,填写ID、名称和描述
  3. 点击”确定”

在配置中指定命名空间和分组:

spring:
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        namespace: dev # 命名空间ID,非名称
        group: SHOPPING_SERVICE # 分组名称
      config:
        server-addr: 127.0.0.1:8848
        namespace: dev
        group: SHOPPING_SERVICE

2. 服务权重和保护阈值

Nacos支持设置服务实例的权重,用于控制负载均衡比例:

spring:
  cloud:
    nacos:
      discovery:
        weight: 2 # 权重值,默认为1

保护阈值是一个0到1之间的浮点数:

  • 当健康实例占总实例的比例小于保护阈值时,无论实例是否健康,都会将其返回给客户端,这样可以保证服务的可用性
  • 在Nacos控制台可以为每个服务设置保护阈值

3. 元数据管理

可以为服务添加自定义元数据,用于服务治理:

spring:
  cloud:
    nacos:
      discovery:
        metadata:
          version: v1
          env: dev
          region: cn-shanghai

4. Nacos的数据模型

Nacos的数据模型由三元组(Namespace, Group, Service/DataId)组成,提供了高度的配置和服务隔离能力:

  • Namespace:租户/环境隔离
  • Group:分组隔离
  • Service/DataId:服务/配置标识

5. 配置加密与安全

对于敏感配置,可以结合Jasypt等工具进行加密:

<dependency>
    <groupId>com.github.ulisesbocchio</groupId>
    <artifactId>jasypt-spring-boot-starter</artifactId>
    <version>3.0.4</version>
</dependency>

配置示例:

jasypt:
  encryptor:
    password: your-secret-key

spring:
  datasource:
    password: ENC(encrypted-password)

Nacos监控与管理

1. Nacos自带监控

Nacos自带的控制台提供了基本的监控功能:

  • 服务列表与健康状态
  • 服务实例详情
  • 配置管理与版本历史

2. 对接Prometheus和Grafana

Nacos可以通过Actuator暴露监控指标,结合Prometheus和Grafana实现更强大的监控:

添加依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

配置Actuator:

management:
  endpoints:
    web:
      exposure:
        include: '*'
  metrics:
    export:
      prometheus:
        enabled: true

然后配置Prometheus抓取这些指标,并在Grafana中创建仪表盘进行可视化。

实战案例:商品服务与订单服务

以下是一个实际案例,演示如何使用Nacos实现服务注册发现和配置管理:

1. 公共模块 (common-api)

// 商品实体
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Product {
    private Long id;
    private String name;
    private BigDecimal price;
    private Integer stock;
}

// 订单实体
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order {
    private Long id;
    private Long productId;
    private Integer quantity;
    private BigDecimal amount;
    private String address;
    private Date createTime;
}

2. 商品服务 (product-service)

// 启动类
@SpringBootApplication
@EnableDiscoveryClient
public class ProductServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProductServiceApplication.class, args);
    }
}

// 控制器
@RestController
@RequestMapping("/api/products")
public class ProductController {
    
    private static final Map<Long, Product> PRODUCT_DB = new ConcurrentHashMap<>();
    
    static {
        PRODUCT_DB.put(1L, new Product(1L, "iPhone 13", new BigDecimal("5999"), 100));
        PRODUCT_DB.put(2L, new Product(2L, "MacBook Pro", new BigDecimal("12999"), 50));
        PRODUCT_DB.put(3L, new Product(3L, "iPad Pro", new BigDecimal("5999"), 80));
    }
    
    @GetMapping("/{id}")
    public Product getProductById(@PathVariable Long id) {
        return PRODUCT_DB.get(id);
    }
    
    @GetMapping
    public List<Product> getAllProducts() {
        return new ArrayList<>(PRODUCT_DB.values());
    }
    
    @PutMapping("/{id}/stock")
    public Product updateStock(@PathVariable Long id, @RequestParam Integer quantity) {
        Product product = PRODUCT_DB.get(id);
        if (product == null) {
            throw new RuntimeException("Product not found");
        }
        
        if (product.getStock() < quantity) {
            throw new RuntimeException("Insufficient stock");
        }
        
        product.setStock(product.getStock() - quantity);
        return product;
    }
}

3. 订单服务 (order-service)

// 启动类
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}

// Feign客户端
@FeignClient(name = "product-service")
public interface ProductClient {
    
    @GetMapping("/api/products/{id}")
    Product getProductById(@PathVariable("id") Long id);
    
    @PutMapping("/api/products/{id}/stock")
    Product updateStock(@PathVariable("id") Long id, @RequestParam("quantity") Integer quantity);
}

// 配置类(从Nacos读取配置)
@Configuration
@RefreshScope
@ConfigurationProperties(prefix = "order")
@Data
public class OrderConfig {
    private BigDecimal taxRate;
    private BigDecimal discountRate;
    private boolean enableExpress;
}

// 控制器
@RestController
@RequestMapping("/api/orders")
public class OrderController {
    
    @Autowired
    private ProductClient productClient;
    
    @Autowired
    private OrderConfig orderConfig;
    
    private AtomicLong orderIdGenerator = new AtomicLong(1);
    
    @PostMapping
    public Order createOrder(@RequestBody CreateOrderRequest request) {
        // 1. 查询商品信息
        Product product = productClient.getProductById(request.getProductId());
        if (product == null) {
            throw new RuntimeException("Product not found");
        }
        
        // 2. 计算订单金额(考虑税率和折扣)
        BigDecimal amount = product.getPrice()
            .multiply(new BigDecimal(request.getQuantity()))
            .multiply(BigDecimal.ONE.subtract(orderConfig.getDiscountRate()))
            .multiply(BigDecimal.ONE.add(orderConfig.getTaxRate()));
        
        // 3. 扣减库存
        productClient.updateStock(product.getId(), request.getQuantity());
        
        // 4. 创建订单
        Order order = new Order();
        order.setId(orderIdGenerator.getAndIncrement());
        order.setProductId(request.getProductId());
        order.setQuantity(request.getQuantity());
        order.setAmount(amount);
        order.setAddress(request.getAddress());
        order.setCreateTime(new Date());
        
        return order;
    }
}

Nacos配置示例(order-service.yaml):

order:
  tax-rate: 0.06
  discount-rate: 0.1
  enable-express: true

常见问题与解决方案

1. 服务注册失败

问题:服务无法注册到Nacos
解决方案:

  • 检查Nacos服务器是否正常运行
  • 确认服务的配置(server-addr, namespace, group)是否正确
  • 检查网络连接,确保可以访问Nacos服务
  • 查看应用日志,分析具体错误信息

2. 配置无法更新

问题:修改了Nacos配置,但服务未生效
解决方案:

  • 确认配置项是否使用了@RefreshScope注解
  • 检查配置的DataId、Group是否正确
  • 验证命名空间是否匹配
  • 检查refresh-enabled是否设置为true
  • 调用/actuator/refresh端点手动刷新

3. 服务发现延迟

问题:新注册的服务需要等待较长时间才能被发现
解决方案:

  • 调整客户端的缓存刷新时间
  • 检查健康检查间隔时间配置
  • 优化集群网络环境

4. 集群同步问题

问题:Nacos集群节点数据不同步
解决方案:

  • 确保集群配置正确
  • 检查MySQL数据库连接是否正常
  • 验证网络连通性
  • 检查JDK版本兼容性

总结

Nacos作为一站式服务发现、配置管理和服务治理平台,为微服务架构提供了强大的基础支持。

参考资料