天天看点

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

包中的配置文件,发现地址应为如上的形式。
SpringCloudAlibaba之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

的控制台:

SpringCloudAlibaba之Nacos服务注册与发现

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

启动 Consumer 应用

调用者服务稍微复杂一点,因为它需要调用提供者的

RESTful

服务。这里使用最原始的

LoadBalanceClient

RestTemplate

来访问提供者提供的服务。

新建

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

修改。
SpringCloudAlibaba之Nacos服务注册与发现

可以看到暴露的内容正是

NacosDiscoveryProperties

subscribe

但为什么

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);
        });
    }
           

需要注意的是,注册好该监听器,会在启动后直接触发一次。现在,让我们重新访问端点,获取到如下信息:

SpringCloudAlibaba之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-discovery

ribbon

包中的源码。

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

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

我与风来

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

SpringCloudAlibaba之Nacos服务注册与发现

但行善事,莫问前程。

继续阅读