Nacos实例一则及其源码环境搭建
\[N_什么是微服务\]
官页 - https://nacos.io/
Nacos Server 发布历史 - https://nacos.io/download/release-history/?spm=5238cd80.6a33be36.0.0.63db1e5d6g4YuH
Quick Start - https://nacos.io/docs/v3.0/quickstart/quick-start/?spm=5238cd80.246ac1ea.0.0.276ee755uXcz1x
文档 - https://nacos.io/docs/v2.5/overview/?spm=5238cd80.246ac1ea.0.0.276ee755uXcz1x
https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html#_introduction
Quick Start
环境
Nacos 依赖 Java 环境来运行,请确保是在以下版本环境中安装使用:
下载解压
进入Nacos Github 的 最新稳定版本 ,选择需要下载的Nacos版本,在Assets中点击下载 nacos-server-$version.zip 包。
unzip nacos-server-$version.zip
cd nacos/bin
启动服务器
启动命令 (standalone代表着单机模式运行,非集群模式):
# linux
sh startup.sh -m standalone
# windowns
startup -m standalone
Running in stand alone mode, All function modules
Port: 8848
打开任意浏览器,输入地址:http://192.168.20.130:8848/nacos/index.html, 即可进入Nacos控制台页面。
首次打开会要求初始化管理员用户
nacos的密码。
做注册中心
服务提供者
服务发现是微服务架构中的关键组件之一。在这样的架构中,手动为每个客户端配置服务列表可能是一项艰巨的任务,并且使得动态扩展极其困难。Nacos Discovery 帮助您自动将您的服务注册到 Nacos 服务器,Nacos 服务器会跟踪服务并动态刷新服务列表。此外,Nacos Discovery 将服务实例的一些元数据,如主机、端口、健康检查 URL、主页等注册到 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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.13.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.yang</groupId>
<artifactId>SpringCloudAlibaba</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>SpringCloudAlibaba</name>
<packaging>pom</packaging>
<properties>
<java.version>1.8</java.version>
<spring-cloud-alibaba-version>2.2.5.RELEASE</spring-cloud-alibaba-version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<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>
<modules>
<module>cloudAlibaba-Nacos</module>
</modules>
</project>
子模块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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.yang</groupId>
<artifactId>SpringCloudAlibaba</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.yang</groupId>
<artifactId>cloudAlibaba-Nacos</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
</project>
YML配置
java
server:
port: 9001
spring:
application:
name: nacos-provider
cloud:
discovery:
server-addr: 127.0.0.1:8848
management:
endpoint:
web:
exposure:
include:'*'
启动类
java
@SpringBootApplication
@EnableDiscoveryClient//加此注解
public class CloudalibabaNacosApplication {
public static void main(String[] args) {
SpringApplication.run(CloudalibabaNacos9001Application.class, args);
}
}
java
@RestController
public class DemoController {
@Value("${server.port}")
private String serverPort;
@GetMapping(value = "/hi")
public String getServerPort(){
return "Hello Nacos Discovery "+serverPort;
}
}
注意 服务提供者可以多个,访问路径是配置在
spring.application.name中的名字http://nacos-provider
服务消费者
Ribbon
它是一个基于HTTP和TCP客户端 负载均衡器。它虽然只是一个工具类库,它却是每一个微服务的基础设施。因为实际上,对于服务间调用、API网关请求转发都需要经过Ribbon负载均衡来实现。总体来说,Ribbon的主要作用是:从注册服务器端拿到对应服务列表后以负载均衡的方式访问对应服务。
Nacos已经整合了Ribbon,所以我们想要使用只需要导入Spring Cloud Alibaba Nacos的依赖就可以直接使用了。
RestTemplate
restTemplate.getForObject(arg1,arg2,arg3...);
第一个参数url表示被调用的目标Rest接口位置:
- url的第一部分是在Nacos中注册的服务提供者名称,如果多个服务提供者注册相同名称,Ribbon会自动 负载均衡 寻找其中一个服务提供者,并且调用接口方法。
- url后半部是控制器的请求路径。
第二个参数是返回值类型 :JavaBean类型或者JavaBean数组类型,如果控制器返回的是List集合,需要使用数组类型接收。
第三个参数是可变参数 : 是传递给url的动态参数,使用参数时候需要在url上需要使用{1}、{2}、{3}进行参数占位,这样传递的参数就会自动替换占位符。
YML配置
java
server:
port: 8083
spring:
application:
name: nacos-consumer
cloud:
discovery:
server-addr: localhost:8848
# 消费者将要去访问的微服务名称(注册成功的Nacos的微服务提供者)
service-url:
nacos-user-service: http://nacos-provider
调用代码
java
@SpringBootApplication
@EnableDiscoveryClient
public class CloudalibabaConsumerNacos8083Application {
public static void main(String[] args) {
SpringApplication.run(CloudalibabaConsumerNacos8083Application.class, args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
java
@RestController
public class DemoController {
@Resource
private RestTemplate restTemplate;
/**
* 消费者去访问具体服务,这种写法可以实现
* 配置文件和代码的分离
*/
@Value("${service-url.nacos-user-service}")
private String serverURL;
@GetMapping(value = "consumer/nacos")
public String getDiscovery(){
System.err.println(serverURL);
return restTemplate.getForObject(serverURL+"/hi",String.class);
}
}
访问: http://localhost:8083/consumer/nacos 即实现路径是, 客户端 > nacos > 服务提供者
Nacos中本身就集成了Ribbon所以它本身就自带负载均衡
做配置中心
业务端配置
依赖
java
<dependency>
<groupId> com.alibaba.cloud </groupId>
<artifactId> spring-cloud-starter-alibaba-nacos-config </artifactId>
</dependency>
配置文件
bootstrap.yml
yaml
# nacos配置
server:
port: 3377
spring:
application:
name: nacos-config-client # 微服务名称
cloud:
nacos:
discovery:
server-addr: localhost:8848 #Nacos服务注册中心地址
config:
server-addr: localhost:8848 #Nacos作为配置中心地址
file-extension: yaml #指定yaml格式的配置
注意: bootstrap.yml(或 bootstrap.properties)是Spring Cloud 微服务架构 中优先级最高 的配置文件,仅用于系统级、高优先级的初始化配置 ,和普通的 application.yml 有明确分工
在 application.yml 配置
yaml
spring:
profiles:
active: dev # 表示开发环境
业务端代码
这里的@RefreshScope实现配置自动更新,意思为如果想要使配置文件中的配置修改后不用重启项目即生效,可以使用@RefreshScope配置来实现
java
@RestController
@RefreshScope //支持Nacos的动态刷新功能
public class ConfigClientController {
@Value("${config.info}")
private String configInfo;
@GetMapping("/config/info")
public String getConfigInfo(){
return configInfo;
}
}
Nacos配置
dataId
在 Nacos 配置中心里,dataId 是定位配置文件的唯一标识 ,Spring Cloud 集成 Nacos 时,它有固定的标准格式
java
${prefix}-${spring.profiles.active}.${file-extension}
prefix默认为spring.application.name的值,也可以通过配置项spring.cloud.nacos.config.prefix来配置。spring.profiles.active即为当前环境对应的 profile,注意:当spring.profiles.active为空时,对应的连接符-也将不存在,dataId 的拼接格式变成${prefix}.${file-extension}(不能删除)file-exetension为配置内容的数据格式,可以通过配置项spring.cloud.nacos.config.file-extension来配置。目前只支持properties和yaml类型。- 通过 Spring Cloud 原生注解
@RefreshScope实现配置自动更新: - 所以根据官方给出的规则我们最终需要在Nacos配置中心添加的配置文件的名字规则和名字为:
java
# ${spring.application.name}-${spring.profiles.active}.${file-extension}
# nacos-config-client-dev.yaml
# 微服务名称-当前环境-文件格式
增加配置
按如上格式 Data ID 是: nacos-config-client-dev.yaml
配置格式: YAML
配置内容:
yaml
config:
info: nacos config center,10001
增加后 访问业务端 即可看到 从配置中心获取的参数: http://localhost:3377/config/info
关于命名空间、分组、环境
命名空间/分组/环境这三个是逻辑概念, 类似文件夹一样 定位到唯一的配置文件
设计出来有一些考量:
- Namespace : 不同环境的配置的区分隔离,例如 开发测试环境和生产环境的资源
- Group: 对配置集进行分组,从而区分 Data ID 相同的配置集,比如不同地区的集群划分到同一个配置组里面去
- Data ID: 配置集,最小的操作单位,可以简单理解为配置文件
命名空间 (Namespace)
用于进行租户粒度的配置隔离。不同的命名空间下,可以存在相同的 Group 或 Data ID 的配置。Namespace 的常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。
配置分组 (Group)
Nacos 中的一组配置集,是组织配置的维度之一。通过一个有意义的字符串(如 Buy 或 Trade )对配置集进行分组,从而区分 Data ID 相同的配置集。当您在 Nacos 上创建一个配置时,如果未填写配置分组的名称,则配置分组的名称默认采用 DEFAULT_GROUP 。配置分组的常见场景:不同的应用或组件使用了相同的配置类型,如 database_url 配置和 MQ_topic 配置。
配置集 ID (Data ID)
Nacos 中的某个配置集的 ID。。一个系统或者应用可以包含多个配置集,每个配置集都可以被一个有意义的名称标识。Data ID 通常采用类 Java 包(如 com.taobao.tc.refund.log.level)的命名规则保证全局唯一性。此命名规则非强制。
配置集:一组相关或者不相关的配置项的集合称为配置集。在系统中,一个配置文件通常就是一个配置集,包含了系统各个方面的配置。例如,一个配置集可能包含了数据源、线程池、日志级别等配置项。
源码环境
Git 仓库页 - https://github.com/alibaba/nacos
源码下载页
https://github.com/alibaba/nacos/releases/tag/2.2.3
简单分析各个模块
Nacos 本质是是一个 Spring boot 的 应用, 其主入口模块是 console 模块
xml
<!-- Submodule management -->
<modules>
<!-- 配置管理模块 -->
<module>config</module>
<!-- 核心基础模块:提供集群管理、网络通信(gRPC)、事件总线等基础设施 -->
<module>core</module>
<!-- 服务注册与发现模块:负责服务的注册、注销、健康检查及服务查询 -->
<module>naming</module>
<!-- 地址服务模块:提供 Server 节点列表查询,辅助客户端寻址 -->
<module>address</module>
<!-- 测试模块:包含项目的单元测试和集成测试代码 -->
<module>test</module>
<!-- API 定义模块:定义 ConfigService 和 NamingService 等核心接口,不含实现 -->
<module>api</module>
<!-- 客户端 SDK 模块:实现 API 接口,封装与服务端通信、缓存、心跳等逻辑 -->
<module>client</module>
<!-- 示例模块:包含使用 Nacos API 的简单代码示例 -->
<module>example</module>
<!-- 公共工具模块:包含通用的工具类、常量、异常定义,被其他模块依赖 -->
<module>common</module>
<!-- 分发打包模块:定义最终发布产物的结构,负责生成发行包 -->
<module>distribution</module>
<!-- 控制台模块:提供 Web 管理界面,用于服务管理和配置发布 -->
<module>console</module>
<!-- CMDB 模块:对接外部配置管理数据库,获取应用与主机元数据 -->
<module>cmdb</module>
<!-- Istio 适配模块:实现与 Istio 服务网格的集成(作为注册中心) -->
<module>istio</module>
<!-- 一致性协议模块:封装 CP(Raft) 和 AP(Distro) 协议,处理数据一致性 -->
<module>consistency</module>
<!-- 认证授权模块:实现用户登录、身份验证及基于角色的权限控制(RBAC) -->
<module>auth</module>
<!-- 系统管理模块:负责系统参数配置、环境变量加载及系统生命周期管理 -->
<module>sys</module>
<!-- 插件定义模块:定义 SPI 扩展点(如认证、数据源插件接口) -->
<module>plugin</module>
<!-- 插件默认实现模块:提供插件接口的默认实现逻辑 -->
<module>plugin-default-impl</module>
<!-- Prometheus 模块:暴露监控指标,支持与 Prometheus 监控系统集成 -->
<module>prometheus</module>
</modules>
源码运行的踩坑指南
java: 找不到符号
...
java: 找不到符号
符号: 类 Logger
位置: 类 com.alibaba.nacos.plugin.trace.NacosTracePluginManager
执行一次 Maven 的 compile 即可
unnamed module
java
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:184)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:162)
... 8 common frames omitted
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field static final boolean java.io.FileSystem.useCanonCaches accessible: module java.base does not "opens java.io" to unnamed module @26aa12dd
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354)
JDK9之后的多模块 反射访问的问题, 对新版本的JDK支持不太好, 用JDK8 跑吧
UnknownHostException: jmenv.tbsite.net
Caused by: java.net.UnknownHostException: jmenv.tbsite.net
at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:567)
at java.base/java.net.Socket.connect(Socket.java:751)
原因是, 默认情况下 Nacos 是以集群模式启动的, 直接在启动参数中添加:
-Dnacos.standalone=true以单机模式启动