服务发现是微服务架构体系中最关键的组件之一。如果尝试用手动的方式来给每一个客户端来配置所有服务提供者的服务列表是一件非常困难的事,而且也不利于服务的动态扩缩容。
Nacos Discovery 可以帮助您将服务自动注册到 Nacos 服务端并且能够动态感知和刷新某个服务实例的服务列表。
除此之外,Nacos Discovery 也将服务实例自身的一些元数据信息-例如 host,port, 健康检查URL,主页等内容注册到 Nacos。
Nacos是阿里巴巴开源的一个对微服务架构中服务发现,配置管理和服务管理平台,由于第一代SpringCloud也就是SpringCloud Netflix很多组件已经进入停更维护模式,所以迫使我们必须要找到一个可以代替Netflix的第二代产品,这时候SpringCloud Alibaba出现了。
Nacos就是注册中心 + 配置中心的结合体
在使用注册中心时,一共有三种角色:服务提供者(Service Provider)、服务消费者(Service Consumer)、注册中心(Registry)。
一般服务提供者被称为Server,服务消费者被称为Client。
1)服务提供者Provider:
2)服务消费者Consumer:
3)注册中心Registry
另外,Provider 和 Consumer 是角色上的定义,**一个服务同时即可以是 Provider 也可以作为 Consumer。**例如说,优惠劵服务可以给订单服务提供接口,同时又调用用户服务提供的接口
示例代码对应仓库:
* 服务提供者:labx-01-sca-nacos-discovery-demo01-provider
* 服务消费者:labx-01-sca-nacos-discovery-demo01-consumer
本小节,我们来搭建一个 Nacos Discovery 组件的快速入门示例。步骤如下:
创建 labx-01-sca-nacos-discovery-demo01-provider 项目,作为服务提供者 demo-provider。最终项目代码如下图所示:
在 pom.xml 文件中,主要引入 Spring Cloud Nacos Discovery 相关依赖。代码如下:
<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">
<parent>
<artifactId>labx-01artifactId>
<groupId>cn.iocoder.springboot.labsgroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>labx-01-sca-nacos-discovery-demo01-providerartifactId>
<properties>
<spring.boot.version>2.2.4.RELEASEspring.boot.version>
<spring.cloud.version>Hoxton.SR1spring.cloud.version>
<spring.cloud.alibaba.version>2.2.0.RELEASEspring.cloud.alibaba.version>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>${spring.boot.version}version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring.cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>${spring.cloud.alibaba.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
dependencies>
project>
引入 spring-cloud-starter-alibaba-nacos-discovery 依赖,将 Nacos 作为注册中心,并实现对它的自动配置。
引入spring-cloud-starter-alibaba-nacos-discovery 依赖,它的内部同时也引入了ribbon
@LoadBalanced注解
创建 application.yaml 配置文件,添加 Nacos Discovery 配置项。配置如下:
spring:
application:
name: demo-provider # Spring 应用名
cloud:
nacos:
# Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类
discovery:
server-addr: 127.0.0.1:8848 # Nacos 服务器地址
service: ${spring.application.name} # 注册到 Nacos 的服务名。默认值为 ${spring.application.name}。
server:
port: 18080 # 服务器端口。默认为 8080
重点看 spring.cloud.nacos.discovery 配置项,它是 Nacos Discovery 配置项的前缀,对应 NacosDiscoveryProperties 配置项.
创建 DemoProviderApplication 类,创建应用启动类,并提供 HTTP 接口。代码如下:
@SpringBootApplication
@EnableDiscoveryClient
public class DemoProviderApplication {
public static void main(String[] args) {
SpringApplication.run(DemoProviderApplication.class, args);
}
@RestController
static class TestController {
@GetMapping("/echo")
public String echo(String name) {
return "provider:" + name;
}
}
}
① @SpringBootApplication注解,被添加在类上,声明这是一个Spring Boot应用。SpringCloud是构建在SpringBoot之上的,所以需要添加。
② @EnableDiscoveryClient注解,开启Spring Cloud的注册发现功能,不过从 Spring Cloud Edgware 版本开始,实际上已经不需要添加 @EnableDiscoveryClient 注解,只需要引入 Spring Cloud 注册发现组件,就会自动开启注册发现的功能。例如说,我们这里已经引入了 spring-cloud-starter-alibaba-nacos-discovery 依赖,就不用再添加 @EnableDiscoveryClient 注解了。
③ TestController 类,提供了 /echo 接口,返回 provider:${name} 结果。
① 通过 DemoProviderApplication 启动服务提供者。
② 打开Nacos控制台,可以在服务列表看到服务demo-provider
创建 labx-01-sca-nacos-discovery-demo01-consumer 项目,作为服务提供者 demo-consumer。最终项目代码如下图所示:
整个项目的代码,和服务提供者是基本一致的,毕竟是示例代码。
和「3.1.1 引入依赖」一样。
创建yaml配置文件,添加相应配置项。配置如下:
spring:
application:
name: demo-consumer # Spring 应用名
cloud:
nacos:
# Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类
discovery:
server-addr: 127.0.0.1:8848 # Nacos 服务器地址
server:
port: 28080 # 服务器端口。默认为 8080
和「3.1.2 配置文件」基本一致,主要是将配置项目 spring.application.name 修改为 demo-consumer。
创建 DemoConsumerApplication 类,创建应用启动类,并提供一个调用服务提供者的 HTTP 接口。代码如下:
@SpringBootApplication
// @EnableDiscoveryClient
public class DemoConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(DemoConsumerApplication.class, args);
}
@Configuration
public class RestTemplateConfiguration {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
@RestController
static class TestController {
@Autowired
private DiscoveryClient discoveryClient;
@Autowired
private RestTemplate restTemplate;
@Autowired
private LoadBalancerClient loadBalancerClient;
@GetMapping("/hello")
public String hello(String name) {
// <1> 获得服务 `demo-provider` 的一个实例
ServiceInstance instance;
if (true) {
// 获取服务 `demo-provider` 对应的实例列表
List<ServiceInstance> instances = discoveryClient.getInstances("demo-provider");
// 选择第一个
instance = instances.size() > 0 ? instances.get(0) : null;
} else {
instance = loadBalancerClient.choose("demo-provider");
}
// <2> 发起调用
if (instance == null) {
throw new IllegalStateException("获取不到实例");
}
String targetUrl = instance.getUri() + "/echo?name=" + name;
String response = restTemplate.getForObject(targetUrl, String.class);
// 返回结果
return "consumer:" + response;
}
}
}
① @EnableDiscoveryClient 注解,因为已经无需添加,所以我们进行了注释,原因在上面已经解释过。
② RestTemplateConfiguration 配置类,创建 RestTemplate Bean。RestTemplate 是 Spring 提供的 HTTP 调用模板工具类,可以方便我们稍后调用服务提供者的 HTTP API。
③ TestController 提供了 /hello 接口,用于调用服务提供者的 /demo 接口。代码略微有几行,我们来稍微解释下哈。
discoveryClient 属性,DiscoveryClient 对象,服务发现客户端,上文我们已经介绍过。这里我们注入的不是 Nacos Discovery 提供的 NacosDiscoveryClient,保证通用性。未来如果我们不使用 Nacos 作为注册中心,而是使用 Eureka 或则 Zookeeper 时,则无需改动这里的代码。
loadBalancerClient 属性,LoadBalancerClient 对象,负载均衡客户端。稍后我们会使用它,从 Nacos 获取的服务 demo-provider 的实例列表中,选择一个进行 HTTP 调用。
/hello 接口,示例接口,对服务提供者发起一次 HTTP 调用。
① 通过 DemoConsumerApplication 启动服务消费者,IDEA 控制台输出日志如:
// ... 省略其它日志
2020-02-08 18:05:35.810 INFO 35047 --- [ main] c.a.c.n.registry.NacosServiceRegistry : nacos registry, DEFAULT_GROUP demo-consumer 10.171.1.115:28080 register finished
注意,服务消费者和服务提供是一种角色的概念,本质都是一种服务,都是可以注册自己到注册中心的。
② 打开 Nacos 控制台,可以在服务列表看到服务 demo-consumer。如下图:
③ 访问服务消费者的 http://127.0.0.1:28080/hello?name=yudaoyuanma 接口,返回结果为 “consumer:provider:yudaoyuanma”。说明,调用远程的服务提供者成功。
④ 打开 Nacos 控制台,可以在订阅者列表看到订阅关系。如下图:
⑤ 关闭服务提供者后,再次访问 http://127.0.0.1:28080/hello?name=yudaoyuanma 接口,返回结果为报错提示 “获取不到实例”,说明我们本地缓存的服务 demo-provider 的实例列表已刷新,没有任何实例。
Nacos 数据模型 Key 由三元组唯一确认。如下图所示:
用于进行租户粒度的配置隔离。默认为 public(公共命名空间)。
不同的命名空间下,可以存在相同的 Group 或 Data ID 的配置。Namespace 的常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。
不同的服务可以归类到统一分组。默认为DEFAULT_GROUP(默认分组).
例如说,用户服务、订单服务、商品服务等等。
同一个服务,我们会部署到开发、测试、预发布、生产等环境中,那么我们需要在项目中,添加不同环境的 Nacos 配置。一般情况下,开发和测试使用同一个 Nacos,预发布和生产使用另一个 Nacos。那么针对相同的 Nacos,我们怎么实现不同环境的隔离呢?
实际上,Nacos 开发者已经告诉我们如何实现了,通过 Nacos Namespace 命名空间。文档说明如下:
下面,我们来搭建一个多环境配置的示例。步骤如下:
① 打开 Nacos UI 界面的「命名空间」菜单,进入「命名空间」功能。如下图所示:
② 点击列表右上角的「新建命名空间」按钮,弹出「新建命名空间」窗口,创建一个 dev 命名空间。输入如下内容,并点击「确定」按钮,完成创建。如下图所示:
③ 重复该操作,继续创建一个 uat 命名空间。最终 dev 和 uat 信息如下图:
从「3.1 搭建服务提供者」小节的 labx-01-sca-nacos-discovery-demo01-provider 项目,复制出 labx-01-sca-nacos-discovery-demo02-provider 项目。然后在其上进行修改,方便搭建~
修改 application.yaml 配置文件,将 Nacos Discovery 配置项删除,稍后添加在不同环境的配置文件中。配置如下:
spring:
application:
name: demo-provider # Spring 应用名
server:
port: 18080 # 服务器端口。默认为 8080
创建开发环境使用的 application-dev.yaml 配置文件,增加 Namespace 为 dev 的 Nacos Discovery 配置项。配置如下:
spring:
cloud:
nacos:
# Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类
discovery:
server-addr: 127.0.0.1:8848 # Nacos 服务器地址
namespace: 14226a0d-799f-424d-8905-162f6a8bf409 # Nacos 命名空间 dev 的编号
创建测试环境使用的 application-uat.yaml 配置文件,增加 Namespace 为 uat 的 Nacos Discovery 配置项。配置如下:
spring:
cloud:
nacos:
# Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类
discovery:
server-addr: 127.0.0.1:8848 # Nacos 服务器地址
namespace: bc8c8c2d-bd85-42bb-ada3-1a8f940ceb20 # Nacos 命名空间 uat 的编号
下面,我们使用命令行参数进行 --spring.profiles.active 配置项,实现不同环境,读取不同配置文件。
① 先配置 --spring.profiles.active 为 dev,设置 DemoProviderApplication 读取 application-dev.yaml 配置文件。如下图所示:
之后通过 DemoProviderApplication 启动服务提供者。
② 打开 Nacos 控制台,可以在服务列表看到服务 demo-provider 注册在命名空间 dev 下。如下图:
从「3.2 搭建服务消费者」小节的 labx-01-sca-nacos-discovery-demo01-consumer 项目,复制出 labx-01-sca-nacos-discovery-demo02-consumer 项目。然后在其上进行修改,方便搭建~
修改 application.yaml 配置文件,将 Nacos Discovery 配置项删除,稍后添加在不同环境的配置文件中。配置如下:
spring:
application:
name: demo-consumer # Spring 应用名
server:
port: 28080 # 服务器端口。默认为 8080
创建开发环境使用的 application-dev.yaml 配置文件,增加 Namespace 为 dev 的 Nacos Discovery 配置项。配置如下:
spring:
cloud:
nacos:
# Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类
discovery:
server-addr: 127.0.0.1:8848 # Nacos 服务器地址
namespace: 14226a0d-799f-424d-8905-162f6a8bf409 # Nacos 命名空间 dev 的编号
创建测试环境使用的 application-uat.yaml 配置文件,增加 Namespace 为 uat 的 Nacos Discovery 配置项。配置如下:
spring:
cloud:
nacos:
# Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类
discovery:
server-addr: 127.0.0.1:8848 # Nacos 服务器地址
namespace: bc8c8c2d-bd85-42bb-ada3-1a8f940ceb20 # Nacos 命名空间 uat 的编号
下面,我们使用命令行参数进行 –spring.profiles.active 配置项,实现不同环境,读取不同配置文件。
① 先配置 --spring.profiles.active 为 dev,设置 DemoConsumerApplication 读取 application-dev.yaml 配置文件。如下图所示:
之后通过 DemoConsumerApplication 启动服务消费者
访问服务消费者的 http://127.0.0.1:28080/hello?name=yudaoyuanma 接口,返回结果为 “consumer:provider:yudaoyuanma”。说明,调用远程的服务提供者【成功】。
② 再配置 --spring.profiles.active 为 uat,设置 DemoConsumerApplication 读取 application-uat.yaml 配置文件。如下图所示:
之后通过 DemoConsumerApplication 启动服务消费者。
访问服务消费者的 http://127.0.0.1:28080/hello?name=yudaoyuanma 接口,返回结果为 报错提示 “获取不到实例”。说明,调用远程的服务提供者【失败】。
原因是,虽然说服务 demo-provider 已经启动,因为其注册在 Nacos 的 Namespace 为 dev,这就导致第 ① 步启动的服务 demo-consumer 可以调用到该服务,而第② 步启动的服务 demo-consumer 无法调用到该服务。
即,我们可以通过 Nacos 的 Namespace 实现不同环境下的服务隔离。未来,在开源版本 Nacos 权限完善之后,每个 Namespace 提供不同的 AccessKey、SecretKey,保证只有知道账号密码的服务,才能连到对应的 Namespace,进一步提升安全性。