什么是Nacos?
Nacos的核心定位是【一个更易于帮助构建云原生应用的动态服务发现、配置和服务管理平台】,本文基于Nacos2.0记录相关内容。
什么是服务治理?
服务治理是微服务架构中最核心最基本的模块,用于实现各个微服务的自动化注册与发现。
服务注册:在服务治理框架中,都会构建一个注册中心,每个服务单元像注册中心登记自己提供服务的详细信息。并在注册中心形成一张服务的清单,服务注册中心要去检测服务清单中的服务是否可用,不可用需要剔除。
服务发现:服务调用方向服务注册中心咨询服务,并获取所有服务的实例清单,实现对具体实例服务的访问。
Nacos Discovery 可以帮助我们将服务自动注册到 Nacos 服务端并且能够动态感知和刷新某个服务实例的服务列表。除此之外,Nacos Discovery 也将服务实例自身的一些元数据信息-例如 host,port, 健康检查URL,主页等内容注册到 Nacos。
Nacos安装
Nacos官网 点击版本 下载压缩包
然后以单机模式启动,这是windows的启动方式
startup.cmd -m standalone
如果在linux中以docker方式启动,则为【 docker run -d -p 8848:8848 -e MODE=standalone --name nacos-server 容器id】
启动成功后访问 http://localhost:8848/nacos 默认用户名和密码都是nacos,直接登录即可。
服务注册
我们尝试编写一个服务并注册到nacos中
父工程pom
<?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>org.example</groupId>
<artifactId>cloud-alibaba-nacos</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>provider8082</module>
<module>provider8083</module>
<module>comsumer9090</module>
</modules>
<properties>
<spring.boot.version>2.2.5.RELEASE</spring.boot.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- SpringBoot-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
然后创建子工程 支付服务提供者provider8082,pom中添加依赖如下:
<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>
<version>2.2.1.RELEASE</version>
</dependency>
</dependencies>
重点是引入nacos的依赖,引入依赖后编写配置文件:
server.port=8082
#nacos服务中心地址
spring.cloud.nacos.discovery.server-addr=Ip
#服务名
spring.cloud.nacos.discovery.service=provider
不需要@EnableDiscoveryClient注解
此时启动该服务,然后查看nacos的后台系统:
然后编写一个控制器用于模拟业务:
@RestController
public class PaymentController {
@Value("${server.port}")
private String port;
@GetMapping("/payment/nacos/{id}")
public String getPayment(@PathVariable("id") Integer id) {
return "nacos服务注册,端口号:" + port + "\t" + "id:" + id;
}
}
测试一下
业务正常。接下来创建第二个支付服务 provider-payment8083
,代码直接拷贝刚才的服务即可,只需修改端口号为8083,其它都一样。
启动payment8083,查看nacos后台:
服务消费
有了服务提供者之后,我们理应创建服务消费者来消费这些服务,当然,消费者也是要注册到nacos中的,创建服务 consumer9090,pom文件依然是引入nacos依赖,然后编写配置文件:
server.port=9090
#nacos服务中心地址
spring.cloud.nacos.discovery.server-addr=ip
#服务名
spring.cloud.nacos.discovery.service=consumer
既然是服务消费,那就少不了服务调用,但是nacos已经为我们集成了ribbon,当我们引入nacos依赖的时候,ribbon依赖也随之引入进来了,所以我们可以直接使用ribbon实现负载均衡,那么首先就需要将ribbon注册到容器中:
@Configuration
public class ConsumerConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
最后编写控制器
@RestController
public class OrderController {
/**
* 需要调用的服务
*/
public static final String SERVER_URL = "http://provider";
@Autowired
private RestTemplate restTemplate;
@GetMapping("/consumer/nacos/{id}")
public String paymentInfo(@PathVariable("id") Integer id) {
// 拼接请求地址
String reqUrl = SERVER_URL + "/payment/nacos/" + id;
return restTemplate.getForObject(reqUrl, String.class);
}
}
启动服务消费者,首先查看nacos后台:
服务注册成功, 测试一下业务代码,访问 localhost:9090/consumer/nacos/1
而且支持负载均衡,默认采用轮询策略。
使用OpenFeign
ribbon虽然能够实现客户端的负载均衡和服务调用,但是稍显麻烦,缺点也很明显,需要在Controller层调用方法请求另外一个服务的Controller方法。为此,我们可以使用OpenFeign来改进这一过程,OpenFeign集成了ribbon,它更侧重服务之间的调用,当然也默认支持负载均衡。
OpenFeign提供了两个重要标注@FeignClient和@EnableFeignClients。
- @FeignClient标注用于声明Feign客户端可访问的Web服务。
- @EnableFeignClients标注用于修饰Spring Boot应用的入口类,以通知Spring Boot启动应用时,扫描应用中声明的Feign客户端可访问的Web服务。
首先修改子项目consumer9090中的pom文件,添加依赖:
<!-- org.springframework.cloud/spring-cloud-starter-openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
在启动类上添加注解
然后编写一个接口,在该接口上标注 @FeignClient("provider")
注解,其中值为需要远程调用的服务名,在接口中定义方法,一般来说,方法的声明与需要调用的方法声明一致。
@FeignClient("provider")
public interface PaymentService {
@GetMapping("/payment/nacos/{id}")
public String getPayment(@PathVariable("id") Integer id);
}
然后改写order控制层 把接口注入进来,直接调用接口中的方法即可。
@RestController
public class OrderController {
/**
* 需要调用的服务
*/
// public static final String SERVER_URL = "http://provider";
//
// @Autowired
// private RestTemplate restTemplate;
//
// @GetMapping("/consumer/nacos/{id}")
// public String paymentInfo(@PathVariable("id") Integer id) {
// // 拼接请求地址
// String reqUrl = SERVER_URL + "/payment/nacos/" + id;
// return restTemplate.getForObject(reqUrl, String.class);
// }
@Autowired
private PaymentService paymentService;
@GetMapping("/consumer/nacos/{id}")
public String paymentInfo(@PathVariable("id") Integer id){
return paymentService.getPayment(id);
}
}
启动项目,测试业务
使用Nacos作为服务配置中心
Nacos 提供用于存储配置和其他元数据的 key/value 存储,为分布式系统中的外部化配置提供服务器端和客户端支持。
在特殊的 bootstrap 阶段,配置被加载到 Spring 环境中。当应用程序通过部署管道从开发到测试再到生产时,我们可以管理这些环境之间的配置,并确保应用程序具有迁移时需要运行的所有内容。
项目一般都会有多个Profile配置,用于区分开发环境,测试环境,准生产环境,生成环境等,每个环境对应一个properties文件(或是yml/yaml文件),然后通过设置 spring.profiles.active 的值来决定使用哪个配置文件
Nacos Config的作用就把这些文件的内容都移到一个统一的配置中心,即方便维护又支持实时修改后动态刷新应用
Data ID的拼接格式:${prefix} - ${spring.profiles.active} . ${file-extension}
-
prefix 默认为 spring.application.name 的值,也可以通过配置项 spring.cloud.nacos.config.prefix 来配置
-
spring.profiles.active 取 spring.profiles.active 的值,即为当前环境对应的 profile
-
file-extension 为配置内容的数据格式,可以通过配置项 spring.cloud.nacos.config.file-extension 来配置
这里我们新建一个子项目 nacos-config2001
pom中的依赖为
<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>
<version>2.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
这里的配置文件有讲究,我们需要创建一个名为 bootstrap.properties[或者bootstrap.yml]
的配置文件,这是因为我们需要优先去配置中心获取所需的配置,当配置中心没有配置时,才使用本地的配置,而bootstrap.properties配置文件的优先级高于其它命名的配置文件,故需将配置写在bootstrap.properties中。
server.port=2001
#nacos服务中心地址
spring.cloud.nacos.discovery.server-addr=192.168.XXX
#nacos配置中心地址
spring.cloud.nacos.config.server-addr=192.168.XXX
#指定配置文件格式
#spring.cloud.nacos.config.file-extension=yaml
#DataId前缀
spring.cloud.nacos.config.prefix=nacosconfig
#服务名
spring.cloud.nacos.discovery.service=nacos-config2001
这里有一行指定配置文件格式的,因为默认是properties,所以如果用yml就需要配置一下。
编写一个接口测试一下
@RestController
public class ConfigController {
@Value("${config.msg}")
private String msg;
@GetMapping("/config/msg")
public String getMsg() {
return msg;
}
}
在nacos控制台的配置列表中新增配置
然后点击发布
启动项目,开始测试
然后我们修改配置,看是否可以生效
发现并未达到我们想要的效果。
Nacos Confg 支持标准 Spring Cloud @RefreshScope特性,即应用订阅某个 Nacos 配置后,当配置内容变化时,Refresh Scope Beans 中的绑定配置的属性将有条件的更新。
所谓的条件是指 Bean 必须:
- 必须条件:Bean 的声明类必须标注
@RefreshScope
- 二选一条件:
- 属性(非 static 字段)标注
@Value
@ConfigurationProperties
Bean
- 属性(非 static 字段)标注
在类上添加@RefreshScope 注解 就可以实现在nacos配置中心里修改配置后,就能够立马在项目中生效,无需重新启动项目。
这里需要注意的是当 Nacos Config 接收到服务端配置变更时,对应的 @RefreshScope
Bean 生命周期回调方法会被调用,并且是先销毁,然后又重新初始化。
配置管理
nacos作为配置中心,除了能够提供配置外,它还具有非常强大的功能,比如命名空间、配置分组等等,首先说说命名空间。命名空间是用来区分部署环境的,一个项目往往需要经历开发、测试、维护三个阶段,每个阶段的配置内容可能不尽相同,为此,可以创建三个命名空间来分别接管这三个阶段的配置;默认情况下会有一个 public
命名空间。然后是Data ID,前面我们已经了解过Data ID的组成结构,所以我们可以直接通过Data ID的不同来区分不同环境的配置
此时我们就能通过修改:
spring.profiles.active=dev
来分别获取三个配置文件的配置。
接下来我们再来看看分组,默认情况下我们会有一个 DEFAULT_GROUP
分组,新建的配置文件都会被存放在该分组下,通过分组我们也能够区分部署环境的配置信息:
这三个配置文件名相同,但是分组分别属于开发、测试和生产环境,然后通过 group
属性指定即可:
spring.cloud.nacos.config.group=DEV_GROUP
最后是命名空间,通过命名空间,我们仍然能够实现同样的效果:
创建好命名空间后,nacos会为每个命名空间分配id
此时服务会去寻找该命名空间下的指定分组的配置文件,若有环境指定,也需要加上,然后在nacos中新建配置文件:
spring.cloud.nacos.config.group=DEV_GROUP
spring.cloud.nacos.config.namespace=531bd39b-92b0-4bd9-b1f1-f32333c62ba5
在指定命名空间下创建配置文件即可。
更多关于 Nacos Config Starter 的配置项如下所示:
配置项 | Key | 默认值 | 说明 |
---|---|---|---|
服务端地址 | spring.cloud.nacos.config.server-addr | Nacos Server 启动监听的ip地址和端口 | |
配置对应的 DataId | spring.cloud.nacos.config.name | 先取 prefix,再取 name,最后取 spring.application.name | |
配置对应的 DataId | spring.cloud.nacos.config.prefix | 先取 prefix,再取 name,最后取 spring.application.name | |
配置内容编码 | spring.cloud.nacos.config.encode | 读取的配置内容对应的编码 | |
GROUP | spring.cloud.nacos.config.group | DEFAULT_GROUP | 配置对应的组 |
文件扩展名 | spring.cloud.nacos.config.fileExtension | properties | 配置项对应的文件扩展名,目前支持 properties 和 yaml(yml) |
获取配置超时时间 | spring.cloud.nacos.config.timeout | 3000 | 客户端获取配置的超时时间(毫秒) |
接入点 | spring.cloud.nacos.config.endpoint | 地域的某个服务的入口域名,通过此域名可以动态地拿到服务端地址 | |
命名空间 | spring.cloud.nacos.config.namespace | 常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等 | |
AccessKey | spring.cloud.nacos.config.accessKey | 当要上阿里云时,阿里云上面的一个云账号名 | |
SecretKey | spring.cloud.nacos.config.secretKey | 当要上阿里云时,阿里云上面的一个云账号密码 | |
Nacos Server 对应的 context path | spring.cloud.nacos.config.contextPath | Nacos Server 对外暴露的 context path | |
集群 | spring.cloud.nacos.config.clusterName | 配置成Nacos集群名称 | |
共享配置 | spring.cloud.nacos.config.sharedDataids | 共享配置的 DataId, "," 分割 | |
共享配置动态刷新 | spring.cloud.nacos.config.refreshableDataids | 共享配置中需要动态刷新的 DataId, "," 分割 | |
自定义 Data Id 配置 | spring.cloud.nacos.config.extConfig | 属性是个集合,内部由 Config POJO 组成。Config 有 3 个属性,分别是 dataId , group 以及 refresh |
集群模式
3个或3个以上Nacos节点才能构成集群。在本地搭建的伪集群
配置集群配置文件
在nacos的解压目录nacos/的conf目录下,有配置文件cluster.conf,请每行配置成ip:port。(请配置3个或3个以上节点)
把【conf】目录下的sql文件导入数据库
【conf】目录下集群配置文件,把该文件后缀名【.example】去掉之后,修改该文件中的内容
请每行配置成ip:port。(请配置3个或3个以上节点)
接着修改【application.properties】文件中的db信息
把nacos目录复制两份,目录名添加对应的端口号,把每个服务中【conf】目录下的【application.properties】文件中的端口号修改
把这三个服务依次启动即可