献给Nacos小白的一篇好文:注册中心

Posted 云水之路

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了献给Nacos小白的一篇好文:注册中心相关的知识,希望对你有一定的参考价值。

服务注册中心

随着微服务的发展,出现了各种各样的服务注册与发现组件,市面使用较多的有eureka、consul、etcd、zookeeper以及本篇的主角nacos等。对于这些组件的优缺点及区别,网络上有很多说法,读者可自行网络识别。Nacos是个不错的选择,它是由阿里牵头,当前社区非常活跃,并且支持网关、限流及监控等配套功能可快速集成,同时它也是个动态配置中心,采用java开发,学习门槛相对低些,受众群体也比较大,所以选择Nacos作为服务的注册和配置中心是个不错的选择。

  1. 简介及环境搭建
  2. 服务注册及发现
  3. 数据存放策略
  4. 使用的一些建议

一、简介及环境搭建

1、简介

Nacos(Dynamic Naming and Configuration Service),顾名思义,na代表注册中心,而co则代表配置中心,s代表服务。Nacos是由阿里开源的服务注册发现与配置中心组件,采用java语言开发,支持单机、集群及多集群模式,支持多数据中心,早些时间是阿里内部项目,经历过大数据量的压力考验,其官网也提供了详细的性能测试报告,以及相关的使用文档说明,方便读者参考学习。需要特别注意的是Nacos是内部网络应用组件,强烈不推荐暴露在公网使用,实际使用时,一般将其部署在内网,或者采用VIP实现虚拟隔离,以保证其使用的安全性,并且生产环境中,必须搭建集群环境以保证高可用。

2、环境搭建

Nacos使用非常简单,前提是需要你安装和配置好jdk或jre环境,然后,按照如下步骤搭建即可,这里以Linux系统为例,以单机模式运行(单机模式适合学习和测试使用,不适合生产环境,后续章节会继续介绍集群环境的搭建),方便讲解和学习。

A、下载软包

下载地址:

https://github.com/alibaba/nacos/releases

进行解压:

$tar -xvf nacos-server-1.4.3.tar.gz

$unzip nacos-server-1.4.3.zip

改个名字:

$mv nacos-server-1.4.3  nacos

B、如何运行

$cd nacos/bin

单机模式:

$startup.sh -m standalone

注意:

单机模式仅适用于本地测试或开发,正常上线时必须配置为集群模式。

C、控制台页

访问地址(默认端口8848、默认帐号/密码:nacos/nacos):

http://127.0.0.1:8848/nacos

登录进来的默认页面效果如下:

二、服务注册及发现

所有支持集成nacos的程序均提供了如何快捷注册服务到nacos的配置方式,如:springboot程序中application.yml配置,这里我们也以此种方式讲解nacos作为服务注册中心,其它类型程序可参看官网说明,对应配置即可。当然,nacos亦提供了http方式的开放api接口供使用者调取(目前参考地址如下):

Open API 指南

详细信息可自行查看其官网说明。

另外,如果我们使用springboot2.x构建服务并要注入到nacos中,那么需要先引入nacos集成依赖包,如:pom.xml配置如下:

<dependency>
    <groupId>
com.alibaba.cloud</groupId>
    <artifactId>
spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

注意:这里我们使用了com.alibaba.cloud包装组件,其提供了很多便捷的api供我们使用,这里省略了version版本,则下载的依赖包版本自动随着springboot版本下载兼容版本。

1、服务注册

A、通过API注册一个服务

$curl -X POST 'http://192.168.43.156:8848/nacos/v1/ns/

instance?serviceName=ApiTest&ip=192.168.43.156&port=8080'

ps: 服务名、IP地址及端口号为必填,其它可选参数可参照官网。此时注册到nacos的是一个空服务,因为其没有对应的程序或进程存在,所以一般以此种方式注册服务时,建议先将服务实体运行后再调取接口注册,避免健康检测扫描时检测不到对应服务健康状态,而将其剔除。

B、application.yml注册

spring:
  application:
    name:
provider-service
 
cloud:
    nacos:

      discovery:
        server-addr: 127.0.0.1:8848

其中server-addr为nacos的服务注册地址127.0.0.1,默认端口8848。服务被注册到默认的保留命名空间(public)中,nacos提供使用一套nacos环境通过namespace来管理多套隔离的服务环境,方便对服务进行隔离和灵活切换,此时注册服务时,需要填写想要注册到的命名空间ID,命名空间如下创建:

不填写命名空间ID时,默认按照UUID格式生成ID,命名空间名字不能重复。下面是将provider-service服务注册到providers空间的配置,需要配置namespace为providers空间ID,如下:

spring:
  application:
    name:
provider-service
 
cloud:
    nacos:
      discovery:

        server-addr: 127.0.0.1:8848
        namespace: c1a176a1-589d-42b5-8eb5-acf734f1c269

为演示跨空间服务隔离效果,这里创建另一个消费者服务client-service,并将其注册到clients命名空间中,该空间的创建方法请参看providers的创建,配置如下:

spring:
  application:
    name:
client-service
 
cloud:
    nacos:
      discovery:

        server-addr: 127.0.0.1:8848
        namespace: 184c0159-a2f7-415a-a395-bb48df58ec8a

两个服务注册后效果如下图:

另外,在nacos中也提供了分组概念,默认服务均被注册到DEFAULT_GROUP中,不需要我们指定,不过令人疑惑的是为什么分组不能可视化维护?但是,我们又可以通过配置将服务注册到指定的分组中,并且经过验证得知,同一个namespace不同group的服务间是不能通信的,只有同一个namespace和同一个group才能通信,这分组隔离与命名隔离道理是一样的。

2、服务发现与隔离

nacos中的服务发现主要是通过服务名、分组和命名空间等信息,在nacos存储中寻找想要消费的服务,然后格式转换并返回。这里我们以测试不同命名空间的服务之间调取为例,来演示服务如何发现及跨命名空间服务调取的限制。

上面创建的client-service和provider-service两个服务,他们分属于不同的命名空间,现在我们使用client-service来调取provider-service服务接口,client-service接口如下:

@GetMapping(value = "/test/hello")
public String testHello(@PathVariable String hello)
   
// 获取指定服务名的多个实例,如果未找到则代表不属于同一个命名空间
    List<ServiceInstance> providers = discoveryClient.getInstances("provider-service");
    if
(CollectionUtils.isEmpty(providers))
       
return "未找到该服务,欲调取服务与当前服务不在一个命名空间!";
   

   
// 实际环境中,为保障服务可用性,服务一般存在多个实例,这里随机取
    Integer index = ThreadLocalRandom.current().nextInt(providers.size());
   
// 构建完整的provider-service服务的接口地址
    String url = providers.get(index).getUri() + "/test/" + hello;
   
// 采用RestTemplate远程调取服务接口并返回结果
    return restTemplate.getForObject(url, String.class);

下面是provider-service服务接口,直接返回调取端传递的hello字符串文本,如下:

@GetMapping(value = "/test/hello")
public String hello(@PathVariable String hello)
   
return hello;

2.1 如何寻找服务

我们在client-service服务中添加如上代码,其中使用了DiscoveryClient,它是个通用的模版接口,而nacos自定义了NacosDiscoveryClient来重写它的getInstances方法,主要通过传递的服务ID或服务名、组名及命名空间(当前服务配置的空间ID)等信息找到服务端信息(实际上最终会通过HttpClient调取nacos开放http接口/instance/list),并转为ServiceInstance服务实例类型供我们使用,这就是如何在nacos中寻找或发现服务。

另外,实际我们在注册服务到nacos时,为了保障服务的高可用性,往往需要为单个服务注册多个实例,所以出现了上面的List<ServiceInstance>来接收同个服务的多个实例,这也是服务负载调度的前提条件。

2.2 跨空间调取限制

了解了发现服务的过程,通过查看源码可知道,我们在调取getInstances并传递服务名时,实际上该方法内会从NacosDiscoveryProperties中获取当前服务client-service所配置的命名空间, 并结合传递的服务名、组名等信息,通过HttpClient调取开放的/instance/list接口,如果查询不到服务信息ServiceInstance,则代表服务不在一个空间,这就是nacos中的命名空间隔离原理。

此时,调取服务provider-service的接口时,直接返回"未找到该服务,欲调取服务与当前服务不在一个命名空间!" 提示信息。我们可以将服务provider-service同样注册到client-service所在的空间时,然后通过RestTemplate访问相关接口,此时就能正常访问,建议读者尝试验证。

三、数据存放策略

我们知道,在nacos中我们可通过服务名字、分组及命名空间等信息,找到服务具体的调取信息,如:IP、端口号、服务名等。那么,这些信息是从哪里获取的?其实也就是要知道服务注册的信息存放在哪里,以及如何存放的。

这里先介绍单机模式,后面再介绍集群模式。注册到nacos的服务实例信息是存放在Map数据结构的内存中,目前nacos使用了ConcurrentSkipListMap,因为我们知道nacos对服务的存储是共享的,所以会存在多个线程并发访问的安全问题,而ConcurrentSkipListMap比较适合解决此类问题,这主要是依靠它的“跳表”存取算法,只需局部加锁,所以吞吐率比较好,这是nacos选择它的主要原因。

那么,都存放哪些服务实例信息了?nacos主要存放了服务所在机器的IP地址和端口号、空间ID、分组Group、服务名字等信息,这也就是前面我们发现服务所需要的一些基本信息。

四、使用的一些建议

1、建议指定服务所归属的命名空间namespace,如果公司的服务测试和生产环境均采用同一套nacos管理的话,那么,就可以创建测试和生产两套命名空间,以便于灵活切换管理。当然,也可以有别的划分维度,比如:按业务块划分或区域划分等,具体要看实际场景需求来定。

2、对于分组的划分,笔者建议从实际角度考虑,如果namespace能够满足隔离需求,可以不设定分组,毕竟少了维护分组的工作。

 

以上是关于献给Nacos小白的一篇好文:注册中心的主要内容,如果未能解决你的问题,请参考以下文章

献给Nacos小白的一篇好文:注册中心

献给Nacos小白的一篇好文:服务的健康检查

献给Nacos小白的一篇好文:服务的健康检查

献给Nacos小白的一篇好文:服务的健康检查

献给Nacos小白的一篇好文:服务的发布与订阅

献给Nacos小白的一篇好文:服务的发布与订阅