Spring Cloud Alibaba Nacos

愈来愈多的读者在和我交流关于Spring Cloud Alibaba的种种事宜,甚至于在一次面试中,一半时间都在聊这个话题。因此,本着对技术钻研的热情,对Spring Cloud Alibaba进行了一番研究。html

在这里,并不想高谈阔论,也不想预言将来,只是挑选了几个从阿里巴巴中间件团队内脱胎出来的开源组件,对于解决实际业务问题有所裨益。java

1、背景

先来讲说大背景。mysql

如今,很明显的一个趋势就是:微服务git

这个趋势的底层驱动力就来源于分布式系统的普及,而微服务的各个特性是现在大大小小的企业没法拒绝的诱惑。github

而后,用上了微服务的架构风格,用Spring Cloud,或者Dubbo搭了一套脚手架,就开始干起来了。web

接下来,一众小公司画完了大饼以后,发现本身根本吃不下。这就是典型的落后劳动力与先进生产力的尖锐矛盾。这个时候,返璞归真的想法是不能有了,重构代价太大。面试

固然,哪里有问题,哪里就有商机。各大XX云厂商通过一系列包装以后,用“云原生(Cloud Native)”的新概念粉墨登场。spring

Spring Cloud Alibaba就是其中之一。sql

这个概念的一个核心价值就是:平滑上云,赋能运维。最明显的业务表象就是,会提供一套Open API,甚至是贴心的提供一个可视化控制台,傻瓜式的那种。shell

2、从NACOS提及

这是一颗耀眼的掌上明珠,迅速引发了个人注意。

按照套路,分为两讲。其一讲述NACOS的功能特性,及其使用,再者就是更深刻一步,看看大厂的攻城狮们写的代码。

本文所使用的版本是NACOS 1.0.0,因为此版本仍是第一个NACOS正式版,NACOS正处在飞速发展阶段,本文的一些内容可能会不适用于之后的版本,请读者自行辨别。

NACOS解决两个核心问题:动态配置管理,服务注册发现。

兼容性方面,除了支持自家的Dubbo,还对Spring Cloud,Kubernetes,Istio有所兼容。
野心勃勃的生态圈
对照以上的全景图,如今的NACOS还有一段距离,可是并不遥远。

至此,不说道说道Eureka,都有点过意不去了。

我用下来的体验是:NACOS彻底能够替代Eureka了。

江山代有才人出,这是必然的结果。

在“云原生”的大背景之下,NACOS顺利成章的推出了Console,将触角进一步延伸至服务的精细化管理。

固然,不排除Eureka也在憋大招。

再说说动态配置的特性。

固然,NACOS略胜一筹,可替代Spring Cloud Config了。

原先在Git/SVN上托管的配置项,均可以在Console上统一管理了。

若是想先睹为快,能够接下着往下读。若是想再多了解一些,能够直接跳过这部分,阅读下一个小节。

能够把NACOS理解成是一个中心化的服务,这在阿里系的架构中家常便饭。因此,必须得先启动这个服务。

有两个办法:其一是直接clone源码,使用maven打包。第二个办法是直接下载GitHub release出来的压缩包。

推荐后者。

方法1:主要运行如下命令:

git clone https://github.com/alibaba/nacos.git 
cd nacos/ 
mvn -Prelease-nacos clean install -U

通过一段时间的构建过程,在./distribution/target目录下有咱们想要的压缩包。

方法2:进入https://github.com/alibaba/nacos/releases,找到压缩包,下载。

为了演示,咱们先用单机模式启动。

Windows环境下:

startup.cmd -m standalone

一切就绪的话,访问http://127.0.0.1:8848/nacos/index.html,使用nacos/nacos登陆。

接下来,随便逛逛。
Console预览

3、重要的概念

为了不在Console中迷失自我,有必要先阐述几个重要的概念。
数据模型
这张图很重要。表述了namespace、group和service/dataId的包含关系。

NACOS给的最佳实践代表,最外层的namespace是能够用于区分部署环境的,好比test,uat,product等。同时,也有一个商业利用价值:多租户。以namespace为单位,给用户开辟使用空间。

其它两个领域模型不用多解释了,见名知意。其目的也很是明显,就是为了可以逻辑上区分两个目标对象。

默认状况下,namespace=public,group=DEFAULT_GROUP。

明白了这个数据模型后,能够稍微玩转一下Console了,好比新建若干个namespace:
namespace列表
namespace顺利建立成功后,会在每一个一级页面看到由namespace组成的TAB,能够任意切换namespace,对其下的数据进行操做。好比下图的配置列表:
配置列表
固然,还有众多的领域模型,可是跟这一讲的关系不大了,下一讲深刻源码的时候再进行剖析。接下来会建立一个Demo工程,用于构建基于NACOS的config server和discovery server。

在进行下一章节以前,强烈建议先建立一个Spring Boot样板工程,能够不作任何配置,不添加任何一段代码。

固然,为了让这一讲的内容更具实战意义,我不会照搬官网的Demo,本文所做的Demo工程也能够从GitHub上克隆获得。请提早准备好,接下来的内容只会挑重点讲解,侧重点不会在如何搭建工程。

4、使用NACOS的搭建工程

工程名称为:nacos-example,在各个pom.xml文件中相关依赖管理就再也不赘述了。

如今来介绍一下,这个demo工程干了一件什么事:用户在下单的时候,须要校验用户的状态,商品是否上架以及购买的数量上限。

为了体现动态配置,用户状态和购买的数量上限作成配置项。而这二者的配置在不一样的namespace下是有所不一样的。

为了体现服务间的调用,下单入口在order模块,经由user模块进行校验。
工程结构
根据上述,NACOS服务启动好了后,咱们分别建立dev,product两个namespace,在写spring boot配置的时候,填写的不是test,product这些namespace名称,而是ID,此ID会在namespace成功建立后,由系统自动分配,目前的生成规则就是随机的UUID函数。

这样的配置规则会存在很大的争议,本文不作讨论。

user模块须要动态配置,因此能够看做是一个config client。又由于须要接受order模块的调用,因此也是一个service provider。

一样的,order模块须要动态配置,因此也是一个config client。

至此,读者会发现一些NACOS带来的“弊病”,最显现的问题就是“多重角色”带来的侵入性。由于是中心化的架构,若是没有作到很好的解耦,出现这个问题也不足为奇了。

特别是在远程调用的角色分配上,NACOS严格听从了“生产者-消费者”模型,在实际业务场景下,不免会有服务既是生产者,又是消费者,而Eureka是没有这种区分的。

接下来把目光转向配置。

阿里系的开源产品大多建议是用properties文件,因为我的习惯,我使用的是yml,达到的效果是同样的。

值得注意的是,由于须要更高优先级的加载顺序,因此配置文件必须使用bootstrap.yml。

user模块的配置文件,区分了环境,为了增长辨识度,也定义了group进行隔离。以下:

server:
  port: 8100
spring:
  application:
    name: user
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yaml
        namespace: 7d3e5f19-a102-471a-b6e0-67bd7d1d35f3
        group: USER_GROUP
      discovery:
        server-addr: 127.0.0.1:8848
        namespace: 7d3e5f19-a102-471a-b6e0-67bd7d1d35f3
---
spring:
  profiles: prod
  cloud:
    nacos:
      config:
        server-addr: 11.162.196.16:8848
        namespace: c4c81555-91e1-4ef5-8b57-77c5407b3481
      discovery:
        server-addr: 11.162.196.16:8848
        namespace: c4c81555-91e1-4ef5-8b57-77c5407b3481
---
spring:
  profiles:
    active: dev

order模块的配置文件相似,再也不赘述。

而后对自定义配置参数进行封装。若是只有单个参数,能够不用JavaBean的形式,可是绝大多数状况下都是多参数配置的,本文给出了JavaBean形式的封装。

user模块使用到的配置参数:

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;

@Getter
@Setter
@ConfigurationProperties(prefix = "user")
public class UserConfig {

    /** * 用户状态:enable-启用,disable-禁用 */
    private String status;
}

order模块使用到的配置参数:

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;

@Getter
@Setter
@ConfigurationProperties(prefix = "order")
public class OrderConfig {

    /** * 最大购买数量 */
    private int maxNum = Integer.MAX_VALUE;

    /** * 是否上架 */
    private boolean onSale = true;
}

而后就是编写功能实现代码,主要入口在order模块中的OrderController:

@RefreshScope
@RestController
@RequestMapping
public class OrderController {

    @Autowired
    private OrderConfig config;

    @Autowired
    private UserRpcService userRpcService;

    @PostMapping(value = "/order")
    public String placeOrder(@RequestParam(name = "num", defaultValue = "1") Integer num) {
        if (!"enable".equals(userRpcService.getUserStatus())) return "该用户已被禁用,暂不能下单";
        if (num <= 0) return "购买数量有误";
        if (!config.isOnSale()) return "商品未上架";
        if (num > config.getMaxNum()) return "购买数量超限";
        return "OK";
    }
}

一些列的下单校验,第一个校验是判断用户状态,使用了基于Feign的远程调用。固然,必须在user模块中提供对应的Controller实现:

@RefreshScope
@RestController
@RequestMapping(value = "/user")
public class UserController {

    @Autowired
    private UserConfig config;

    @GetMapping(value = "/status")
    public String getUserStatus() {
        return config.getStatus();
    }
}

Controller中的@RefreshScope注解就是用来实现配置自动更新的。

整个工程准备就绪以后,先别急着run,还须要在Console中添加配置,用以测试动态配置是否生效。

添加配置的过程再也不赘述,须要注意的是Data Id的填写规范。dataId的完整格式由三部分组成:

${prefix}-${spring.profile.active}.${file-extension}

prefix,默认使用${spring.application.name},也能够经过spring.cloud.nacos.config.prefix来配置。

spring.profile.active,即为当前环境对应的 profile,详情能够参考 Spring Boot文档。 注意:当 spring.profile.active 为空时,对应的链接符 - 也将不存在,dataId 的拼接格式变成 ${prefix}.${file-extension}

file-exetension,为配置内容的数据格式,能够经过配置项 spring.cloud.nacos.config.file-extension 来配置。目前只支持 properties 和 yaml 类型。
Demo工程的配置列表
接下来就是分别启动order和user两个模块,随便玩玩。

5、其余功能简介

NACOS会记录配置文件的历史版本,保留30天,同时还贴心的提供了一键回滚功能,回滚操做将会触发配置更新。
配置历史版本
NACOS提供了配置监听的Open-API。注册监听采用的是异步 Servlet 技术。注册监听本质就是带着配置和配置值的 MD5 值和后台对比。若是 MD5 值不一致,就当即返回不一致的配置。若是值一致,就等待必定时间段,返回空值。

最后就是优雅上/下线功能,算是NACOS的一大亮点。在每一个服务详情里面,能够有多个实例(instance),经过Console能够控制每一个实例的上/下线。若是是从新上线的话,会有一段时间的注册过程,并非点击“上线”按钮以后就能立马访问到该实例。

6、NACOS的进阶功能

若是决定要将NACOS大规模投产,上述功能还远远知足不了条件。在这一章节,将会讲解若干个进阶功能,为你的NACOS服务保驾护航。

一、集群模式

只启动一个NACOS服务实例未免有些势单力薄了,前述内容都是在单机部署的前提下,这一小节教你如何部署一个NACOS集群。

找到conf/cluster.conf文件,若是没有,就新建。编辑里面的内容,仅需指定机器的ip和port,最好是3个或者3个以上。若是没有这么多机器资源,能够直接在一台机器上部署,区分端口号便可。以下:

# ip:port
192.168.0.88:8848
192.168.0.88:8849
192.168.0.88:8840

请注意,在集群模式下就不能用127.0.0.1或者localhost了,当前版本对于网卡解析存在bug。

这个问题是由于Nacos获取本机IP时,没有获取到正确的外部IP,须要保证InetAddress.getLocalHost().getHostAddress()或者hostname -i的结果是与cluster.conf里配置的IP是一致的。

找到conf/application.properties,若是没有,说明有问题。编辑里面的内容,主要目的是启用MySQL做为存储层(目前仅支持MySQL)。在该文件末尾追加:

spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root
db.password=123456

而后新建一个名为nacos的数据库,运行conf/nacos-mysql.sql脚本文件。

以上,就是运行NACOS集群模式所需的配置内容。

由于要在一台机器上模拟多个NACOS服务,能够将配置好的工程复制出两份,而后在conf/application.properties中修改端口号便可。
目录
而后,分别在三个命令窗口启动NACOS:

startup -m cluster

分别使用对应的端口号访问Console,若是能顺利打开,说明部署成功了。

最后,在上述搭建的demo工程中,修改server-addr的值,把其余NACOS访问地址也加上去:

server:
  port: 8200
spring:
  application:
    name: order
  cloud:
    nacos:
      config:
        server-addr: 192.168.0.88:8848,192.168.0.88:8849,192.168.0.88:8840
        file-extension: yaml
        namespace: 7d3e5f19-a102-471a-b6e0-67bd7d1d35f3
        group: ORDER_GROUP
      discovery:
        server-addr: 192.168.0.88:8848,192.168.0.88:8849,192.168.0.88:8840
        namespace: 7d3e5f19-a102-471a-b6e0-67bd7d1d35f3

二、安全措施

很遗憾,NACOS的官网并无就安全性进行详细的介绍。惟一看到的accessKeysecretKey两项疑似安全的配置项,倒是为了配合阿里云的ACM而设立的。

让我最为关注的配置文件加密功能也暂未release出来,如下是官方原话:

Nacos计划在1.X版本提供加密的能力,目前还不支持加密,只能靠sdk作好了加密再存到nacos中。
NACOS Console的安全性一样很糟糕,密码没法修改,用户不能建立,也没有角色、权限的概念。

NACOS的服务注册与发现基于HttpURLConnection进行远程调用,这个地方是提供了HTTPS的启用开关,JVM参数名是:com.alibaba.nacos.client.naming.tls.enable

除此以外,目前只能在NACOS的外围作一些安全措施了。

三、一些有用的配置

nacos.home,这个是NACOS服务的启动参数,以JVM参数的形式传入。可是很遗憾,使用这个参数并非很方便,须要修改启动脚本,大概是在startup.sh文件中的104行:

JAVA_OPT="${JAVA_OPT} -Dnacos.home=${BASE_DIR}"

更为完全的方法是修改BASE_DIR的值(默认为startup.sh文件的父目录),大概在startup.sh文件中的71行:

export BASE_DIR=`cd $(dirname $0)/..; pwd`

nacos.logging.path,日志是一个系统运行最宝贵的附加产物,有的时候为了便于管理,须要自定义日志目录。若是须要指定,一样须要修改startup.sh脚本。大概在104行后追加一行:

JAVA_OPT="${JAVA_OPT} -Dnacos.logging.path=指定的目录"

固然,日志级别也能指定,分别经过com.alibaba.nacos.naming.log.level和com.alibaba.nacos.config.log.level这两个JVM参数指定。

不过,还须要特别说明一下,并不是全部日志都是有价值的,能够根据实际状况进行删减,不然会对服务器形成没必要要的负担,相关日志配置能够在conf/nacos-logback.xml文件中修改。

四、endpoint

在上述配置文件中所涉及的serverAddr参数是穷举了全部可用的NACOS服务,在大规模应用场景下,这种配置方式显然是不合理的。endpoint至关于一个DNS服务,能够代理全部NACOS服务,只须要指定一个访问域名。

这篇文章是对endpoint的一个最佳实践,而且给出了环境隔离的有效手段。

7、总结

本文是对Spring Cloud Alibaba Nacos的功能性介绍。

若是研发能力强劲的队伍,能够如今尝尝这只“螃蟹”,顺带还能贡献很多的PR。

我的建议先观望一段时间,大概在v1.2.0版本以后,就能够逐渐引入到公司的技术栈内了。
在这里插入图片描述