Nacos使用和註冊部分原始碼介紹

大魔王先生發表於2021-01-19

Nacos簡單介紹

Nacos致力於幫助您發現、配置和管理微服務。Nacos提供了一組簡單易用的特性集,幫助您快速實現動態服務發現、服務配置、服務後設資料及流量管理。Nacos幫助您更敏捷和容易地構建、交付和管理微服務平臺。Nacos是構建以“服務”為中心的現代應用架構 (例如微服務正規化、雲原生正規化) 的服務基礎設施。
接下來主要介紹Nacos作為註冊中心的使用和註冊部分的原始碼解析。

Nacos安裝

Nacos預裝環境

Nacos 依賴 Java 環境來執行。如果您是從程式碼開始構建並執行Nacos,還需要為此配置 Maven環境這裡就不介紹Maven和Java安裝,大家自行安裝一下。

安裝的方式主要有兩種:
  1. 從GitHub上下載原始碼安裝:
//下載原始碼的地址
git clone https://github.com/alibaba/nacos.git
cd nacos/
//編譯原始碼
mvn -Prelease-nacos -Dmaven.test.skip=true clean install -U  
ls -al distribution/target/
//進入編譯包
cd distribution/target/nacos-server-$version/nacos/bin

2.下載安裝包的形式:

tar -xvf nacos-server-$version.tar.gz
cd nacos/bin
啟動伺服器

啟動命令(standalone代表著單機模式執行,非叢集模式):

sh startup.sh -m standalone
關閉伺服器
sh shutdown.sh
單機環境下使用Mysql:

在0.7版本之前,在單機模式時nacos使用嵌入式資料庫實現資料的儲存,不方便觀察資料儲存的基本情況。0.7版本增加了支援mysql資料來源能力,具體的操作步驟:

  1. 安裝資料庫,版本要求:5.6.5+
  2. 初始化mysql資料庫,資料庫初始化檔案:nacos-mysql.sql
  3. 修改conf/application.properties檔案,增加支援mysql資料來源配置(目前只支援mysql),新增mysql資料來源的url、使用者名稱和密碼。
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

對於單機來說我們就是玩個demo是夠的,但是對於生產來說我們就要考慮高可用的方案了:
對於叢集部署的方式如果在虛擬機器環境就採用Nagix負載3臺虛擬機器,對於K8S的環境通過Service負載3臺Pod的形式搭建高可用環境,對於資料庫選擇來說最好採用主從結構,保證資料庫的高可用。
整體的部署可能如下;


下圖就是搭建好以後的整體介面:

Nacos使用

1.建立一個Spring Boot空應用

2.編輯pom.xml檔案

    <properties>
        <springboot.vetsion>2.2.11.RELEASE</springboot.vetsion>
        <spring-cloud-version>Hoxton.SR9</spring-cloud-version>
        <spring-cloud-alibaba-version>2.2.3.RELEASE</spring-cloud-alibaba-version>
    </properties>
<dependencyManagement>
    <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${springboot.vetsion}</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>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba-version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>     
    </dependencies>
</dependencyManagement>
<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-discovery</artifactId>
    </dependency>
</dependencies>

3.建立應用的啟動專案

@SpringBootApplication(scanBasePackages = {"com.springcloud.study"})
@EnableDiscoveryClient
public class SystemApplication {

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

4.配置application.yaml檔案

server:
  port: 8081
spring:
  application:
    name: system
  # 資料來源配置項
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/study_user?useSSL=false&useUnicode=true&characterEncoding=UTF-8
    driver-class-namecom.mysql.jdbc.Driver
    username: root
    password: 123456
  #nacos基礎配置
  cloud:
    nacos:
      discovery:
        server-addr: 10.226.73.115:8886
        #環境選擇
        #namespace: b7d341fc-df29-45ce-b3cd-4415f66b1ee0

5.啟動專案

Nacos註冊部分原始碼分析

客戶端通過Rest介面式向Nacos Server註冊自己的服務,提供自身的後設資料,比如ip地址、埠等資訊。 Nacos Server接收到註冊請求後,就會把這些後設資料資訊儲存在一個雙層的記憶體Map中。對於註冊部分的原始碼整體上分為兩部分:

客戶端註冊原始碼

按照Spring Boot Starter的習慣,我們首先找到spring-cloud-starter-alibaba-nacos-discovery啟動項,如下圖所示:


標紅部分的NacosServiceRegistryAutoConfiguration是我們註冊部分關注的類,首先我們看下該類的整體的繼承結構:

重點關注AbstractAutoServiceRegistration介面,NacosAutoServiceRegistration該Bean的注入就是我們注入開始的:

整體看下該類,基本上是對AbstractAutoServiceRegistration繼承和實現,該類位於spring-cloud-common這個jar下面,Spring Cloud Commons模組是為了對微服務中的服務註冊與發現、負載均衡、熔斷器等功能提供一個抽象層程式碼,這個抽象層與具體的實現無關。這樣這些功能具體的實現上可以採用不同的技術去實現,並可以做到在使用時靈活的更換。
NacosAutoServiceRegistration內部存在一個@EventListener註解,@EventListener是一種事件驅動程式設計在spring4.2的時候開始有的,可以理解為ApplicationListener介面的擴充套件,方便我們使用,可以理解為Spring為我們提供的一個事件監聽、訂閱的實現,內部實現原理是觀察者設計模式;為的就是業務系統邏輯的解耦,提高可擴充套件性以及可維護性。事件釋出者並不需要考慮誰去監聽,監聽具體的實現內容是什麼,釋出者的工作只是為了釋出事件而已。

接下來我們重點看下AbstractAutoServiceRegistration內部start方法,

register的具體的實現類是NacosServiceRegistry,內部呼叫NacosNamingService的registerInstance方法,該方法內部通過呼叫NamingProxy的reqApi,通過NacosRestTemplate請求服務端的/instance方法註冊到服務端。


至此完成了客戶端註冊到服務端,下圖是整體的時序圖:
服務端註冊部分

服務端註冊相對比較複雜一點,這塊需要將Nacos原始碼下載一下,找到naming模組,InstanceController就是我們呼叫服務端的介面,如下圖所示:


接下來重點看下ServiceManager的registerInstance的方法,如下圖:

首先看下createEmptyService方法,該方法目的雙肩一個雙層的Map物件,用於儲存註冊應用的資訊,整體的結構Map(namespace, Map(group::serviceName, Service)),這裡正好對應註冊中心介紹時候的張圖,我們整體在看一下:


初始化的登錄檔結構以後,接下來就是將應用的資訊註冊新增到登錄檔中,主要分為兩步如下圖:

addIpAddresses就是獲取當前註冊服務的所有ip,整體的流程如下圖:

這裡的主要的重點是put方法,這裡我們全部按照CP的場景去解釋,AP的場景在未來的章節補充,CP的實現類是DistroConsistencyServiceImpl,如下圖:

通過把需要註冊到登錄檔的服務新增到阻塞佇列當中,Notifier本質上一個執行緒,然後通過執行run內部的hander方法,如下圖:

通過呼叫Service的onChange方法來變更登錄檔的資訊,內部主要通過updateIPs完成登錄檔資訊的表更,主要也是採用CopyOnWrite的思想,如下圖:

到此服務端的注入就完成了,這個裡面整體上有三處亮點:
1.CopyOnWrite的思想的廣泛應用;
2.通過阻塞佇列實現非同步任務提升系統效能,並且解決併發寫入問題;
3.觀察者設計的廣泛應用;

結束

歡迎大家點點關注,點點贊,感謝!

相關文章