Spring Cloud Alibaba — Nacos

01、简介

Nacos 官网:https://nacos.io/zh-cn/index.html
Nacos 是阿里巴巴的产品,现在是 SpringCloud 中的一个组件。相比 Eureka 功能更加丰富,在国内受欢迎程度较高。
Nacos 致力于帮助您发现、配置和管理微服务。
Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。
Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。
Nacos 是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。
Nacos 的关键特性包括:服务发现和服务健康监测动态配置服务动态 DNS 服务服务及其元数据管理

02、安装

2.1、下载解压

GitHub 下载地址:https://github.com/alibaba/nacos/releases
请添加图片描述
Nacos 是绿色版软件,解压即安装。下载完毕后将 ZIP 包解压到指定非中文目录即可。

目录说明:bin 启动脚本、conf 配置文件。
请添加图片描述

2.2、运行登录

在 Nacos 的 bin 目录中打开命令行终端,以单机运行方式启动 Nacos 服务:startup.cmd -m standalone
请添加图片描述登录地址:http://localhost:8848/nacos/index.html#/login
账号密码:nacos / nacos

2.3、快捷方式

在 Nacos 的 bin 目录中创建一个 start-nacos.bat 文件,将以下内容复制到该文件中。
然后双击该文件就可以直接启动 Nacos 了,可以将该文件发送一个快捷方式到桌面以便操作。

cd .
startup.cmd -m standalone
@pause

03、项目准备

创建 Maven 父工程:spring-cloud-alibaba-nacos ,删除其它文件只留一个 pom.xml 文件,导入依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.study</groupId>
    <artifactId>spring-cloud</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.9.RELEASE</version>
        <relativePath/>
    </parent>

    <properties>
        <spring-cloud-alibaba.version>2.2.5.RELEASE</spring-cloud-alibaba.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <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>

    <dependencies>
        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!-- web开发 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- nacos服务注册 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
    </dependencies>
</project>

3.1、服务消费者

创建 Maven 子工程:order-service

src/main/java/com/study/bean/Order.java

// 订单类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Order {
    private Long id;
    private Long price;
    private String name;
    private Integer num;
    private Long userId;
    private User user;
}

src/main/java/com/study/bean/User.java

// 用户类
@Data
public class User {
    private Long id;
    private String username;
    private String address;
}

src/main/java/com/study/controller/OrderController.java

// 订单控制器
@Slf4j
@RestController
@RequestMapping("/order")
public class OrderController {
    
    @Autowired
    private RestTemplate restTemplate;

    // 根据ID获取订单信息
    @GetMapping("/{id}")
    public Order getById(@PathVariable("id") Long id) {
        // 根据ID获取订单信息
        Order order = new Order(id, 9999L, "HUAWEI P" + id, Math.toIntExact(id), id, null);
        // 根据订单中的用户ID查询用户并返回
        String url = "http://user-service/user/" + order.getUserId();
        User user = restTemplate.getForObject(url, User.class);
        // 存入用户信息
        order.setUser(user);
        System.out.println("order = " + order);
        return order;
    }
}

src/main/java/com/study/OrderServiceApplication.java

@SpringBootApplication
public class OrderServiceApplication {

    @Bean
    @LoadBalanced // 负载均衡
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }

}

src/main/resources/application.yml

server:
  port: 9001

spring:
  application:
    name: order-service #服务名称
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #服务地址

3.2、服务提供者

创建 Maven 子工程:user-service

src/main/java/com/study/bean/User.java

// 用户类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Long id;
    private String username;
    private String address;
}

src/main/java/com/study/controller/UserController.java

// 用户控制器
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {

    // 根据ID获取用户信息
    @GetMapping("/{id}")
    public User getById(@PathVariable("id") Long id) {
        User user = new User(id, "张三" + id, "杭州西湖");
        System.out.println("user = " + user);
        return user;
    }
}

src/main/java/com/study/UserServiceApplication.java

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

src/main/resources/application.yml

server:
  port: 9002

spring:
  application:
    name: user-service #服务名称
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #服务地址

04、注册中心

服务提供者:服务的被调用方(为其它服务提供服务的服务)。

服务消费者:服务的调用方(依赖其它服务的服务)。
请添加图片描述

4.1、注册服务

请添加图片描述
启动服务消费者与服务提供者:UserServiceApplication、OrderServiceApplication。查看 Nacos 控制台:
请添加图片描述
查询订单:http://localhost:9001/order/1

{
    "id": 1,
    "price": 9999,
    "name": "HUAWEI P1",
    "num": 1,
    "userId": 1,
    "user": {
        "id": 1,
        "username": "张三1",
        "address": "杭州西湖"
    }
}

4.2、服务分级

一个服务可以有多个实例,例如 user-service 可以有:127.0.0.1:8081、127.0.0.1:8082、127.0.0.1:8083 …

假如这些实例分布于全国各地的不同机房,例如:127.0.0.1:8081 和 127.0.0.1:8082 在杭州机房、127.0.0.1:8083 在上海机房 …

Nacos 将同一机房内的实例划分为一个集群。也就是说 user-service 是服务,一个服务可以包含多个集群,如杭州、上海,每个集群下可以有多个实例,形成分级模型如图:
请添加图片描述

4.3、配置集群

修改 user-service 的 application.yml 文件,添加集群配置:

spring:
  cloud:
    nacos:
      discovery:
        cluster-name: Hangzhou #集群名称,默认DEFAULT

重启 user-service 服务,查看 Nacos 控制台:
请添加图片描述
请添加图片描述
再添加两个 user-service 服务,一个集群为杭州,一个集群为上海:
请添加图片描述
请添加图片描述
Name:UserServiceApplication-9003-Hangzhou
VM options:-Dserver.port=9003 -Dspring.cloud.nacos.discovery.cluster-name=Hangzhou
请添加图片描述
第二个添加操作同上:
Name:UserServiceApplication-9004-Shanghai
VM options:-Dserver.port=9004 -Dspring.cloud.nacos.discovery.cluster-name=Shanghai

再看 Nacos 控制台,user-service 服务杭州集群多了一个端口为 9003 的实例,并新增了一个上海集群及其实例。
请添加图片描述

4.4、负载均衡

此时清空三个用户服务的控制台日志,然后查询三次订单。
再看三个用户服务控制台,发现每个服务都访问了一次。这是因为订单服务启动类中:

@SpringBootApplication
@MapperScan("com.study.mapper")
public class OrderServiceApplication {
    @Bean
    @LoadBalanced // 负载均衡
    public RestTemplate restTemplate() { // RestTemplate模板提供便捷的方式来访问远程HTTP服务
        return new RestTemplate();
    }
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }

}

@LoadBalanced 默认的实现是 ZoneAvoidanceRule ,是一种轮询方案(一种内置负载均衡规则类),以区域可用的服务器为基础进行服务器的选择。使用 Zone 对服务器进行分类,这个 Zone 可以理解为一个机房、机架等,而后再对 Zone 内的多个服务实例做轮询。

同集群(同城市)中的实例互相访问的速度比跨集群(跨城市)访问的速度相对来说要快,而默认的这个并不能实现根据同集群优先来实现负载均衡。因此 Nacos中提供了一个 NacosRule 的实现,可以优先从同集群中挑选实例。

修改 order-service 的 application.yml 文件,添加集群配置和负载均衡规则:

spring:
  cloud:
    nacos:
      discovery:
        cluster-name: Hangzhou #集群名称
        
user-service:
  ribbon:
    NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule #nacos负载均衡规则 

重启 order-service 服务,清空三个用户服务的控制台日志,然后再查询四次订单。
发现两个杭州用户服务分别轮询了两次,而上海用户服务则没有被轮询到。

4.5、权重配置

在实际部署中会出现服务器设备性能有差异的场景,部分实例所在机器性能较好,另一些较差,正常来说肯定希望性能好的机器承担更多的用户请求。

但默认情况下 NacosRule 是同集群内随机挑选,不会考虑机器的性能问题。因此 Nacos 提供了权重配置来控制访问频率,权重值在 0~1 之间,权重越大则访问频率越高,权重为 0 则该实例永远不会被访问。

在 Nacos 控制台,找到 user-service 服务的实例列表,点击编辑即可修改权重:
请添加图片描述请添加图片描述
此时大概查询订单十次左右,相对来说才会轮询到该实例一次。

在服务升级的时候,有一种较好的方案就是可以通过调整权重来进行平滑升级。例如先把 user-service 的实例1 权重调节为 0,让用户访问逐渐流向其它实例,升级实例1后,再把权重从 0 调到 0.1,放进少数用户来测试有无问题,如果没问题就逐渐调大权重比例,此时用户是无感知的,起到平滑升级的效果。

4.6、环境隔离

Nacos 中服务存储和数据存储的最外层都是一个名为命名空间的东西,用来来实现环境隔离功能。

  • Nacos 中可以有多个命名空间。
  • 命名空间下可以有 group、service 等。
  • 不同命名空间之间相互隔离,例如不同命名空间的服务互相不可见。

请添加图片描述
新建命名空间:
请添加图片描述

修改 order-service 的 application.yml 文件,添加命名空间配置:

spring:
  cloud:
    nacos:
      discovery:
        namespace: dev #填入新建命名空间时,自定义的命名空间ID或自动生成的ID

重启 order-service 服务,查看 Nacos 控制台的服务列表:
请添加图片描述

此时再查询订单会报错,控制台提示:java.lang.IllegalStateException: No instances available for user-service 找不到用户服务。因为不同命名空间的服务互相不可见。然后把命名空间配置注释掉重启服务,以便后续操作。

4.7、实例类型

Nacos 的服务实例分为两种类型:

  • 临时实例:如果实例宕机超过一定时间,会从服务列表剔除,默认类型。
  • 非临时实例:如果实例宕机,不会从服务列表剔除,也可以叫永久实例。

临时实例:
请添加图片描述
修改 order-service 的 application.yml 文件,添加永久实例配置:

spring:
  cloud:
    nacos:
      discovery:
        ephemeral: false # 设置为非临时实例

请添加图片描述

05、配置管理

当微服务部署的实例越来越多,达到数十、数百时,逐个修改微服务配置就会让人抓狂,而且很容易出错。
此时需要一种统一配置管理方案,可以集中管理所有实例的配置,并且可以在配置变更时,及时通知微服务,实现配置的热更新。
请添加图片描述

5.1、添加配置

user-service 服务添加配置:
请添加图片描述
请添加图片描述
Data ID 格式:[服务名称]-[环境].[后缀名]
请添加图片描述

注意:项目的核心配置,需要热更新的配置才有放到 Nacos 管理的必要。基本不会变更的一些配置还是保存在微服务本地比较好。

5.2、拉取配置

微服务要拉取 Nacos 中管理的配置,并且与本地的 application.yml 配置合并,才能完成项目启动。
因此 Spring 引入了一种新的配置文件:bootstrap.yml,它会在 application.yml 之前被读取。

user-service 服务添加 bootstrap.yml 文件,并将 application.yml 文件中 Nacos 相关配置提取到 bootstrap.yml 文件。

父工程导入依赖:

<!-- nacos配置管理-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

bootstrap.yml:

spring:
  application:
    name: user-service #服务名称
  profiles:
    active: dev #环境名称
  cloud:
    nacos:
      server-addr:
      discovery:
        server-addr: localhost:8848 #服务地址
        cluster-name: Hangzhou #集群名称
      config:
        server-addr: localhost:8848 #服务地址
        file-extension: yaml #文件后缀名

application.yml:

server:
  port: 9002

#spring:
#  application:
#    name: user-service #服务名称
#  cloud:
#    nacos:
#      discovery:
#        server-addr: localhost:8848 #服务地址
#        cluster-name: Hangzhou #集群名称

UserController 中添加内容用以读取配置:

@Value("${pattern.dateformat}")
private String dateformat;

@RequestMapping("/now")
public String now() {
    return new SimpleDateFormat(dateformat).format(new Date());
}

重启所有 user-service 服务,访问 http://localhost:9002/user/now ,http://localhost:9003/user/now 均返回 2022-05-07 14:54:30 。

5.3、配置热更新

Nacos 的配置文件变更后,微服务无需重启即可感知。通过以下两种配置实现:

方式1:在 @Value 注入的变量所在的类上添加 @RefreshScope 注解。

1、重启服务,访问 http://localhost:9002/user/now ,输出 2022-05-07 14:56:20
2、将 Nacos 中之前配置的日期格式改为 yyyy/MM/dd HH:mm:ss
3、无需重启服务,再访问 http://localhost:9002/user/now ,输出 2022/05/07 14:57:30

方式2:使用 @ConfigurationProperties 注解。(推荐

src/main/java/com/study/config/PatternProperties.java

@Data
@Component
@ConfigurationProperties(prefix = "pattern")
public class PatternProperties {
    private String dateformat;
}

UserController 中添加内容用以读取配置:

@Autowired
private PatternProperties patternProperties;

@RequestMapping("/now2")
public String now2() {
    return new SimpleDateFormat(patternProperties.getDateformat()).format(new Date());
}
重启服务,访问 http://localhost:9002/user/now2 ,输出 2022/05/07 14:59:28
将 Nacos 中之前配置的日期格式改为 yyyy年MM月dd日 HH:mm:ss
无需重启服务,再访问 http://localhost:9002/user/now2 ,输出 2022年05月07日 15:00:36

5.4、公共配置

微服务启动时会从 Nacos 读取多个配置文件:

  • [spring.application.name]-[spring.profiles.active].yaml ,例如:user-service-dev.yaml 。
  • [spring.application.name].yaml ,例如:user-service.yaml 。

无论 profiles 如何变化,[spring.application.name].yaml 这个文件一定会加载,因此多个环境的公共配置写入这个文件。

多种配置优先级:服务名称-环境.yaml > 服务名称.yaml > 本地配置。即:Nacos环境配置 > Nacos公共配置 > 项目配置

06、数据持久化

Nacos 默认数据存储在内嵌数据库 Derby 中,不属于生产可用的数据库。这里切换为 MySQL 数据库。

先把 Nacos 服务和 项目服务全部暂停。

创建数据库:create database nacos; use nacos;

导入表结构:nacos\conf\nacos-mysql.sql

修改配置文件:nacos\conf\application.properties

#*************** Config Module Related Configurations ***************#
### If use MySQL as datasource:
spring.datasource.platform=mysql

### Count of DB:
db.num=1

### Connect URL of DB:
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=root
db.password.0=root

重启 Nacos,重启 UserServiceApplication 服务,发现控制台报错,找不到 @Value("${pattern.dateformat}") ,这是因为切换数据库导致之前的配置信息丢失,注释这行代码即可。

然后创建一个 user-service-test.yaml 配置,然后查看数据库:
请添加图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值