摘要: 原创出处 http://www.iocoder.cn/Spring-Cloud-Alibaba/Nacos-Discovery/ 「芋道源码」欢迎转载,保留摘要,谢谢!

  • 1. 概述
  • 2. 注册中心原理
  • 3. 快速入门
  • 4. Nacos 概念详解
  • 5. 更多的配置项信息
  • 6. 多环境配置
  • 7. 监控端点
  • 666. 彩蛋

本文在提供完整代码示例,可见 https://github.com/YunaiV/SpringBoot-Labs 的 labx-01 目录。

原创不易,给点个 Star 嘿,一起冲鸭!

1. 概述

本文我们来学习 Spring Cloud Alibaba 提供的 Spring Cloud Alibaba Nacos Discovery 组件,基于 Spring Cloud 的编程模型,接入 Nacos 作为注册中心,实现服务的注册与发现。

服务注册/发现: Nacos Discovery

服务发现是微服务架构体系中最关键的组件之一。如果尝试着用手动的方式来给每一个客户端来配置所有服务提供者的服务列表是一件非常困难的事,而且也不利于服务的动态扩缩容。

Nacos Discovery 可以帮助您将服务自动注册到 Nacos 服务端并且能够动态感知和刷新某个服务实例的服务列表。

除此之外,Nacos Discovery 也将服务实例自身的一些元数据信息-例如 host,port, 健康检查URL,主页等内容注册到 Nacos。

在开始本文之前,胖友需要对 Nacos 进行简单的学习。可以阅读《Nacos 安装部署》文章,将第一二小节看完,在本机搭建一个 Nacos 服务。

2. 注册中心原理

在开始搭建 Nacos Discovery 的示例之前,我们先来简单了解下注册中心的原理。

在使用注册中心时,一共有三种角色:服务提供者(Service Provider)、服务消费者(Service Consumer)、注册中心(Registry)。

在一些文章中,服务提供者被称为 Server,服务消费者被称为 Client。胖友们知道即可。

三个角色交互如下图所示:

  • Provider:

    • 启动时,向 Registry 注册自己为一个服务(Service)的实例(Instance)。
    • 同时,定期向 Registry 发送心跳,告诉自己还存活。
    • 关闭时,向 Registry 取消注册
  • Consumer:

    • 启动时,向 Registry 订阅使用到的服务,并缓存服务的实例列表在内存中。
    • 后续,Consumer 向对应服务的 Provider 发起调用时,从内存中的该服务的实例列表选择一个,进行远程调用。
    • 关闭时,向 Registry 取消订阅
  • Registry:

    • Provider 超过一定时间未心跳时,从服务的实例列表移除。
    • 服务的实例列表发生变化(新增或者移除)时,通知订阅该服务的 Consumer,从而让 Consumer 能够刷新本地缓存。

当然,不同的注册中心可能在实现原理上会略有差异。例如说,Eureka 注册中心,并不提供通知功能,而是 Eureka Client 自己定期轮询,实现本地缓存的更新。

另外,Provider 和 Consumer 是角色上的定义,一个服务同时即可以是 Provider 也可以作为 Consumer。例如说,优惠劵服务可以给订单服务提供接口,同时又调用用户服务提供的接口。

3. 快速入门

示例代码对应仓库:

  • 服务提供者:labx-01-sca-nacos-discovery-demo01-provider
  • 服务消费者:labx-01-sca-nacos-discovery-demo01-consumer

本小节,我们来搭建一个 Nacos Discovery 组件的快速入门示例。步骤如下:

  • 首先,搭建一个服务提供者 demo-provider ,注册服务到 Nacos 中。
  • 然后,搭建一个服务消费者 demo-consumer,从 Nacos 获取到demo-provider 服务的实例列表,选择其中一个示例,进行 HTTP 远程调用。

3.1 搭建服务提供者

创建 labx-01-sca-nacos-discovery-demo01-provider 项目,作为服务提供者 demo-provider。最终项目代码如下图所示:

3.1.1 引入依赖

在 pom.xml 文件中,主要引入 Spring Cloud Nacos Discovery 相关依赖。代码如下:

<?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">

    <parent>
        <artifactId>labx-01</artifactId>
        <groupId>cn.iocoder.springboot.labs</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>labx-01-sca-nacos-discovery-demo01-provider</artifactId>

    <properties>
        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>
        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>
        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>
    </properties>

    <!--
        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件,进行依赖版本的管理,防止不兼容。
        在 https://dwz.cn/mcLIfNKt 文章中,Spring Cloud Alibaba 开发团队推荐了三者的依赖关系
     -->

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-parent</artifactId>
                <version>${spring.boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring.cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring.cloud.alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <!-- 引入 SpringMVC 相关依赖,并实现对其的自动配置 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖,将 Nacos 作为注册中心,并实现对其的自动配置 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
    </dependencies>

</project>

友情提示:有点小长,不要慌~

在 <dependencyManagement /> 中,我们引入了 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件,进行依赖版本的管理,防止不兼容。在《Spring Cloud 官方文档 —— 版本说明》文档中,推荐了三者的依赖关系。如下表格:

Spring Cloud VersionSpring Cloud Alibaba VersionSpring Boot Version
Spring Cloud Greenwich2.1.1.RELEASE2.1.X.RELEASE
Spring Cloud Finchley2.0.1.RELEASE2.0.X.RELEASE
Spring Cloud Edgware1.5.1.RELEASE1.5.X.RELEASE
  • 这里,我们选择了 Spring Cloud Alibaba 版本为2.2.0.RELEASE
  • 当前版版本下,我们使用的 Nacos 版本为 1.1.4

引入 spring-cloud-starter-alibaba-nacos-discovery 依赖,将 Nacos 作为注册中心,并实现对它的自动配置。

3.1.2 配置文件

创建 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 配置项。

3.1.3 DemoProviderApplication

创建 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 应用。Spring Cloud 是构建在 Spring Boot 之上的,所以需要添加。

@EnableDiscoveryClient 注解,开启 Spring Cloud 的注册发现功能。不过从 Spring Cloud Edgware 版本开始,实际上已经不需要添加 @EnableDiscoveryClient 注解,只需要引入 Spring Cloud 注册发现组件,就会自动开启注册发现的功能。例如说,我们这里已经引入了 spring-cloud-starter-alibaba-nacos-discovery 依赖,就不用再添加 @EnableDiscoveryClient 注解了。

拓展小知识:在 Spring Cloud Common 项目中,定义了 DiscoveryClient 接口,作为通用的发现客户端,提供读取服务和读取服务列表的 API 方法。而想要集成到 Spring Cloud 体系的注册中心的组件,需要提供对应的 DiscoveryClient 实现类。

例如说,Spring Cloud Alibaba Nacos Discovery 提供了 NacosDiscoveryClient 实现,Spring Cloud Netflix Eureka 提供了 EurekaDiscoveryClient 实现。

如此,所有需要使用到的地方,只需要获取到 DiscoveryClient 客户端,而无需关注具体实现,保证其通用性。

TestController 类,提供了 /echo 接口,返回 provider:${name}结果。

3.1.4 简单测试

① 通过 DemoProviderApplication 启动服务提供者,IDEA 控制台输出日志如:

// ... 省略其它日志
2020-02-08 15:25:57.406  INFO 27805 --- [           main] c.a.c.n.registry.NacosServiceRegistry    : nacos registry, DEFAULT_GROUP demo-provider 10.171.1.115:18080 register finished
  • 服务 demo-provider 注册到 Nacos 上的日志。

② 打开 Nacos 控制台,可以在服务列表看到服务 demo-provider。如下图:

3.2 搭建服务消费者

创建 labx-01-sca-nacos-discovery-demo01-consumer 项目,作为服务提供者 demo-consumer。最终项目代码如下图所示:

整个项目的代码,和服务提供者是基本一致的,毕竟是示例代码 

3.2.1 引入依赖

和「3.1.1 引入依赖」一样,只是修改 Maven <artifactId /> 为 labx-01-sca-nacos-discovery-demo01-consumer,见 pom.xml 文件。

3.2.2 配置文件

创建 application.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

3.2.3 DemoConsumerApplication

创建 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 调用。

拓展小知识:在 Spring Cloud Common 项目中,定义了LoadBalancerClient 接口,作为通用的负载均衡客户端,提供从指定服务中选择一个实例、对指定服务发起请求等 API 方法。而想要集成到 Spring Cloud 体系的负载均衡的组件,需要提供对应的 LoadBalancerClient 实现类。

例如说,Spring Cloud Netflix Ribbon 提供了 RibbonLoadBalancerClient 实现。


如此,所有需要使用到的地方,只需要获取到 DiscoveryClient 客户端,而无需关注具体实现,保证其通用性。

接口,示例接口,对服务提供者发起一次 HTTP 调用。

  • <1> 处,获得服务 demo-provider 的一个实例。这里我们提供了两种方式的代码,分别基于 DiscoveryClient 和 LoadBalancerClient。
  • <2> 处,通过获取到的服务实例 ServiceInstance 对象,拼接请求的目标 URL,之后使用 RestTemplate 发起 HTTP 调用。

3.2.4 简单测试

① 通过 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
  • 服务 demo-consumer 注册到 Nacos 上的日志。

注意,服务消费者和服务提供是一种角色的概念,本质都是一种服务,都是可以注册自己到注册中心的。

② 打开 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 的实例列表已刷新,没有任何实例。

©著作权归作者所有:来自51CTO博客作者mb5ff80520dfa04的原创作品,如需转载,请注明出处,否则将追究法律责任

更多相关文章

  1. 分布式链路追踪 SkyWalking 源码分析 —— 应用于应用实例的注册
  2. 注册中心 Eureka源码解析 —— 应用实例注册发现 (九)之岁月是把萌
  3. 注册中心 Eureka 源码解析 —— 应用实例注册发现(一)之注册
  4. 注册中心 Eureka 源码解析 —— 应用实例注册发现(七)之增量获取
  5. 注册中心 Eureka 源码解析 —— 应用实例注册发现(六)之全量获取
  6. 注册中心 Eureka 源码解析 —— 应用实例注册发现 (四)之自我保护
  7. 注册中心 Eureka 源码解析 —— 应用实例注册发现 (三)之下线
  8. 数据会说谎的真实例子有哪些

随机推荐

  1. Oracle ------ SQLDeveloper中SQL语句格
  2. 【MySQL】配置MySQL安装和远程访问步骤
  3. TSQL - 非标准化表上的最大值或最高日期
  4. MySQL——关于MySQL分组查询group by和or
  5. linq to sql的一个问题
  6. MySQL5.7多源复制
  7. Spark SQL:如果是NULL处理。
  8. mysql index hint 在index不存在时的处理
  9. 数据库的认识+SQL
  10. MySQL5.7.10下载及安装及配置