Spring Cloud(十六):Alibaba 之 Nacos

上个月最后一天的凌晨,Spring Cloud Alibaba 正式入驻了 Spring Cloud 官方孵化器,并在 maven 中央库发布了第一个版本。html

目前 Spring Cloud Alibaba 还只能算是预览版吧,里边的坑确定很多,不过我仍是决定试试,看看 Alibaba 到底靠谱不靠谱。java

Nacos for Spring Cloud

Spring Cloud Alibaba

目前 Spring Cloud Alibaba 项目还处于 Spring Cloud 官方孵化器中,打开它 Github 的就能看到 “亲切” 的中文文档mysql

它目前只有三个组件:git

  • Sentinel:把流量做为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。github

  • Nacos:一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。web

  • AliCloud OSS: 阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您能够在任何应用、任什么时候间、任何地点存储和访问任意类型的数据。spring

看官方路线图上讲后边还会增长sql

  • Dubbo:Apache Dubbo™ (incubating) 是一款高性能 Java RPC 框架。数据库

  • RocketMQ:Apache RocketMQ™ 基于 Java 的高性能、高吞吐量的分布式消息和流计算平台。apache

  • Schedulerx:阿里中间件团队开发的一款分布式任务调度产品,支持周期性的任务与固定时间点触发任务。

  • AliCloud SLS:针对日志类数据的一站式服务,在阿里巴巴集团经历大量大数据场景锤炼而成。您无需开发就能快捷完成日志数据采集、消费、投递以及查询分析等功能,提高运维、运营效率,创建 DT 时代海量日志处理能力。

从数量上来看,Alibaba 的组件数量和目前 Netflix 的相比少了一多半,可是仔细看看各组件的功能描述,也就明白了。在没真正上手以前,我我的先大胆猜想一下:

  • Nacos = Eureka/Consule + Config + Admin
  • Sentinel = Hystrix + Dashboard + Turbine
  • Dubbo = Ribbon + Feign
  • RocketMQ = RabbitMQ
  • Schedulerx = Quartz
  • AliCloud OSS、AliCloud SLS 这三个应该是独有的
  • 链路跟踪(Sleuth、Zipkin)不知道会不会在 Sentinel 里

以上只是猜想,待我从坑里爬出来以后再回来更新。也欢迎你们一块儿交流探讨~

这里我就先试试 Nacos。

Nacos

nacos arch

这是 Nacos 的架构图,能够看到它确实是融合了服务注册发现中心、配置中心、服务管理等功能,和我以前猜测的它是 Eureka/Consule + Config + Admin 的合体差很少。

另外经过官方文档发现,Nacos 除了能够和 Spring Cloud 集成,还能够和 Spring、SpringBoot 进行集成。

不过咱们只关注于 Spring Cloud,别的就略过了,直接上手吧~

工程的目录结构以下:

1
2
3
4
5
6
7
8
9
10
11
alibaba
├── nacos-config
│   ├── pom.xml
│   └── src
├── nacos-consumer
│   ├── pom.xml
│   └── src
├── nacos-provider
│   ├── pom.xml
│   └── src
└── pom.xml

首先引入 Spring Cloud Alibaba 的 BOM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.4.RELEASE</version>
    <relativePath/>
</parent>
<properties>
    <spring-cloud.version>Finchley.SR2</spring-cloud.version>
    <spring-cloud-alibaba.version>0.2.0.RELEASE</spring-cloud-alibaba.version>
</properties>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>${spring-cloud-alibaba.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

这里版本号有坑,文档上说和 Spring Boot 2.0.x 版本兼容,可是实测 2.0.6.RELEASE 报错

1
java.lang.NoClassDefFoundError: org/springframework/core/env/EnvironmentCapable

Nacos Server

在使用 Nacos 以前,须要先下载 Nacos 并启动 Nacos Server。

Nacos Server 有两种运行模式:

  • standalone
  • cluster

不论哪一种方式吧,都须要先去 https://github.com/alibaba/nacos/releases 下载最新的 release 包,而后解压,以 nacos-server-0.4.0.zip 为例

1
2
unzip nacos-server-0.4.0.zip
cd nacos

standalone 模式

此模式通常用于 demo 和测试,不用改任何配置,直接敲如下命令执行

1
sh bin/startup.sh -m standalone

Windows 的话就是

1
cmd bin/startup.cmd -m standalone

而后从 http://localhost:8848/nacos/index.html 进入控制台就能看到以下界面了

Nacos Console

cluster 模式

集群模式须要依赖 MySQL,而后改两个配置文件:

  • conf/cluster.conf
  • conf/application.properties

具体怎么改,在这里就先不展开了。咱们先用 standalone 模式撸起来,享受 coding 的快感,而后再慢慢转到 cluster 上边。

配置管理

  1. 在 nacos/pom.xml 里添加依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
    </dependencies>
    
  2. 启动类不用修改

    1
    2
    3
    4
    5
    6
    
    @SpringBootApplication
    public class NacosApplication {
        public static void main(String[] args) {
            SpringApplication.run(NacosApplication.class, args);
        }
    }
    
  3. 修改 bootstrap.yml

    1
    2
    3
    4
    5
    6
    7
    
    spring:
      application:
        name: nacos
      cloud:
        nacos:
          config:
            server-addr: 127.0.0.1:8848
    

    注意:必须是写在 bootstrap.yml 中,配置在 application.yml 中不行,启动报错

    1
    
    java.lang.IllegalArgumentException: Could not resolve placeholder 'spring.application.name' in value "${spring.application.name}"
    

    至于 bootstrap.yml 和 application.yml 的区别,以前讲过这里就不赘述了。

  4. 添加一个 Endpoint 便于观察

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    @RestController
    @RequestMapping("/config")
    @RefreshScope
    public class ConfigController {
    
        @Value("${useLocalCache:false}")
        private boolean useLocalCache;
    
        @RequestMapping("/get")
        public boolean get() {
            return useLocalCache;
        }
    
    }
    

    注意必定要加@RefreshScope注解

  5. 当心!此处有坑!

    这时候先别急着启动 NacosConfigApplication 的,须要须要经过调用 Nacos Open API 往 Nacos Server 里发布一个配置。dataId 为 nacos.properties,内容为useLocalCache=true

    1
    2
    3
    4
    5
    
    curl -X "POST" "http://127.0.0.1:8848/nacos/v1/cs/configs" \
         -H 'Content-Type: application/x-www-form-urlencoded; charset=utf-8' \
         --data-urlencode "dataId=nacos.properties" \
         --data-urlencode "group=DEFAULT_GROUP" \
         --data-urlencode "content=useLocalCache=true"
    

    这里涉及一个 dataId 的概念

    dataId 的完整格式以下:

    1
    2
    
    > ${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 类型。
  6. 而后启动 NacosConfigApplication,从启动日志里能看到

    1
    
    Located property source: CompositePropertySource {name='NACOS', propertySources=[NacosPropertySource {name='nacos.properties'}]}
    

    若是 propertySources 里边是空的,那抱歉,你掉到坑里边了。 若是你能看到以前发布的 dataId,那恭喜,请求 http://localhost:8080/config/get 就能够看到返回内容 true 了。

  7. 再次调用 Nacos Open API 修改内容为useLocalCache=false

  8. 再次访问 http://localhost:8080/config/get ,此时返回内容为false,说明程序中的useLocalCache值已经被动态更新了。

固然,以上手动调用 Nacos Open API 的方式也能够经过 Nacos Console 的可视化界面来操做

Nacos Console Config

另外咱们能够查询配置的历史记录并能快速回滚

Nacos Config History

还能查询到某个配置当前的被监听状态(这里的分页有些 bug)

Nacos 监听查询

数据源

通过了上边的一些简单操做,咱们已经能够正常使用 Nacos 配置中心了。
可是不知道你有没有想过:配置数据是存在哪里呢?

咱们没有对 Nacos Server 作任何配置,那么数据只有两个位置能够存储:

  • 内存
  • 本地数据库

若是咱们如今重启刚刚在运行的 Nacos Server,会发现刚才加的 nacos.properties 配置还在,说明不是内存存储的。

这时候咱们打开NACOS_PATH/data,会发现里边有个derby-data目录,Derby 是 Java 编写的数据库,属于 Apache 的一个开源项目。咱们的配置数据如今就存储在这个库中。

Derby 我并非很熟悉,那能不能将数据源改成咱们熟悉的 MySQL 呢?固然能够了。

注意:不支持 MySQL 8.0 版本

这里我以本地运行的 MySQL 为例:

  1. 建立一个名为nacos_config的 database

  2. NACOS_PATH/conf/nacos-mysql.sql中的表结构导入刚才建立的库中,这几张表的用途就本身研究吧

  3. 修改NACOS_PATH/conf/application.properties,加入 MySQL 配置

    1
    2
    3
    4
    
    db.num=1
    db.url.0=jdbc:mysql://localhost:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
    db.user=root
    db.password=root
    
  4. 建立cluster.conf,填入要运行 Nacos Server 机器的 ip

    1
    2
    
    192.168.100.155
    192.168.100.156
    

    我就是运行个 demo,没有多余机器来组建集群怎么办呢?

    其实不用虚拟机,直接只填一个本地地址也是能够的(仅限于配置管理,服务发现不行)

这里有两个坑:

  • Nacos Server 的数据源是用 Derby 仍是 MySQL 彻底是由其运行模式决定的:

    • standalone 的话仅会使用 Derby,即便在 application.properties 里边配置 MySQL 也照样无视;
    • cluster 模式会自动使用 MySQL,这时候若是没有 MySQL 的配置,是会报错的。
  • 官方提供的 cluster.conf 示例以下

    1
    2
    3
    4
    5
    
    #it is ip
    #example
    10.10.109.214
    11.16.128.34
    11.16.128.36
    

    从习惯来看,这个#号后边的应该就是注释的,可是抱歉哦,必须删掉,不然下面的异常就扑面而来

    1
    2
    3
    4
    5
    6
    7
    8
    
    Caused by: java.lang.NumberFormatException: For input string: "it is ip:0"
      at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
      at java.lang.Long.parseLong(Long.java:589)
      at java.lang.Long.parseLong(Long.java:631)
      at com.alibaba.nacos.naming.core.DistroMapper.onServerStatusUpdate(DistroMapper.java:125)
      at com.alibaba.nacos.naming.core.DistroMapper.init(DistroMapper.java:100)
      at com.alibaba.nacos.naming.core.DistroMapper.<clinit>(DistroMapper.java:65)
      ... 79 common frames omitted
    

以上配置结束后,运行 Nacos Server 就能看到效果了。

除了 MySQL 的数据表发生了变化,咱们会发现NACOS_PATH/data下的目录结构也发生了变化,多了config-data/DEFAULT_GROUP/nacos_config这么一个文件,里边的内容就是咱们的配置

1
useLocalCache=true

这是容错呢? 仍是缓存呢?只有等看过源码才知道了。

服务发现

服务注册中心和服务发现的服务端都是由 Nacos Server 来提供的,咱们只须要提供 Service 向其注册就行了。

首先咱们先将 Nacos Server 由伪分布式改成 standalone 模式,缘由后边再说吧。

这里模拟提供两个 service:provider 和 consumer

1
2
3
4
5
6
7
8
alibaba
├── nacos-provider
│   ├── pom.xml
│   └── src
└── nacos-consumer
│   ├── pom.xml
│   └── src
└── pom.xml
  1. 首先在 provider 和 consumer 的 pom 添加依赖

    1
    2
    3
    4
    5
    6
    7
    8
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    
  2. 在二者的 bootstrap.yml 中添加配置

    provider

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    spring:
      application:
        name: nacos-provider
      cloud:
        nacos:
          discovery:
            server-addr: 127.0.0.1:8848
    server:
      port: 18080
    

    consumer

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    spring:
      application:
        name: nacos-consumer
      cloud:
        nacos:
          discovery:
            server-addr: 127.0.0.1:8848
    server:
      port: 18081
    
  3. 使用 Spring Cloud 的原生注解 @EnableDiscoveryClient 开启服务发现

    1
    2
    3
    4
    5
    6
    7
    
    @SpringBootApplication
    @EnableDiscoveryClient
    public class NacosProviderApplication {
        public static void main(String[] args) {
            SpringApplication.run(NacosProviderApplication.class, args);
        }
    }
    
  4. 提供 Endpoint 以供访问

    1. Provider

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      
      @RestController
      @RequestMapping("/echo")
      public class EchoController {
      
          @RequestMapping(value = "/{string}", method = RequestMethod.GET)
          public String echo(@PathVariable String string) {
              return "Hello Nacos Discovery " + string;
          }
      
      }
      
    2. Consumer

      在 NacosConsumerApplication 中集成 RestTemplate 和 Ribbon

      1
      2
      3
      4
      5
      
      @LoadBalanced
      @Bean
      public RestTemplate restTemplate() {
        return new RestTemplate();
      }
      

      提供 Controller

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      
      @RestController
      @RequestMapping("/echo")
      public class TestController {
      
          private final RestTemplate restTemplate;
      
          @Autowired
          public TestController(RestTemplate restTemplate) {
              this.restTemplate = restTemplate;
          }
      
          @RequestMapping(value = "/{str}", method = RequestMethod.GET)
          public String echo(@PathVariable String str) {
              return restTemplate.getForObject("http://nacos-provider/echo/" + str, String.class);
          }
      
      }
      
  5. 分别启动 NacosProviderApplication 和 NacosConsumerApplication ,调用 http://localhost:18080/echo/windmt 和 http://localhost:18081/echo/windmt ,返回内容均为 Hello Nacos Discovery windmt,说明服务发现成功了。

这时候查看 Nacos Console 也能看到已注册的服务列表及其详情

Nacos 服务列表

Nacos 服务详情

如今来说一下为何前边要将 Nacos Server 由伪分布式再改成 standalone 模式。

单个节点的 Nacos Server 伪分布式在配置管理运行的好好的,可是到了服务发现,它就失效了。

经过 log 能够发现一些端倪,单节点的在选主的时候,没法正确选出 leader

1
2
==> logs/naming-raft.log <==
2018-11-13 16:38:56,424 INFO leader timeout, start voting,leader: null, term: 1

从而致使 Client 没法正常注册

1
2
3
4
5
6
java.lang.IllegalStateException: failed to req API:/nacos/v1/ns/instance after all servers([127.0.0.1:8848]) tried
    at com.alibaba.nacos.client.naming.net.NamingProxy.reqAPI(NamingProxy.java:339) ~[nacos-client-0.3.0.jar:na]
    at com.alibaba.nacos.client.naming.net.NamingProxy.reqAPI(NamingProxy.java:272) ~[nacos-client-0.3.0.jar:na]
    at com.alibaba.nacos.client.naming.net.NamingProxy.registerService(NamingProxy.java:171) ~[nacos-client-0.3.0.jar:na]
    at com.alibaba.nacos.client.naming.NacosNamingService.registerInstance(NacosNamingService.java:161) ~[nacos-client-0.3.0.jar:na]
    ... ...

小结

当今年年初 Dubbo 进入 Apache 孵化器的时候,就有预感阿里要与 Spring Cloud 结缘。只是没想到这么快。

现在 Spring Cloud Alibaba 已经进入了 Spring Cloud 官方孵化器,相信等不了多久也就能正式发布了。虽然你们在生产环境必然还不会这么快速地接入,可是总归是多了一种选择。

而 Nacos 做为微服务核心的服务注册与发现中心,让你们在 Eureka 和 Consule 以外有了新的选择,开箱即用,上手简洁,暂时也没发现有太大的坑。但将配置中心融合也融合进来是好是坏,这个我先按下不表。

总而言之,Spring Cloud Alibaba 的入驻对于 Spring Cloud 生态总归是好的~

本文示例代码:https://github.com/zhaoyibo/spring-cloud-study/tree/master/alibaba

参考

GitHub - spring-cloud-incubator/spring-cloud-alibaba
GitHub - alibaba/nacos)
Nacos Documentation