SpringCloudAlibaba之Nacos服务注册与发现

写在前面

本文参照Spring官方文档,写了相关代码 (源码地址),在此做下笔记;

官网关于 Nacos 的介绍是这样的:“ Nacos 是一个易于使用的动态服务发现、配置和服务管理平台,用于构建云本地应用程序”。一句话描述了 Nacos 能够实现的功能:服务发现配置服务治理

那么,我们先从基础的服务发现开始吧!

入门

因为后续我们将陆续使用到 Spring-Cloud-Alibaba 中的其它组件,所以,我们在顶层模块中引入如下的 POM 管理:

	<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.1.0.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

当然,不要忘了这个项目需要是 SpringBoot 项目,所以,我们之间让这个顶层模块归于 Spring-Boot 下,添加如下:

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.8.RELEASE</version>
    </parent>

以上是整个大项目的 POM 文件描述,以后各子模块均在该项目下创建。现在,让我们按学习的进度来建立各个子项目(子模块)。

启动 Nacos 服务

首先,我们需要 下载 Nacos 服务,进入 /bin 目录下,通过 startup.cmd 启动它。启动成功之后,访问 http://<ip>:8848/nacos 可查看控制台:

官网在这里描述为 http://ip:8848 ,但你访问这个地址会 404。我看了 nacos-server.jar 包中的配置文件,发现地址应为如上的形式。

nacos首页

启动 Provider 应用

创建一个 provider_app 子模块,引入如下依赖:

	<dependencies>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

并添加 application.yml 配置文件:

application.yml

spring:
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
  application:
    name: provider-app
server:
  port: 18083

新增启动类:

package com.duofei;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

/**
 * 启动类
 * @author duofei
 * @date 2020/7/22
 */
@SpringBootApplication
@EnableDiscoveryClient
public class NacosProviderDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(NacosProviderDemoApplication.class, args);
    }

    @RestController
    public class EchoController{
        @GetMapping("/echo/{string}")
        public String echo(@PathVariable String string){
            return "Hello Nacos Discovery " + string;
        }
    }
}

启动应用程序,观察 Nacos 的控制台:
nacos服务列表

可以看出,我们刚运行的服务成功注册。如果仔细留意应用程序的启动日志,也能看见打印的注册完成的信息。

启动 Consumer 应用

调用者服务稍微复杂一点,因为它需要调用提供者的 RESTful 服务。这里使用最原始的 LoadBalanceClientRestTemplate 来访问提供者提供的服务。

新建 consumer-app 项目,依赖不变,配置文件稍作修改:

application.yml

spring:
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
  application:
    name: consumer-app
server:
  port: 18084

启动类如下:

package com.duofei;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.sql.SQLOutput;

/**
 * 启动类
 * @author duofei
 * @date 2020/7/22
 */
@SpringBootApplication
@EnableDiscoveryClient
public class NacosConsumerApp {

    public static void main(String[] args) {
        SpringApplication.run(NacosConsumerApp.class, args);
    }

    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

    @RestController
    public class NacoController{

        @Autowired
        private LoadBalancerClient loadBalancerClient;
        @Autowired
        private RestTemplate restTemplate;

        @Value("${spring.application.name}")
        private String appName;

        @GetMapping("/echo/app-name")
        public String echoAppName(){
            ServiceInstance serviceInstance = loadBalancerClient.choose("provider-app");
            String path = String.format("http://%s:%s/echo/%s", serviceInstance.getHost(), serviceInstance.getPort(), appName);
            System.out.println("request path: " + path);
            return restTemplate.getForObject(path, String.class);
        }

    }
}

访问 http://192.168.3.18:18084/echo/app-name ,但在获取服务实例时,发现获取到的服务实例地址并不是 192.168.3.18 , 所以无法访问到注册的服务。这是因为服务提供者的配置文件并未配置 ip 地址,我们所拿到的 ip 地址是在启动过程中通过 Java 原生的 NetworkInterface 获取的。具体的底层执行代码则是在 NacosDiscoveryProperties 类的 init 方法。

所以,我们修改服务提供者类的配置文件,新增 ip 配置:

spring:
	cloud:
		nacos:
			discovery:
				ip: 192.168.3.18

然后重新访问 http://192.168.3.18:18084/echo/app-name ,就可以得到 Hello Nacos Discovery consumer-app 的响应了。

Nacos 的服务注册与发现流程很简单,但唯一让我有点困惑的便是这 ip 获取了。原因可能是因为我本地有建立过虚拟机,然后有比较多的虚拟网络接口,而 nacos 的筛选方式也比较简单粗暴,直接取了第一个符合条件的网络接口。

不过,在后续发现,可以在配置文件中指定网络接口的名称:

spring:
	cloud:
		nacos:
			discovery:
				network-interface: eth9

上面这个网口名称对应了我的 192.168.3.18 的 ip 地址,这样也可以正常注册 ip 地址。


服务发现端点

Nacos Discovery 在内部提供了一个具有 nacos-discovery 端点 id 的端点。

端点暴露的 json 内容为:

  1. subscribe: 展示了当前的服务订阅信息;
  2. NacosDiscoveryProperties: 展示了当前服务的 Nacos 配置;

想要访问 /nacos-discovery 这个端点路径,需要引入 spring-boot-starter-actuator 组件,并在配置文件中新增如下内容:

management:
  endpoints:
    web:
      exposure:
        include: nacos-discovery

如上的修改,我们将它应用到了前面的服务提供者和服务调用者子模块中,然后,访问 http://192.168.3.18:18083/actuator/nacos-discovery 路径或者是将端口改为服务调用者的端口路径:

/actuator 这个基础路径,可以通过 management.endpoints.web.base-path 修改。
nacos响应结果

可以看到暴露的内容正是 NacosDiscoveryPropertiessubscribe

但为什么 subscribe 的内容为空呢?这是因为,订阅信息需要主动去订阅。在我们的 consumer-app 的启动类中增加如下代码:

	
	@Autowired
    private NacosDiscoveryProperties nacosDiscoveryProperties;
	// 实现 InitializingBean 接口
	@Override
    public void afterPropertiesSet() throws Exception {
        NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();
        namingService.subscribe("provider-app", event -> {
            System.out.println(event);
        });
    }

需要注意的是,注册好该监听器,会在启动后直接触发一次。现在,让我们重新访问端点,获取到如下信息:
nacos响应结果
我们可以在 nacos 的控制台,修改订阅的服务信息,也会触发该事件。但查看该事件的实现类只有一个,所以很难确定具体的事件行为是什么。

服务发现的配置

服务发现的配置可以参考 NacosDiscoveryProperties 类。大多配置都见名知意,可一切还是要以代码的执行结果为准,多思考为什么,切记不要先入为主。

因为我自己在这里就犯了一个先入为主的错误。我打算配置一个 namespace ,第一步是在 nacos 控制台上新建了一个命名空间,命名空间名为 demo ,意为测试代码运行时使用的命名空间。紧接着,第二步,我在 consumer-app 配置文件中新增了如下配置:

spring:
  cloud:
    nacos:
      discovery:
        namespace: demo

可结果并不如我所愿,consumer-app 始终无法注册到"demo"的命名空间。然后,我开始跟踪源码,结果发现在底层,namespace 最终对应的是 namespaceId 。所以,这里其实是需要一个命名空间 id 的。而该 id 值在你创建好命名空间后就可以得到。这个地方的值修改为命名空间 id 后,一切就正常了。

consumer-app 项目修改完命名空间后,记得将 provider-app 也修改为对应的命名空间,否则 consumer-app 无法访问到 provider-app


总结

nacos 的服务发现与注册真的比较简单。我这里整个配置项也就使用了三个,这可能是因为还没有集成 Dubbo 或者其它的 RPC 框架。

这里还有一点没有提到,那就是负载均衡。不过,你可以发现 nacos-discovery 使用了 Ribbon 。那么,nacos-discovery 自然会使用 ribbon 来实现负载均衡策略。想了解更多的可以去查看 ribbon 文档,还有其它疑问的也可以看下 nacos-discoveryribbon 包中的源码。

文档中的内容也需要持怀疑的态度,代码才是检验“真理”的标准!

并不是指文档中的内容有错,而是说文档中的内容简洁,容易让我们刚入门的小白造成理解上的误差!


我与风来

认认真真学习,做思想的产出者,而不是文字的搬运工。


比心


但行善事,莫问前程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值