天天看點

Alibaba之Nacos詳解

上個月最後一天的淩晨,Spring Cloud Alibaba 正式入駐了 Spring Cloud 官方孵化器,并在 maven 中央庫釋出了第一個版本。

目前 Spring Cloud Alibaba 還隻能算是預覽版吧,裡邊的坑肯定不少,不過我還是決定試試,看看 Alibaba 到底靠譜不靠譜。

Alibaba之Nacos詳解

一、Spring Cloud Alibaba

目前 Spring Cloud Alibaba 項目還處于 Spring Cloud 官方孵化器中,打開它 Github 的就能看到 “親切” 的中文文檔。

它目前隻有三個元件:

  • Sentinel:把流量作為切入點,從流量控制、熔斷降級、系統負載保護等多個次元保護服務的穩定性。
  • Nacos:一個更易于建構雲原生應用的動态服務發現、配置管理和服務管理平台。
  • AliCloud OSS: 阿裡雲對象存儲服務(Object Storage Service,簡稱

    OSS),是阿裡雲提供的海量、安全、低成本、高可靠的雲存儲服務。您可以在任何應用、任何時間、任何地點存儲和通路任意類型的資料。

看官方路線圖上講後邊還會增加:

  • Dubbo:Apache Dubbo™ (incubating) 是一款高性能 Java RPC 架構。
  • RocketMQ:Apache RocketMQ™ 基于 Java 的高性能、高吞吐量的分布式消息和流計算平台。
  • 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

Alibaba之Nacos詳解

這是 Nacos 的架構圖,可以看到它确實是融合了服務注冊發現中心、配置中心、服務管理等功能,和我之前猜想的它是 Eureka/Consule + Config + Admin 的合體差不多。

另外通過官方文檔發現,Nacos 除了可以和 Spring Cloud 內建,還可以和 Spring、SpringBoot 進行內建。

不過我們隻關注于 Spring Cloud,别的就略過了,直接上手吧~

工程的目錄結構如下:

alibaba
├── nacos-config
│   ├── pom.xml
│   └── src
├── nacos-consumer
│   ├── pom.xml
│   └── src
├── nacos-provider
│   ├── pom.xml
│   └── src
└── pom.xml
           

首先引入 Spring Cloud Alibaba 的 BOM

<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 報錯

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 為例

unzip nacos-server-0.4.0.zip
cd nacos
           

1、standalone 模式

此模式一般用于 demo 和測試,不用改任何配置,直接敲以下指令執行

sh bin/startup.sh -m standalone
           

Windows 的話就是

cmd bin/startup.cmd -m standalone
           

然後從 http://localhost:8848/nacos/index.html 進入控制台就能看到如下界面了

Alibaba之Nacos詳解

2、cluster 模式

叢集模式需要依賴 MySQL,然後改兩個配置檔案:

conf/cluster.conf
conf/application.properties
           

具體怎麼改,在這裡就先不展開了。我們先用 standalone 模式撸起來,享受 coding 的快感,然後再慢慢轉到 cluster 上邊。

四、配置管理

在 nacos/pom.xml 裡添加依賴:

<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>
           

啟動類不用修改:

@SpringBootApplication
public class NacosApplication {
    public static void main(String[] args) {
        SpringApplication.run(NacosApplication.class, args);
    }
}
           

修改 bootstrap.yml

spring:
  application:
    name: nacos
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
           

注意:必須是寫在 bootstrap.yml 中,配置在 application.yml 中不行,啟動報錯

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

至于 bootstrap.yml 和 application.yml 的差別,之前講過這裡就不贅述了。

添加一個 Endpoint 便于觀察

@RestController
@RequestMapping("/config")
@RefreshScope
public class ConfigController {

    @Value("${useLocalCache:false}")
    private boolean useLocalCache;

    @RequestMapping("/get")
    public boolean get() {
        return useLocalCache;
    }
}
           

注意一定要加@RefreshScope注解

小心!此處有坑!

這時候先别急着啟動 NacosConfigApplication 的,需要需要通過調用 Nacos Open API 往 Nacos Server 裡釋出一個配置。dataId 為 nacos.properties,内容為useLocalCache=true

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 的完整格式如下:

prefix−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.prefix.{file-extension}
  • file-exetension 為配置内容的資料格式,可以通過配置項 spring.cloud.nacos.config.file-extension 來配置。目前隻支援 properties 和 yaml 類型。

然後啟動 NacosConfigApplication,從啟動日志裡能看到

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

如果 propertySources 裡邊是空的,那抱歉,你掉到坑裡邊了。 如果你能看到之前釋出的 dataId,那恭喜,請求 http://localhost:8080/config/get 就可以看到傳回内容 true 了。

再次調用 Nacos Open API 修改内容為useLocalCache=false

再次通路 http://localhost:8080/config/get ,此時傳回内容為false,說明程式中的useLocalCache值已經被動态更新了。

當然,以上手動調用 Nacos Open API 的方式也可以通過 Nacos Console 的可視化界面來操作

Alibaba之Nacos詳解

另外我們可以查詢配置的曆史記錄并能快速復原

Alibaba之Nacos詳解

還能查詢到某個配置目前的被監聽狀态(這裡的分頁有些 bug)

Alibaba之Nacos詳解

五、資料源

經過了上邊的一些簡單操作,我們已經可以正常使用 Nacos 配置中心了。

但是不知道你有沒有想過:配置資料是存在哪裡呢?

我們沒有對 Nacos Server 做任何配置,那麼資料隻有兩個位置可以存儲:

  • 記憶體
  • 本地資料庫

如果我們現在重新開機剛剛在運作的 Nacos Server,會發現剛才加的 nacos.properties 配置還在,說明不是記憶體存儲的。

這時候我們打開NACOS_PATH/data,會發現裡邊有個derby-data目錄,Derby 是 Java 編寫的資料庫,屬于 Apache 的一個開源項目。我們的配置資料現在就存儲在這個庫中。

Derby 我并不是很熟悉,那能不能将資料源改為我們熟悉的 MySQL 呢?當然可以了。

注意:不支援 MySQL 8.0 版本

這裡我以本地運作的 MySQL 為例:

建立一個名為nacos_config的 database

将NACOS_PATH/conf/nacos-mysql.sql中的表結構導入剛才建立的庫中,這幾張表的用途就自己研究吧

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

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
           

建立cluster.conf,填入要運作 Nacos Server 機器的 ip

192.168.100.155
192.168.100.156
           

我就是運作個 demo,沒有多餘機器來組建叢集怎麼辦呢?

其實不用虛拟機,直接隻填一個本地位址也是可以的(僅限于配置管理,服務發現不行)。

這裡有兩個坑:

Nacos Server 的資料源是用 Derby 還是 MySQL 完全是由其運作模式決定的:

standalone 的話僅會使用 Derby,即使在 application.properties 裡邊配置 MySQL 也照樣無視;

cluster 模式會自動使用 MySQL,這時候如果沒有 MySQL 的配置,是會報錯的。

官方提供的 cluster.conf 示例如下

#it is ip
#example
10.10.109.214
11.16.128.34
11.16.128.36
           

從習慣來看,這個#号後邊的應該就是注釋的,但是抱歉哦,必須删掉,否則下面的異常就撲面而來

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這麼一個檔案,裡邊的内容就是我們的配置

useLocalCache=true
           

這是容錯呢?還是緩存呢?隻有等看過源碼才知道了。

六、服務發現

服務注冊中心和服務發現的服務端都是由 Nacos Server 來提供的,我們隻需要提供 Service 向其注冊就好了。

首先我們先将 Nacos Server 由僞分布式改為 standalone 模式,原因後邊再說吧。

這裡模拟提供兩個 service:provider 和 consumer

alibaba
├── nacos-provider
│   ├── pom.xml
│   └── src
└── nacos-consumer
│   ├── pom.xml
│   └── src
└── pom.xml
           

首先在 provider 和 consumer 的 pom 添加依賴

<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>
           

在兩者的 bootstrap.yml 中添加配置

1、provider

spring:
  application:
    name: nacos-provider
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
server:
  port: 18080
consumer

spring:
  application:
    name: nacos-consumer
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
server:
  port: 18081
           

使用 Spring Cloud 的原生注解 @EnableDiscoveryClient 開啟服務發現

@SpringBootApplication
@EnableDiscoveryClient
public class NacosProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(NacosProviderApplication.class, args);
    }
}
           

提供 Endpoint 以供通路

@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

@LoadBalanced
@Bean
public RestTemplate restTemplate() {
  return new RestTemplate();
}
           

提供 Controller

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

}
           

分别啟動 NacosProviderApplication 和 NacosConsumerApplication ,調用 http://localhost:18080/echo/windmt 和 http://localhost:18081/echo/windmt ,傳回内容均為 Hello Nacos Discovery windmt,說明服務發現成功了。

這時候檢視 Nacos Console 也能看到已注冊的服務清單及其詳情

Alibaba之Nacos詳解
Alibaba之Nacos詳解

現在來講一下為什麼前邊要将 Nacos Server 由僞分布式再改為 standalone 模式。

單個節點的 Nacos Server 僞分布式在配置管理運作的好好的,但是到了服務發現,它就失效了。

通過 log 可以發現一些端倪,單節點的在選主的時候,無法正确選出 leader

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

進而導緻 Client 無法正常注冊

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 結緣。隻是沒想到這麼快。