基于Nacos的注册中心与配置中心
Nacos简介
- 概述
Nacos全称是动态命名和配置服务,Nacos是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。Nacos主要用于发现、配置和管理微服务。
- 什么是Nacos
Nacos支持几乎所有主流类型的服务的发现、配置和管理。Nacos的关键特性包括:
-
服务发现和服务健康监测
-
动态配置服务
-
动态DNS
-
服务及其元数据管理
概念
- 地域
物理的数据中心,资源创建后不能更换。
- 可用区
同一个地域内,电力和网络互相独立的物理区域。同一个可用区内,实例的网络延迟较低。
- 接入点
地域的某个服务的入口域名。
- 命名空间
用来进行租户粒度的配置隔离。不同命名空间下,可以存在相同的Group或Data ID的配置。命名空间可以用来实现不同环境的配置区分隔离,例如开发测试环境和生产环境的资源隔离等。
- 配置
在系统开发过程中,通常会将一些需要变更的参数、变量等从代码中分离出来独立管理,以独立的配置文件的形式存在。
- 配置管理
系统配置的编辑、存储、分发、变更管理、历史版本管理、变更审计等所有与配置相关的活动。
- 配置项
一个具体的可配置的参数与其值域。
- 配置集
一组相关或者不相关的配置项的集合称为配置集。在系统中,一个配置文件通常就是一个配置集,包含了系统各个方面的配置。
- 配置集ID
Nacos中的某个配置集的ID。配置集ID是组织划分配置的维度之一。Data ID通常用于组织划分系统的配置集。一个系统或者应用可以包含多个配置集,每个配置集都可以被一个有意义的名称标识。Data ID通常采用类似Java包的命名规则保证全局唯一性。
- 配置分组
Nacos中的一组配置集,是组织配置的维度之一。通过一个有意义的字符串对配置集进行分组,从而区分Data ID相同的配置集。默认分组的名称是DEFAULT_GROUP。
- 配置快照
Nacos的客户端SDK会在本地生成配置的快照。当客户端无法连接到Nacos Server时,可以使用配置快照显示系统的整体容灾能力。配置快照类似于Git的本地提交,会在适当的时机更新。
- 服务
通过预定义接口网络访问的提供给客户端的软件功能。
- 服务名
服务提供的标识,通过该标识可以唯一确定其指代的服务。
- 服务注册中心
存储服务实例和服务负载均衡策略的数据库。
- 服务发现
在计算机网络上,对服务下的实例的地址和元数据进行探测,并以预先定义的接口提供给客户端进行查询。
- 元信息
Nacos数据描述信息,如服务版本、权重、容灾策略、负载均衡策略、鉴权配置。
- 应用
用于标识服务提供方的服务的属性。
- 服务分组
不同的服务可以归类到同一分组。
- 虚拟集群
同一个服务下的所有服务实例组成一个默认集群,集群可以被进一步按需求划分,划分的单位可以是虚拟集群。
- 实例
提供一个或多个服务的具有可访问网络地址的进程。
- 权重
实例级别的配置。权重为浮点数。权重越大,分配给该实例的流量越大。
- 健康检查
以指定方式检查服务下挂载的实例的健康度,从而确定该实例能否提供服务。
- 健康保护域值
为了防止因过多实例不健康导致流量全部流向实例,继而造成流量压力把健康实例压垮并型号才能雪崩效应,应该健康保护域值定义为一个0到1之间的浮点数。
Nacos架构
- 服务
服务指的是一个或一组软件功能,目的是不同的客户端可以为不同的目的重用。
- 服务注册中心
服务注册中心是服务、其实例及元数据的数据库。服务实例在启动时注册到服务注册表,并在关闭时注销。服务和路由器的客户端查询服务注册表以查找服务的可用实例。服务注册中心可能会调用服务实例的健康检查API来验证它是否能够处理请求。
- 服务元数据
服务元数据是指包括服务端点、服务标签、服务版本号、服务实例权重、路由规则、安全策略等描述服务的数据。
- 服务提供方
提供可复用和可调用服务的应用方。
- 服务消费方
发起对某个服务调用的应用方。
- 逻辑架构及组件介绍
- 服务管理:实现服务CRUD,域名CRUD,服务健康状态检查,服务权重管理等功能
- 配置管理:实现配置管CRUD,版本管理,灰度管理,监听管理,推送轨迹,聚合数据等功能
- 元数据管理:提供元数据CURD 和打标能力
- 插件机制:实现三个模块可分可合能力,实现扩展点SPI机制
- 事件机制:实现异步化事件通知,sdk数据变化异步通知等逻辑
- 日志模块:管理日志分类,日志级别,日志可移植性(尤其避免冲突),日志格式,异常码+帮助文档
- 回调机制:sdk通知数据,通过统一的模式回调用户处理。接口和数据结构需要具备可扩展性
- 寻址模式:解决ip,域名,nameserver、广播等多种寻址模式,需要可扩展
- 推送通道:解决server与存储、server间、server与sdk间推送性能问题
- 容量管理:管理每个租户,分组下的容量,防止存储被写爆,影响服务可用性
- 流量管理:按照租户,分组等多个维度对请求频率,长链接个数,报文大小,请求流控进行控制
- 缓存机制:容灾目录,本地缓存,server缓存机制。容灾目录使用需要工具
- 启动模式:按照单机模式,配置模式,服务模式,dns模式,或者all模式,启动不同的程序+UI
- 一致性协议:解决不同数据,不同一致性要求情况下,不同一致性机制
- 存储模块:解决数据持久化、非持久化存储,解决数据分片问题
- Nameserver:解决namespace到clusterid的路由问题,解决用户环境与nacos物理环境映射问题
- CMDB:解决元数据存储,与三方cmdb系统对接问题,解决应用,人,资源关系
- Metrics:暴露标准metrics数据,方便与三方监控系统打通
- Trace:暴露标准trace,方便与SLA系统打通,日志白平化,推送轨迹等能力,并且可以和计量计费系统打通
- 接入管理:相当于阿里云开通服务,分配身份、容量、权限过程
- 用户管理:解决用户管理,登录,sso等问题
- 权限管理:解决身份识别,访问控制,角色管理等问题
- 审计系统:扩展接口方便与不同公司审计系统打通
- 通知系统:核心数据变更,或者操作,方便通过SMS系统打通,通知到对应人数据变更
- OpenAPI:暴露标准Rest风格HTTP接口,简单易用,方便多语言集成
- Console:易用控制台,做服务管理、配置管理等操作
- SDK:多语言sdk
- Agent:dns-f类似模式,或者与mesh等方案集成
- CLI:命令行对产品进行轻量化管理,像git一样好用
- 领域模型
- 数据模型
Nacos数据模型Key由三元组唯一确定,Namespace默认是空串,公共命名空间,分组默认是DEFAULT_GROUP。
- 服务领域模型
Nacos服务的分级存储模型。
服务:健康检查开关、元数据、路由机制、保护域值;
集群:健康检查模式、元数据、同步机制;
实例:IP、端口、权重、健康状态、下线状态、元数据、响应时间。
- 配置领域模型
围绕配置主要有两个关联实体,一个是配置变更历史,一个是服务标签,由ID关联。
- 两种启动模式
Nacos支持将数据中心与配置中心在一个进程合并部署或者将两者分离部署的两种模式。
- 免费的公有云服务模式
Nacos支持公有云模式。
Nacos快速开始
- 安装环境
64位操作系统,JDK1.8+,Maven 3.2.x+,Nacos的运行需要至少2C4g60g*3的机器
- 下载源码或安装包
# 从Github上下载源码
git clone https://github.com/alibaba/nacos.git
cd nacos/
mvn -Prelease-nacos -Dmaven.test.skip=true clean install -U ls -al distribution/target/
cd distribution/target/nacos-server-$version/nacos/bin
# 下载编译后压缩包方式
# 在Github项目地址下载
- 启动服务器
# Linux/Unix/Mac
# 启动命令,单机模式运行
sh startup.sh -m standalone
# Windows
startup.cmd -m standalone
- 服务注册发现和配置管理
- 服务注册
curl -X POST 'http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=nacos.naming.serviceName&ip=20.18.7.10&port=8080'
- 服务发现
curl -X GET 'http://127.0.0.1:8848/nacos/v1/ns/instance/list?serviceName=nacos.naming.serviceName'
- 发布配置
curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test&content=HelloWorld"
- 获取配置
curl -X GET "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test"
- 关闭服务器
# Linux/Unix/Mac
sh shutdown.sh
# Windows
shutdown.cmd
Nacos Spring Boot实例
- 启动配置管理
- 创建实例工程
nacos-spring-boot-config
maven依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>nacos-config-spring-boot-starter</artifactId>
<version>0.2.1</version>
</dependency>
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>nacos-config-spring-boot-actuator</artifactId>
<version>0.2.1</version>
</dependency>
</dependencies>
application.yml配置文件:
nacos:
config:
server-addr: 127.0.0.1:8848 #配置Nacos Server地址
management:
endpoints:
web:
exposure:
include: '*'
endpoint:
health:
show-details: always
创建控制器ConfigController.class,提供访问点:
package org.lxx.nacos.spring.boot.config.controller;
import com.alibaba.nacos.api.config.annotation.NacosValue;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("config")
public class ConfigController {
//设置属性值,默认不会自动更新
@NacosValue(value = "${useLocalCache:false}", autoRefreshed = true)
private boolean useLocalCache;
@RequestMapping(value = "/get", method = RequestMethod.GET)
@ResponseBody
public boolean get() {
return useLocalCache;
}
}
创建启动类NacosConfigApplication.java:
package org.lxx.nacos.spring.boot.config;
import com.alibaba.nacos.spring.context.annotation.config.NacosPropertySource;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Nacos控制台添加配置
*/
@SpringBootApplication
//加载dataId为test的数据源,并开启自动更新
@NacosPropertySource(dataId = "test", autoRefreshed = true)
public class NacosConfigApplication {
public static void main(String[] args) {
SpringApplication.run(NacosConfigApplication.class, args);
}
}
- 启动服务调用
curl http://localhost:8080/config/get
,返回内容是false
。通过调用Nacos Open API向Nacos Server发布配置,再次访问接口,内容被动态更新了。
curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=test&group=DEFAULT_GROUP&content=useLocalCache=true"
- 启动服务发现
- 创建实例工程
nacos-spring-boot-discovery
maven依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>nacos-discovery-spring-boot-starter</artifactId>
<version>0.2.1</version>
</dependency>
</dependencies>
application.yml:
nacos:
discovery:
server-addr: 127.0.0.1:8848
创建控制器DiscoveryController.class:
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
@Controller
@RequestMapping("discovery")
public class DiscoveryController {
//注入Nacos的命名服务
@NacosInjected
private NamingService namingService;
@RequestMapping(value = "/get", method = RequestMethod.GET)
@ResponseBody
public List<Instance> get(@RequestParam String serviceName)
throws NacosException {
return namingService.getAllInstances(serviceName);
}
}
创建启动类NacosDiscoveryApplication.class:
package org.lxx.nacos.spring.boot.discovery;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class NacosDiscoveryApplication {
public static void main(String[] args) {
SpringApplication.run(NacosDiscoveryApplication.class, args);
}
}
- 启动
NacosDiscoveryApplication
,调用curl http://localhost:8080/discovery/get?serviceName=example
,此时返回为空 JSON 数组[]
。通过调用 Nacos Open API 向 Nacos server 注册一个名称为test
服务,再次访问接口返回了接口信息。
curl -X POST "http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=test&ip=127.0.0.1&port=8080"
Nacos Mysql Jpa实例
- 创建实例工程``
创建数据表:
CREATE TABLE `user` (
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci;
INSERT INTO `user` VALUES (1, '张三');
maven依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>nacos-config-spring-boot-starter</artifactId>
<version>0.2.1</version>
</dependency>
</dependencies>
application.yml:
nacos:
config:
server-addr: 127.0.0.1:8848
添加模型类User.class:
package org.lxx.nacos.spring.boot.config.mysql.model;
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Data
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String name;
}
添加DAO层接口UserRepository.java:
package org.lxx.nacos.spring.boot.config.mysql.dao;
import org.lxx.nacos.spring.boot.config.mysql.model.User;
import org.springframework.data.repository.CrudRepository;
public interface UserRepository extends CrudRepository<User, Integer> {
}
添加Service层实现UserService.java:
package org.lxx.nacos.spring.boot.config.mysql.service;
import org.lxx.nacos.spring.boot.config.mysql.dao.UserRepository;
import org.lxx.nacos.spring.boot.config.mysql.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
private final UserRepository userRepository;
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User findById(Integer id) {
return userRepository.findById(id).orElse(null);
}
}
添加控制器UserController.java:
package org.lxx.nacos.spring.boot.config.mysql.controller;
import org.lxx.nacos.spring.boot.config.mysql.model.User;
import org.lxx.nacos.spring.boot.config.mysql.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController("user")
public class UserController {
private final UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping
public User get(@RequestParam int id) {
return userService.findById(id);
}
}
添加启动类SpringBootMySQLApplication.java:
package org.lxx.nacos.spring.boot.config.mysql;
import com.alibaba.nacos.spring.context.annotation.config.NacosPropertySource;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@NacosPropertySource(dataId = "mysql")
public class SpringBootMySQLApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootMySQLApplication.class, args);
}
}
在Nacos控制台配置mysql数据集:
Data ID: mysql, GROUP: DEFAULT_GROUP。
spring.datasource.url=jdbc:mysql://localhost:3306/mytest?serverTimezone=GMT%2B8&characterEncoding=UTF-8&useSSL=false
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root
- 启动应用后,访问
http://127.0.0.1:8080/user?id=1
可以读取到数据库中的数据。