主题
Nacos
是什么
Nacos是一个为云原生应用提供的服务动态发现、配置管理、服务管理的平台。
支持3种集群模式:单机模式、集群模式、多集群模式。
单机模式多用于测试和单机试用,集群模式多用于生产环境,确保高可用,多集群模式多用于多数据中心场景
TIP
nacos可以做服务发现、配置管理和服务管理,支持3种集群模式
单机模式启动
nacos\bin\startup.cmd
配置单机模式
set MODE="standalone"
配置集群模式
set MODE="cluster"
管理后台
http://127.0.0.1:8848/nacos/index.html
用户名和密码都是: nacos
整体框架
Nacos提供了两个核心的服务,Config Service配置中心和Naming Service命名服务。
接着就是Nacos Core,也是Nacos的核心实现部分。
最底层是Consistency Protocol,数据一致性协议,用来实现Nacos集群数据同步。
对外,使用Http协议,提供OpenAPI,客户端程序可以通过该API来完成数据的交互和处理
Nacos2.x的新特性
新增了支持gRPC协议、安全机制比如鉴权、加密。
Nacos中的namespace和group
Nacos中提供了namespace和group命名空间和分组的机制,它是Nacos提供的一种数据模型 namespace用来区分不同环境,group可以专注在业务层面的数据分组。 不同的命名空间下,可以存在相同的Group 或 Data ID 的配置
SpringBoot注册到Nacos
yaml
spring:
application:
name: use-nacos-spring-boot-wxm
server:
port: 7824
dubbo:
registry:
address: nacos://127.0.0.1:8848
protocol:
name: dubbo
port: 20880
application:
parameters:
namespace: c7ba173f-29e5-4c58-ae78-b102be11c4f9
group: use-nacos-spring-boot
nacos:
discovery:
server-addr: 127.0.0.1:8848
namespace: c7ba173f-29e5-4c58-ae78-b102be11c4f9
register:
group-name: use-nacos-spring-boot-wxm
service-name: use-nacos-spring-boot-wxm
auto-register: true
java
import org.apache.dubbo.config.spring.context.annotation.DubboComponentScan;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@DubboComponentScan(value = "cn.diyai")
@EnableDubbo
public class UserNacosApplication {
public static void main(String[] args) {
SpringApplication.run(UserNacosApplication.class,args);
}
}
package cn.diyai.service;
public interface PayService {
String pay();
}
package cn.diyai.service.impl;
import cn.diyai.service.PayService;
import org.apache.dubbo.config.annotation.DubboService;
@DubboService(group = "use-nacos-spring-boot",version = "1.0.0")
public class PayServiceImpl implements PayService {
@Override
public String pay() {
return "payResult";
}
}
package cn.diyai.controller;
import cn.diyai.service.PayService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController(value = "/pay")
public class PayController {
@Autowired
PayService payService;
@GetMapping("/toPay")
public String pay(){
return payService.pay();
}
}
java
@EnableDiscoveryClient(autoRegister = true)
@Import({EnableDiscoveryClientImportSelector.class})
public class NacosServiceRegistry implements ServiceRegistry<Registration> {
private static final String STATUS_UP = "UP";
private static final String STATUS_DOWN = "DOWN";
private static final Logger log = LoggerFactory.getLogger(NacosServiceRegistry.class);
private final NacosDiscoveryProperties nacosDiscoveryProperties;
@Autowired
private NacosServiceManager nacosServiceManager;
public NacosServiceRegistry(NacosDiscoveryProperties nacosDiscoveryProperties) {
this.nacosDiscoveryProperties = nacosDiscoveryProperties;
}
}
register方法
public void register(Registration registration) {
if (StringUtils.isEmpty(registration.getServiceId())) {
log.warn("No service to register for nacos client...");
} else {
NamingService namingService = this.namingService();
String serviceId = registration.getServiceId();
String group = this.nacosDiscoveryProperties.getGroup();
Instance instance = this.getNacosInstanceFromRegistration(registration);
try {
namingService.registerInstance(serviceId, group, instance);
log.info("nacos registry, {} {} {}:{} register finished", new Object[]{group, serviceId, instance.getIp(), instance.getPort()});
} catch (Exception var7) {
log.error("nacos registry, {} register failed...{},", new Object[]{serviceId, registration.toString(), var7});
ReflectionUtils.rethrowRuntimeException(var7);
}
}
}
Nacos提供的Nacos Console可以完成各种运维操作。核心模块Nacos Server由以下4部分组成:
- Open API暴露各种操作,客户端可以调用Open API完成各项事宜。
- Config Service和Naming Service分别对应配置功能及服务注册/发现功能。
- Nacos Core是整个Nacos Server的核心,上层的Config Service和Naming Service底层都依赖Nacos Core。
- Nacos Core底层提供一致性协议算法。
Nacos Service Discovery 和 Spring Web
基于Properties创建实例
java
public static ConfigService createConfigService(Properties properties) throws NacosException {
try {
Class<?> driverImplClass = Class.forName("com.alibaba.nacos.client.config.NacosConfigService");
Constructor constructor = driverImplClass.getConstructor(Properties.class);
ConfigService vendorImpl = (ConfigService)constructor.newInstance(properties);
return vendorImpl;
} catch (Throwable var4) {
throw new NacosException(-400, var4);
}
}
Nacos
注册中心 Eureka、Nacos、Consul、Etcd、Zookeeper、。。。
为什么需要?
是什么
https://nacos.io/zh-cn/docs/what-is-nacos.html Nacos是一个为云原生应用提供的服务动态发现、配置管理、服务管理的平台。
它是诞生在阿里巴巴内部的一个项目,经历了多次双十一的挑战,在应用扩展性和大规模服务治理上起到了非常大的作用。 直到2018年开始开源,从开源到现在已经了很多个版本的迭代,目前最新版本是2.1.1
2.x版本在性能上和扩展性上做了非常大的改变,它的核心目标也是拥抱云原生生态。
Nacos整体架构
整个架构还是非常清晰,Nacos提供了两个很核心的服务
- Config Service,配置中心
- Naming Service, 命名服务,也就是服务注册与发现
接着就是Nacos Core,也是Nacos的核心实现部分。
最底层是Consistency Protocol,数据一致性协议,用来实现Nacos集群数据同步。 对外,使用Http协议,提供OpenAPI,客户端程序可以通过该API来完成数据的交互和处理
Nacos2.x新特性
Nacos2.x版本主要就引入了两个新特性
- 引入了gRPC协议的支持,gRPC协议在性能上要远远比Nacos1.x中的 http协议要好。
- 增加了安全机制,如鉴权、加密
但是这两种升级对我们的日常影响不是很大,主要影响的点在于Nacos2.x的客户端无法兼容Nacos1.x的服务端。
但是Nacos2.x服务端是完全兼容1.x的客户端的。
升级2.0服务端
Nacos安装部署
Nacos是一个独立的中间件,需要单独下载安装才可使用,Nacos支持三种集群模式
单机模式 - 用于测试和单机试用。
集群模式 - 用于生产环境,确保高可用。
多集群模式 - 用于多数据中心场景。
单机环境安装
本次分享的Nacos版本为:2.1.1。 这也是目前Spring Cloud Alibaba生态中最新的release版本 Spring Boot版本:2.6.12 Nacos
执行tar -zxvf nacos-server-2.1.1.tar.gz命令解压文件
执行sh startup.sh -m standalone,启动单个节点的nacos server
访问
http://localhost:8848/nacos
的Nacos Server的控制台。 默认登 录帐号密码:nacos/nacos。
集群环境安装
推荐用户把所有服务列表放到一个vip下面,然后挂到一个域名下面
http://ip1:port/openAPI 直连ip模式,机器挂则需要修改ip才可以使用。
http://SLB:port/openAPI 挂载SLB模式(内网SLB,不可暴露到公网,以免带来安全风险),直连SLB即可,下面挂server真实ip,可读性不好。 http://nacos.com:port/openAPI 域名 + SLB模式(内网SLB,不可暴露到公网,以免带来安全风险),可读性好,而且换ip方便,推荐模式
搭建Nacos集群
- 准备好三台虚拟机,如果没有三台,那就直接复制三份,并修改端口
shell
192.168.8.133:8848
192.168.8.133:8849
192.168.8.133:8850
修改这三个节点的持久化配置(Mysql持久化配置)
propertiesspring.datasource.platform=mysql #### Count of DB: db.num=1
Connect URL of DB:
db.url.0=jdbc:mysql://192.168.8.132:3306/nacos? characterEncoding=utf8&connectTimeout=1000&socketTimeo ut=3000&autoReconnect=true&useUnicode=true&useSSL=fal se&serverTimezone=UTC db.user.0=root db.password.0=123456
3. 通过${NACOS_HOME}\conf\cluster.conf.example示例文件,复制一份cluster.conf文件出来
4. 把三个节点的ip和端口写在该配置文件中,这个文件需要每个Nacos
Server节点都修改。
```shell
192.168.8.133:8848
192.168.8.133:8849
192.168.8.133:8850
- 启动服务后,在Nacos-Server任意一个节点的控制台页面:集群管理菜单,可以看到如下内容,表示集群配置成功。
客户端配置方式
propertiesspring.cloud.nacos.config.server- addr=192.168.8.133:8848,192.168.8.133:8849,192.168.8.133:8850
测试方式,访问其中一个节点,更新一个配置,同步看其他节点的配置 变化。
Nacos注册中心的使用
如果应用程序需要使用Nacos作为注册中心,那么至少要提供下面这些接口。
- 服务注册接口,应用程序启动以后把服务地址注册保存到注册中心
- 服务地址获取接口,获取指定服务的所有地址信息
- 服务地址变更监听接口,实现服务变更的动态监听
服务注册接口和使用
java
void registerInstance(String serviceName, String ip, int port) throws
NacosException;
void registerInstance(String serviceName, String ip, int port, String
clusterName) throws NacosException;
void registerInstance(String serviceName, Instance instance) throws
NacosException;
其中: serviceName: 表示服务名称 ip: 表示服务实例的ip地址 port: 服务实例的端口 clusterName: 集群名 instance: 实例属性 https://github.com/nacos-group/nacos-spring-boot-project
Spring Boot集成Nacos实现服务注册的Demo
spring boot版本:2.6.12,Nacos spring boot版本:0.2.12(最新版) 引入Nacos依赖包
xml
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>nacos-discovery-spring-boot-starter</artifactId>
<version>0.2.12</version>
</dependency>
配置Nacos的服务地址
nacos.discovery.server-addr=192.168.8.133:8848
编写Controller接口
java
@RestController
public class NacosController {
@NacosInjected
private NamingService namingService;
@GetMapping("/registry")
public void registry() throws NacosException {
Instance instance=new Instance();
instance.setClusterName("Test1"); //集群名字
instance.setEnabled(true); //是否启用
//临时节点/持久节点, 临时节点是AP模式(采用distro算法),持久节点是CP(采用raft算法)
instance.setEphemeral(true);
instance.setHealthy(true); //健康状态
instance.setIp("192.168.216.112"); //ip
instance.setPort(8080); //端口
instance.setWeight(4.0); //权重 取值范围 1 到 100,数值越大,权重越大
namingService.registerInstance("example",instance);
}
注册成功以后,可以在Nacos的控制台看到注册上去的服务信息。
服务实例查找
当客户端需要获取目标服务的全部实例是,可以通过以下api来完成,它会 返回指定服务名字或者指定集群列表中的所有实例.
java
List<Instance> getAllInstances(String serviceName) throws
NacosException;
List<Instance> getAllInstances(String serviceName, List<String>
clusters) throws NacosException;
演示代码如下,增加下面的接口,根据指定服务名称获取实例列表
java
@RestController
public class NacosController {
@NacosInjected
private NamingService namingService;
@GetMapping("/discovery")
public List<Instance> get(@RequestParam String serviceName)
throws NacosException {
return namingService.getAllInstances(serviceName);
}
}
}
服务事件订阅
当服务提供者出现服务扩容和缩容时,服务消费者需要感知到服务地址的变化,可以通过监听服务来动态感知到服务的变化
java
void subscribe(String serviceName, EventListener listener) throws
NacosException;
void subscribe(String serviceName, List<String> clusters,
EventListener listener) throws NacosException;
其中,listener表示的是服务监听的事件回调。 Nacos还提供了取消监听的接口,定义如下。
java
void unsubscribe(String serviceName, EventListener listener)
throws NacosException;
void unsubscribe(String serviceName, List<String> clusters,
EventListener listener) throws NacosException;
了解了这些点之后,我们再去从源码层面分析Nacos的一些实现细节。当然,我们的分析过程,会基于Spring Cloud Alibaba来做。 增加如下接口,用来注册订阅事件,当我们调用registry发起注册、或者在nacos控制台对服务进行下线的时候,都会触发事件通知。
java
@GetMapping("/sub")
public void subscribe(@RequestParam String serviceName) throws
NacosException {
namingService.subscribe(serviceName, new EventListener() {
@Override
public void onEvent(Event event) {
NamingEvent namingEvent=(NamingEvent)event;
System.out.println("收到事
件:"+namingEvent.getServiceName());
}
});
}
Nacos中的Namespace和Group
在Nacos中提供了namespace和group命名空间和分组的机制,它是Nacos提供的一种数据模型。
namespace可以解决多环境以及多租户数据的隔离问题。比如在多套环境下,可以根据指定环境创建不同的namespace,实现多环境隔离。或者在多租户的场景中,每个用户可以维护自己的namespace,实现每个用户的配置数据和注册数据的隔离。 group是分组机制,它的维度是实现服务注册信息或者DataId的分组管理机制,对于group的用法,没有固定的规则,它也可以实现不同环境下的分组,也可以实现同一个应用下不同配置类型或者不同业务类型的分组。
官方建议是,namespace用来区分不同环境,group可以专注在业务层面的数据分组。实际上在使用过程中,最重要的是提前定要统一的口径和规定,避免不同的项目团队混用导致后期维护混乱的 问题。
Nacos中的Namespace和Group关系如下图所示,其中Namespace默认是public,Group默认是DEFAULT_GROUP。
自定义Namespace
name用于进行租户粒度的配置隔离。不同的命名空间下,可以存在相同的Group 或 Data ID 的配置。Namespace 的常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。
在没有明确指定配置的情况下, 默认使用的是 Nacos 上 Public 这个namespae。
如果需要使用自定义的命名空间,可以通过以下配置来实现:
1、在Nacos-Console中创建两个命名空间,分别是
2、修改dubbo相关配置项目
properties
dubbo.registries.nacos-
registry.address=nacos://192.168.8.133:8848?
namespace=47201fb3-2b45-4199-87c5-298a748c9385
或者使用这种方式
properties
dubbo.registry.parameters.namespace=5cbb70a5-xxx-xxx-xxx-d43479ae0932
自定义Group
在没有明确指定配置的情况下, 默认使用的是 DEFAULT_GROUP 。如果需要自定义自己的 Group,可以通过以下配置来实现:
properties
dubbo.registries.nacos-
registry.address=nacos://192.168.8.133:8848?
namespace=47201fb3-2b45-4199-87c5-
298a748c9385&group=example
或者使用下面的方式
properties
dubbo.registry.parameters.group=example
Nacos的基本应用
服务端的配置存储 配置的CRUD 配置的动态感知
gRPC协议通信(protobuf+http2)
Dubbo没有集成Nacos作为动态配置中心。 Dubbo - Config Center (维护dubbo.properties 外部化配置) -> nacos Dubbo 集成Nacos做动态配置管理 ? Spring Boot + Dubbo + Nacos Spring Cloud , 注解实现动态感知? @RefeshScope @Value (Nacos服务器上的配置作为外部化配置)
Nacos的基本使用
前面分析了Nacos作为配置中心的基本功能,那么不难分析出,Nacos必然也会提供配置管理相关的API来完成功能的实现。 从远程服务器上获取配置 服务器上配置变更后的通知
从服务器获取配置
用于服务启动的时候从 Nacos 获取配置。
java
public String getConfig(String dataId, String group, long
timeoutMs) throws NacosException
1、dataId string 配置 ID,采用类似 package.class(如com.taobao.tc.refund.log.level)的命名规则保证全局唯一性,class 部分建议是配置的业务含义。全部字符小写。只允许英文字符和 4 种特殊字符("."、":"、"-"、"_"),不超过 256 字
2、group string (Nacos:Test)保证唯一性,只允许英文字符和4种特殊字符("."、":"、"-"、"_"),不超过128字节 配置分组,建议填写产品名:模块名
3、timeout long 读取配置超时时间,单位 ms,推荐值 3000。
引入jar包依赖
xml
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-api</artifactId>
<version>2.1.1</version>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-common</artifactId>
<version>2.1.1</version>
</dependency>
编写配置获取代码
java
public class NacosExample {
public static void main(String[] args) {
String serverAddr="192.168.8.133:8848";
String dataId="example";
String groupId="DEFAULT_GROUP";
Properties properties=new Properties();
properties.put("serverAddr",serverAddr);
try {
//通过nacosfactory创建一个配置中心的服务
ConfigService configService=
NacosFactory.createConfigService(properties);
// 5000表示读取配置的超时时间,如果超时或者出现网络
content=configService.getConfig(dataId,groupId,3000);
故障,会抛出NacosException的异常
String
System.out.println(content);
} catch (NacosException e) {
e.printStackTrace();
}
}
}
上述代码的含义是, 使用NacosFactory构建ConfigService 通过getConfig来获得配置,查询指定groupId下对应的dataId这个数 配置变更订阅
java
public class NacosMain {
public static void main(String[] args) {
String serverAddr="192.168.8.133:8848";
String dataId="example";
String groupId="DEFAULT_GROUP";
Properties properties=new Properties();
properties.put("serverAddr",serverAddr);
try {
//通过nacosfactory创建一个配置中心的服务
ConfigService configService=
NacosFactory.createConfigService(properties);
// 5000表示读取配置的超时时间,如果超时或者出现网络故
障,会抛出NacosException的异常
String
content=configService.getConfig(dataId,groupId,3000);
configService.addListener(dataId, groupId, new Listener() {
@Override
public Executor getExecutor() {
return null;
}
@Override
public void receiveConfigInfo(String s) {
System.out.println("收到配置变更通知:"+s);
}
});
System.out.println(content);
System.in.read(); //保证程序不执行结束
} catch (NacosException | IOException e) {
e.printStackTrace();
}
}
}
配置 SpringBoot集成Nacos实现动态
下面演示Spring Boot集成Nacos实现动态配置获取 添加jar包依赖
xml
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>nacos-config-spring-boot-starter</artifactId>
<version>0.2.12</version>
</dependency>
注意:版本 0.2.x.RELEASE 对应的是 Spring Boot 2.x 版本,版本 0.1.x.RELEASE 对应的是 Spring Boot 1.x 版本。 编写测试案例 在main方法中增加注解
java
@NacosPropertySource(dataId="example",autoRefreshed = true)
@SpringBootApplication
public class NacosDemoApplication {
public static void main(String[] args) {
SpringApplication.run(NacosDemoApplication.class, args);
}
}
使用 @NacosPropertySource 加载 dataId 为 example 的配置源,并开启自动更新 增加一个controller作为测试
java
@RestController
public class NacosController {
true)
private String txt;
@GetMapping("/")
public String get(){
return txt;
}
}
@NacosValue(value = "${txt:default-msg}",autoRefreshed = )