1、nacos介绍
Nacos是阿里的一个开源产品,它是针对微服务架构中的服务发现、配置管理、服务治理的综合型解决方案。
Nacos致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您实现动态服务发现、服务配置管理、服务及流量管理。 Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。
Nacos是构建以“服务”为中心的现代应用架构的服务基础设施。
2、nacos特性
Nacos主要提供以下四大功能:
1. 服务发现与服务健康检查
Nacos使服务更容易注册,并通过DNS或HTTP接口发现其他服务,Nacos还提供服务的实时健康检查,以防止向不健康的主机或服务实例发送请求。
2. 动态配置管理
动态配置服务允许您在所有环境中以集中和动态的方式管理所有服务的配置。Nacos消除了在更新配置时重新部署应用程序,这使配置的更改更加高效和灵活。
3. 动态DNS服务
Nacos提供基于DNS 协议的服务发现能力,旨在支持异构语言的服务发现,支持将注册在Nacos上的服务以域名的方式暴露端点,让三方应用方便的查阅及发现。
4. 服务和元数据管理
Nacos 能让您从微服务平台建设的视角管理数据中心的所有服务及元数据,包括管理服务的描述、生命周期、服务的静态依赖分析、服务的健康状态、服务的流量管理、路由及安全策略。
3、nacos安装下载
Nacos 下载地址:Releases · alibaba/nacos · GitHub
进入到:nacos-server-2.0.3\nacos\bin 修改如下,改成单机模式启动
set MODE="standalone"
在双击启动: startup.cmd
或者使用进入bin目录,使用启动命令
startup.cmd -m standalone
# standalone代表着单机模式运行,非集群模式,默认是集群模式
访问:http://127.0.0.1:8848/nacos
默认账户密码:nacos/nacos
4、nacos使用外部MySQL存储数据
当我们使用Nacos作为配置信息的时候,我们希望能够对配置信息进行更好的数据管理,那么默认的Nacos是将nacos-server作为分布式配置中心的数据存储到了一个叫做derby的内嵌数据库到Java应用程序中了。但是它并不便于管理。Nacos提供了可以配置外部MySQL来存储配置数据。
目前nacos只支持mysql5.6.5+版本以上
操作步骤
第一步(安装MySQL5.7,并下载nacos安装包)
第二步 (创建nacos_config数据库)
创建nacos_config数据库,并执行nacos/conf/目录下的nacos-mysql.sql初始化脚本文件。
第三步(修改配置文件)
- 修改nacos/conf目录下的application.properties配置文件,增加支持MySQL数据源配置(目前只支持MySQL),添加数据源url、用户名、密码。如下所示:
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://localhost:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&serverTimezone=Asia/Shanghai
db.user=root
db.password=123456
第四步(启动)
- 进入nacos\bin目录下,通过startup.cmd -m standalone命令启动Nacos。
第五步 验证配置是否OK
访问 http://localhost:8848/nacos
创建命名空间
查询tenant_info表中数据
5、Nacos作为注册中心
创建一个springboot项目,里面包含producer和consumer两个子模块
1)引入依赖
<!-- 父工程依赖 -->
<?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.0.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>SpringCloud</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>SpringCloud</name>
<description>SpringCloud</description>
<packaging>pom</packaging>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.49</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
==============================================================================
<!-- 子模块依赖 -->
<!-- nacos注册中心依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>0.2.2.RELEASE</version>
</dependency>
2)producer和consumer配置文件
spring:
application:
name: producer
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
server:
port: 8083
=============================================================================
spring:
application:
name: consumer
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
server:
port: 8082
3)producer的controller和启动类
package com.example.producer.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Slf4j
public class ProducerController {
@Value("${server.port}")
private String port;
@RequestMapping("/getMember")
public String getMember() {
return "The port of producer is "+port;
}
}
=============================================================================
package com.example.producer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
//@EnableDiscoveryclient
public class ProducerApplication {
public static void main(String[] args) {
SpringApplication.run(ProducerApplication.class, args);
}
}
4)consumer的controller和启动类
//一个客户端上的类,用于搜索服务
@Autowired
private DiscoveryClient discoveryClient;
//远程调用template模板
@Autowired
private RestTemplate restTemplate;
/**
* 消费端 调用 生产端接口 http://127.0.0.1:8082/orderToMember2
*/
@RequestMapping("/orderToMember2")
public String orderToMember2() {
// HttpClient 工具类 实现RPC远程调用
// String memberUrl = "http://127.0.0.1:8082/getMember";
/**
* 根据服务名称 从注册中心 获取 会员的接口地址
* 服务提供 启动多个集群
*/
List<ServiceInstance> instances = discoveryClient.getInstances("producer");
ServiceInstance serviceInstance = instances.get(0);
// 会员服务的ip和端口
String memberUrl = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/" + "getMember";
return "订单服务调用会员服务:" + restTemplate.getForObject(memberUrl, String.class);
}
===========================================================================
package com.example.consumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
//远程调用工具
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
6、Nacos作为配置中心
1)首先在nacos发布配置。
浏览器访问 http://127.0.0.1:8848/nacos ,打开nacos控制台,并点击菜单配置管理->配置列表:
在Nacos添加如下的配置:
注意dataid是以properties(默认的文件扩展名方式)为扩展名,这里使用yml。
2)需要在pom.xml中添加如下依赖
<!-- nacos配置中心依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>0.2.2.RELEASE</version>
</dependency>
3)创建bootstrap.yml文件配置
server:
port: 8084
spring:
application:
###服务的名称
name: nacos-config
cloud:
nacos:
discovery:
###nacos注册地址
server-addr: 127.0.0.1:8848
enabled: true
config:
###配置中心连接地址
server-addr: 127.0.0.1:8848
###分组
group: DEFAULT_GROUP
###类型
file-extension: yaml
###执行访问的远程配置文件的名称 指的是data id
# name: nacos-config
#采用引用 项目名为远程配置中心的名称的方式进行指定配置文件名称
prefix: ${spring.application.name}
注意:连接nacos分布式配置中心一定采用bootstrap形式优先加载 否则可能会报错。
bootstrap.yml 用于应用程序上下文的引导阶段。
application.yml 由父Spring ApplicationContext加载。
4)第一种方式:NacosController
package com.example.nacosconfig.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@SpringBootApplication
@RefreshScope //使当前类下的配置支持动态更新
public class NacosController {
@Value("${boyatop.name}")
private String userName;
@RequestMapping("/getConfig")
public String getConfig() {
return userName;
}
public static void main(String[] args) {
SpringApplication.run(NacosController.class);
}
}
5) 第二种方式:NacosClient
package com.example.nacosconfig;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.exception.NacosException;
import java.util.Properties;
public class NacosClient {
public static void main(String[] args) throws NacosException, InterruptedException {
String serverAddr = "127.0.0.1";
String dataId = "nacos-config.yaml";
String group = "DEFAULT_GROUP";
Properties properties = new Properties();
properties.put(PropertyKeyConst.SERVER_ADDR, serverAddr);
ConfigService configService = NacosFactory.createConfigService(properties);
String content = configService.getConfig(dataId, group, 5000);
System.out.println(content);
}
}
远程配置中心注意总结:
1.本地的配置文件必须为bootstrap.yml或者bootstrap.properties
2.本地的配置文件中的后缀只有两种写法file-extension: properties或者yml
3.本地需要指定远程的配置文件的名称 有两种形式
name具体指定配置中心叫什么这里些什么
prefix引用的方式 应用本地项目名称 此时应当保证项目名称和配置中心dataId名称一致
4. 远程配置文件书写规范dataId和配置文件的类型选择保持一致
5. 本地编写远程配置文件名称的时候执行步骤3的时候名称只写.之前的名称
7、Nacos配置管理
1)配置介绍
对于Nacos配置管理,通过Namespace、group、Data ID能够定位到一个配置集
配置集(Data ID)
在系统中,一个配置文件通常就是一个配置集,一个配置集可以包含了系统的各种配置信息,例如,一个配置集可能包含了数据源、线程池、日志级别等配置项。每个配置集都可以定义一个有意义的名称,就是配置集的ID即Data ID。
配置项
配置集中包含的一个个配置内容就是配置项。它代表一个具体的可配置的参数与其值域,通常以key=value 的形式存在。例如我们常配置系统的日志输出级别(logLevel=INFO|WARN| ERROR) 就是一个配置项。
配置分组(Group)
配置分组是对配置集进行分组,通过一个有意义的字符串(如 Buy 或 Trade )来表示,不同的配置分组下可以有相同的配置集(Data ID)。当您在 Nacos 上创建一个配置时,如果未填写配置分组的名称,则配置分组的名称默认采用 DEFAULT_GROUP 。配置分组的常见场景:可用于区分不同的项目或应用,例如:订单系统的配置集
可以定义一个group为:ORDER_GROUP。
命名空间(Namespace)
命名空间(namespace)可用于进行不同环境的配置隔离。例如可以隔离开发环境、测试环境和生产环境,因为它们的配置可能各不相同,或者是隔离不同的用户,不同的开发人员使用同一个nacos管理各自的配置,可通过namespace隔离。不同的命名空间下,可以存在相同名称的配置分组(Group) 或配置集。
最佳实践
Nacos抽象定义了Namespace、Group、Data ID的概念,具体这几个概念代表什么,取决于我们把它们看成什么,这里推荐给大家一种用法,如下图:
Namespace:代表不同环境,如开发、测试、生产环境。
Group:代表某微服务中的工程
DataId:每个项目下往往有若干个工程,每个配置集(DataId)是一个工程的主配置文件
2)多环境下Nacos文件配置
① Data ID方案
bootstrap.yml文件添加 spring.profiles.active
spring:
application:
###服务的名称
name: nacos-config
cloud:
nacos:
discovery:
###nacos注册地址
server-addr: 127.0.0.1:8848
enabled: true
config:
###配置中心连接地址
server-addr: 127.0.0.1:8848
###分组
group: DEFAULT_GROUP
###类型
file-extension: yaml
###执行访问的远程配置文件的名称 指的是data id
# name: nacos-config
#采用引用 项目名为远程配置中心的名称的方式进行指定配置文件名称
prefix: ${spring.application.name}
profiles:
active: prod
创建配置文件Data ID为:nacos-config-dev.yml, 其配置如下:
server:
port: 9980
nacos:
config: 这里是dev环境
创建配置文件Data ID为:nacos-config-prod.yml, 其配置如下:
server:
port: 9981
nacos:
config: 这里是prod环境
切换active,启动服务,我们发现端口的切换效果。
通过上面的配置,将项目分为dev、prod两个环境启动后,进行测试
访问 http://127.0.0.1:9980/getValue 返回:这里是dev环境
访问 http://127.0.0.1:9981/getValue 返回:这里是prod环境
② Group方案
创建配置文件Data ID为:nacos-config.yml, Group为:DEV_GROUP 其配置如下:
server:
port: 9980
nacos:
config: 这里是dev环境
创建配置文件Data ID为:nacos-config.yml, Group为:TEST_GROUP其配置如下:
server:
port: 9981
nacos:
config: 这里是prod环境
修改bootstrap.yml
spring:
application:
name: nacos-config
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
config:
server-addr: 127.0.0.1:8848
prefix: ${spring.application.name}
file-extension: yml
group: TEST_GROUP
切换group,启动服务,我们发现端口的切换效果。
通过上面的配置,将项目分为DEV_GROUP、TEST_GROUP两个环境启动后,进行测试
访问 http://127.0.0.1:9980/getValue 返回:这里是dev环境
访问 http://127.0.0.1:9981/getValue 返回:这里是prod环境
只通过Group来进行多环境的区分的方式我不推荐使用,因为涉及到了多环境自然就会改变spring.profile.active,而profile一旦生效,配置文件就会依据DataID的规则进行查找。所以Group的方式仅作参考。
Group的合理用法应该是配合namespace进行服务列表和配置列表的隔离和管理
③ Namespace方案
Namespace命名空间进行环境隔离也是官方推荐的一种方式。Namespace的常用场景之一是不同环境的配置的区分隔离,例如:开发测试环境和生产环境的资源(如配置、服务)隔离等。
创建命名空间DEV和TEST,不同的命名空间会生成相应的UUID,如下图
在DEV命名空间下 创建配置文件Data ID为:nacos-config.yml, 其配置如下:
erver:
port: 9980
nacos:
config: 这里是dev环境
在TEST命名空间下创建配置文件DataID为:nacos-config.yml,其配置如下:
server:
port: 9981
nacos:
config: 这里是test环境
修改bootstrap.yml
spring:
application:
name: nacos-config
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
config:
server-addr: 127.0.0.1:8848
prefix: ${spring.application.name}
file-extension: yml
namespace: 6c339ea2-cb77-4f2c-843e-aefe8315bf6f
将namespace配置为DEV的ID: 6c339ea2-cb77-4f2c-843e-aefe8315bf6f,启动进行测试
访问 http://127.0.0.1:9980/getValue 返回:这里是DEV命名空间
将namespace配置为TEST的ID: 6c339ea2-cb77-4f2c-843e-aefe8315bf6f,启动进行测试
访问 http://127.0.0.1:9981/getValue 返回:这里是TEST命名空间
通过指定namespace的方式启动,均可读取到对应的启动端口和相关配置
Namespace是官方推荐的环境隔离方案,确实有他的独到之处,使用namespace这种方案,同时可以与DataID+profile的方式结合
同时释放Group的限制,大大提高多环境配置管理的灵活性。
④ 总结
DataID: 适用于项目不多,服务量少的情况。
Group:实现方式简单,但是容易与DataID方案发生冲突,仅适合于本地调试
Namespace:实现方式简单,配置管理简单灵活,同时可以结合DataID共同使用,推荐这种方案
8、Nacos共享配置
一个项目中服务数量增加后,配置文件相应增加,多个配置文件中会存在相同的配置,那么我们可以将相同的配置独立出来,作为该项目中各个服务的共享配置文件,每个服务都可以通过Nacos进行共享配置的读取
下面用一个demo演示下,是否可行
demo工程:nacos-config-share
配置文件:nacos-config-share.yml
共享配置文件:shareconfig1.yml,shareconfig2.yml
1)创建项目
在聚合工程Nacos下创建名为nacos-config-share的子工程,其pom.xml文件依赖与之前的项目都一致
1、 修改springboot启动类NacosConfigShareApplication.java
package com.example.nacosconfigshare;
import com.alibaba.nacos.client.config.impl.LocalConfigInfoProcessor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@RestController
@RefreshScope
@EnableDiscoveryClient
public class NacosConfigShareApplication {
//LocalConfigInfoProcessor
public static void main(String[] args) {
SpringApplication.run(NacosConfigShareApplication.class, args);
}
@Value("${nacos.share}")
private String share;
@Value("${share.config1}")
private String shareConfig1;
@Value("${share.config2}")
private String shareConfig2;
@RequestMapping("/getValue")
public String getValue() {
return share;
}
@RequestMapping("/getShare1")
public String getShare1() {
return shareConfig1;
}
@RequestMapping("/getShare2")
public String getShare2() {
return shareConfig2;
}
}
2)创建配置文件bootstrap.yml
spring:
application:
name: nacos-config-share
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
config:
server-addr: 127.0.0.1:8848
prefix: ${spring.application.name}
file-extension: yml
shared-dataids: shareconfig1.yml,shareconfig2.yml
refreshable-dataids: shareconfig1.yml,shareconfig2.yml
从配置文件可以看出,通过shared-dataids属性来指定要读取共享配置文件的DataID,多个文件用,分隔使用refreshable-dataids指定共享配置文件支持自动刷新。
3)创建nacos配置文件
创建配置文件nacos-config-share.yml
创建配置文件shareconfig1.yml
4)启动测试
从日志中我们可以看到配置文件的加载
访问启动类中提供的接口,测试下能否获取到共享配置文件中的值
访问127.0.0.1:9984/getValue,返回:nacos-config-share
访问127.0.0.1:9984/getShare1,返回:这里是共享配置文件1
访问127.0.0.1:9984/getShare2,返回:这里是共享配置文件2
再测试下refreshable-dataids配置的自动刷新是否生效
在Nacos控制台中修改共享配置文件shareconfig2.yml的值为:这里是共享配置文件2update
编辑保存后,重新请求 127.0.0.1:9984/getShare2 ,观察返回结果如下:
以上返回结果说明通过在配置文件中指定shared-dataids和refreshable-dataids是可以实现共享配置文件的读取和自动刷新的。
5)需求变更:不同分组
假设现在要读取shareconfig3.yml和shareconfig4.yml文件但是它的Group为SHARE3_GROUP和SHARE4_GROUP, 即共享配置文件与项目自身配置文件不在同一Group中(上边的例子是全都在DEFAULT_GROUP分组) 那如果继续用上边的方法,就无法读取共享配置文件
这时可以使用另一个配置ext-config,它可以由用户自定义指定需要加载的配置DataID、Group以及是否自动刷新并且ext-config是一个集合(List),支持多个配置文件的指定。
6)新建Nacos配置文件
先创建配置配置文件shareconfig3.yml和shareconfig4.yml,注意他们的Group属性
7)启动类NacosConfigShareApplication.java中新增如下代码
@Value("${share.config3}")
private String shareConfig3;
@Value("${share.config4}")
private String shareConfig4;
@RequestMapping("/getShare3")
public String getShare3() {
return shareConfig3;
}
@RequestMapping("/getShare4")
public String getShare4() {
return shareConfig4;
}
8) bootstrap.yml添加ext-config配置
spring:
application:
name: nacos-config-share
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
config:
server-addr: 127.0.0.1:8848
prefix: ${spring.application.name}
file-extension: yml
shared-dataids: shareconfig1.yml,shareconfig2.yml
refreshable-dataids: shareconfig1.yml,shareconfig2.yml
ext-config:
- data-id: shareconfig3.yml
group: SHARE3_GROUP
refresh: true
- data-id: shareconfig4.yml
group: SHARE4_GROUP
refresh: true
9) 启动测试
项目经过修改后,可以看到项目自身的nacos配置文件属于DEFAULT_GROUP下,默认读取
shareconfig1.yml,shareconfig2.yml 都属于DEFAULT_GROUP下,通过shared-dataids指定进行读取
shareconfig3.yml,shareconfig4.yml 都属于非DEFAULT_GROUP下,通过ext-config配置属性进行自定义读取
启动项目,测试所有的配置文件是否可以正常读取
访问127.0.0.1:9984/getValue,返回:nacos-config-share
访问127.0.0.1:9984/getShare1,返回:这里是共享配置文件1
访问127.0.0.1:9984/getShare2,返回:这里是共享配置文件2这里是共享配置文件2
访问127.0.0.1:9984/getShare3,返回:这里是共享配置文件3,Group:SHARE3_GROUP
访问127.0.0.1:9984/getShare4,返回:这里是共享配置文件4,Group:SHARE4_GROUP
修改shareconfig4.yml的配置内容为:这里是共享配置文件4,Group:SHARE4_GROUP update,保存后,再次调用127.0.0.1:9984/getShare4,返回如下:
调用接口后发现,两种共享配置的加载方式都可以正常读取,并且可以一起使用。ext-config的方式实现了用户自定义配置共享配置文件。
10)总结
shared-dataids方式:
适合于共享配置文件与项目默认配置文件处于相同Group时,直接两条命令就可以搞定
优点:配置方便
缺点:只能在同一Group中
ext-config方式:
它可以由开发者自定义要读取的共享配置文件的DataId、Group、refresh属性,这样刚好解决了shared-dataids存在的局限性。
优点:可以与shared-dataids方案结合使用,用户自定义配置。灵活性强
缺点:配置容易出错,要熟悉YAML语法