前言

上一篇咱们提到Nacos作为动态装备中心,那么这篇来聊聊Nacos作为服务注册中心。注册中心其实就类似于企查查这种平台,把公司信息汇合到这个平台便利他人运用。咱们把服务注册到Nacos也就是为了让他人发现咱们的服务而且运用它。

手摸手教程—Nacos服务注册中心

本文根据Cloud Alibaba 2021.0.1.0 + Springboot 2.6.3

搭建服务

首要还是以单机模式发动nacos-serve

startup.cmd -m standalone

服务提供者与服务顾客的pom中均添加以下场景发动器

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
            <version>2021.0.1.0</version>
        </dependency>       
<!-- https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-nacos-discovery -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>2021.0.1.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-bootstrap -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bootstrap</artifactId>
            <version>3.1.2</version>
        </dependency>

我这里根据【Nacos手摸手教学一】来做的,服务提供者的bootstrap.yml 留意修正namespace、ip

spring:
  cloud:
    nacos:
      config:
        server-addr: ip:8848
        file-extension: yaml
        namespace: 434e7801-0b5d-4fd2-b96a-acbac849fd84
      discovery:
        server-addr: ip:8848
  application:
    name: provider-serve
  profiles:
    active: dev

服务顾客的装备文件一样的,不一样的也就是服务名consumer-serve

接下来咱们发动两个服务,回到nacos控制台

手摸手教程—Nacos服务注册中心

原理

服务确实注册到Nacos了,表面来看咱们相当于登记了姓名,那么假如我把某个服务停了,nacos又是如何知道该服务还能不能正常运用的呢?咱们不仅要知其然,也要知其所以然。

大致流程:每个服务都会有一个nacos client,它用来和nacos server打交道,用来具体的服务注册、查询等操作,服务提供者在发动的时候会向nacos server注册自己,服务顾客在发动的时候订阅nacos server上的服务提供者。

手摸手教程—Nacos服务注册中心

NacosNamingService 中的 registerInstance 方法用于向Nacos注册实例,Nacos 中的实例分为暂时和永久2种类型,注册前先判别实例是否是暂时实例(默许都是暂时实例),假如是Instance是暂时实例,则创立一个 BeatTask 心跳线程定时调用 HTTP PUT /instance/beat 向 Nacos 服务器发送心跳,然后调用 HTTP POST /instance 向 Nacos 服务器注册实例。

手摸手教程—Nacos服务注册中心

BeatTask 线程经过 HTTP PUT /instance/beat 向 Nacos 发送心跳恳求,假如当时实例在服务器上不存在,则从头注册实例,不然等待履行下一次心跳

@Override
public void run() {
    if (beatInfo.isStopped()) {
        return;
    }
    //下一次心跳时刻
    long nextTime = beatInfo.getPeriod();
    try {
        //发送心跳
        JsonNode result = serverProxy.sendBeat(beatInfo, BeatReactor.this.lightBeatEnabled);
        long interval = result.get("clientBeatInterval").asLong();
        boolean lightBeatEnabled = false;
        if (result.has(CommonParams.LIGHT_BEAT_ENABLED)) {
            lightBeatEnabled = result.get(CommonParams.LIGHT_BEAT_ENABLED).asBoolean();
        }
        BeatReactor.this.lightBeatEnabled = lightBeatEnabled;
        //运用服务器回来的心跳时刻
        if (interval > 0) {
            nextTime = interval;
        }
        int code = NamingResponseCode.OK;
        if (result.has(CommonParams.CODE)) {
            code = result.get(CommonParams.CODE).asInt();
        }
        //服务器回来实例不存在,从头注册实例
        if (code == NamingResponseCode.RESOURCE_NOT_FOUND) {
            Instance instance = new Instance();
            instance.setPort(beatInfo.getPort());
            instance.setIp(beatInfo.getIp());
            instance.setWeight(beatInfo.getWeight());
            instance.setMetadata(beatInfo.getMetadata());
            instance.setClusterName(beatInfo.getCluster());
            instance.setServiceName(beatInfo.getServiceName());
            instance.setInstanceId(instance.getInstanceId());
            instance.setEphemeral(true);
            try {
                //从头注册实例
                serverProxy.registerService(beatInfo.getServiceName(),
                                            NamingUtils.getGroupName(beatInfo.getServiceName()), instance);
            } catch (Exception ignore) {
            }
        }
    } catch (NacosException ex) {
        NAMING_LOGGER.error("[CLIENT-BEAT] failed to send beat: {}, code: {}, msg: {}",
                            JacksonUtils.toJson(beatInfo), ex.getErrCode(), ex.getErrMsg());
    }
    //开启下一次心跳
    executorService.schedule(new BeatTask(beatInfo), nextTime, TimeUnit.MILLISECONDS);
}

服务端也会为每个 Service 发动一个 ClientBeatCheckTask 线程用于检测该 Service 下一切的实例的健康状况,假如实例的 lastBeat 最后心跳时刻超越了心跳超时时刻(默许15秒),则设置 healthy 健康状况为 false,并经过 UDP 向客户端 push 最新的数据,假如 lastBeat 超越30秒,表明此实例已不可用,经过调用 HTTP DELETE /instance 从实例列表中删除该实例。