Eureka源碼分析-環境構建篇

 

     承接上一篇文章《什麼是微服務》,我們已經對微服務有一定了解,並且以一個實現了註冊中心、服務提供者及消費者的例子作爲文章的結尾,而本篇文章,主要介紹Eureka源代碼的環境構建及示例調試。 

 

  1. 環境構建
  2. 調試方式
  3. 運行原理
  4. 示例調試

 

 

 

一、環境構建

學習一門新技術的原理和根本,最好的方式就是研究它的源代碼,搞懂它是如何誕生的,同時,也考慮下爲什麼這樣去設計?我們要相信,從每段代碼中,都能看出設計者當時的設計思路,通過源代碼,就能與設計者進行心靈上的溝通。

 

 

1、相關說明

據作者瞭解,剛接觸並選用Spring Cloud作爲實現微服務架構技術棧的同學,經常認爲是Spring Cloud提供了本章的主角-Eureka註冊中心,其實不然。Eureka的開發維護公司是大名頂頂的Netflix,並且這家公司也提供了不少優秀的開源產品,比如:Eureka、Zuul、Ribbon、Hystrix、Sidecar等,而Spring Cloud則是我們強大的開源社區在維護開發,並且它的誕生是建立在不斷集成及定製那些優秀的開源產品,比如:Netflix上面的那些開源產品,目前都已集成和發佈使用,Spring Cloud構建在Spring boot之上,欲建立一套標準體系的微服務整體解決方案,這可是不少程序員的福音,也是很多中小企業的福音。說到這裏,估計很多同學有些按耐不住了,別急,後續我會逐一介紹微服務架構體系的各種實現技術的原理及特點。

 

 

2、環境構建

第一步,我們先分析下Netflix的Eureka的源代碼,地址如下:

https://github.com/Netflix/eureka.git,同時,我們可以使用eclipse或Idea等提供的版本工具,如:Git/Svn等下載源代碼,這個地址下載得到的是主分支代碼,也是穩定和較新的代碼,當然,這裏我使用Git命令下載也可以(需要點時間:筆者環境下載時大概10分鐘),命令如下:

$git clone https://github.com/Netflix/eureka.git

 

第二步,源代碼拿到後,我們可以使用IDE工具將其打開,比如:我使用Idea導入源代碼,選擇以Gradle方式導入編譯(因爲Eureka官方源碼採用Gradle來管理)。另外,首次導入打開時,選擇採用本地還是源碼中提供的gradle暱?我建議使用源代碼中的gradle版本,因爲就作者寫文時,最新的gradle版本已經4.10,而源碼中默認提供的是2.10,兩者版本相差較大,如果使用較新的版本,可能出現各種編譯錯誤,所以建議採用源代碼提供的gradle編譯打包。接下來就是等待了,如果你已經安裝了合適版本的gradle,那您只需等待gradle下載Eureka框架的依賴包,這個時間比較長,大家耐心等待,建議聽首魔性的歌曲休息下哈。

 

第三步,相關依賴包下載完成後,我們試着啓動調試下Eureka。這裏我們先以生成war方式來調試,具體如下:

 

A、採用源代碼自動gradlew工具編譯打包或IDE環境gradle打包也可。 

$./gradlew  clean build

 

B、將編譯得到的war,放入你的Web容器(改名爲eureka.war),如:Tomcat

路徑: eureka/eureka-server/build/libs

包名: eureka-server-1.9.6-SNAPSHOT.war

 

如下圖所示:

 

 

C、當我們啓動Tomcat後,需要等待許久,就筆者已足足等待20分鐘,並且,運行解析過程中,可能會報如下錯誤(這些錯不影響運行war後,訪問控制檯頁面):

 

錯誤1:

TransportException: Cannot execute request on any known server

 

錯誤2:

java.net.ConnectException: Connection refused

 

 

D、將eureka-resources加入到build.gradle一起打包

經過上面的步驟,發現放入Tomcat的war不顯示頁面,因爲打包編譯時,並未把對應的jsp及css等文件一起打包,所以,我們需要將eureka-resources一起打包進去(若正常顯示,可忽略),具體如下:

 

$cd  eureka-server

$vim  build.gradle

 

加入依賴編譯eureka-resources包,截圖如下:

 

E、經過上面所有的處理後,激動的一刻到了,期待已久的註冊中心服務端控制檯頁面出現了,具體如下(比較簡陋哦):

 

看到上面的頁面結果,就說明eureka-server的war部署成功了。

 

 

二、調試方式

實際上,就Eureka源碼僅提供了兩種調試方式:一種是通過編譯生成的war方式,另一種方式,則是模擬Mock的方式,而下面介紹的第三種方式,其實是通過修改war包方式得來的,好處是不需要每次修改後都需build的時間耗費,只需直接運行程序即可。

 

 

1、模擬Mock方式

這裏介紹的是模擬方式,也就是採用與Eureka-Server實現邏輯相同的

MockRemoteEurekaServer作爲服務端,來模擬的測試功能。因爲是模擬,所以速度比較快,但又因爲是模擬,某些功能不建議依賴這種方式來測試,而建議採用下面的方式。那麼,先介紹下這種方式,它的優點很明顯,速度快,缺點是模擬的Mock更新較慢時,某些功能無法測試。但隨着Eureka2.x不再開發維護的消息到來,這種方式也是靠譜的。經過分析,所有測試單元,如果其父類爲AbstractTester,那麼,它使用的就是這種模擬Mock測試方式,具體是在運行setUp時,創建了MockRomoteEurekaServer,並初始化了Eureka相關的配置環境,

如eureka-core-resources中下面的測試單元都有采用Mock:

 

 

比如,我們選擇ApplicationResoucesTest來運行測試,因爲採用了Junit單元測試,所以,我們可以雙擊要測試的@Test,單獨測試之即可,這裏我測試下其中的testFullAppsGetJson獲取所有註冊的服務json信息,控制檯運行結果可視性不好,筆者截取下來,格式化了下,具體如下:

 

 

那麼,其它功能測試流程相同,各位同學自行試試吧!

 

 

2、編譯war方式

這種方式,其實就是每次修改調試時,必須重新build一次,生成新的eureka-server-x.x.x-SNAPSHOT.war包,然後,在startServer時,在程序中指定該war的位置,以便讀取裏面的配置信息,並最終將Server啓動,具體以下面截圖中測試單元爲例:

 

 

編譯生成war後,我們選擇上圖中任意測試單元測試,比如:選擇測試

@Test
public void testRegistration() throws Exception {
    InstanceInfo instanceInfo = 
instanceInfoIt.next();
    
EurekaHttpResponse<Void> httpResponse = jerseyEurekaClient.register(instanceInfo);

    
System.out.println("status-code:" + httpResponse.getStatusCode());
    
assertThat(httpResponse.getStatusCode()is(equalTo(204)));
}

 

爲了更清晰驗證,筆者臨時添加了System.out來打印結果,如果返回的結果碼爲204,就說明註冊服務測試成功,結果如下:

 

 

3、直接啓動方式

通過war包的調試方式比較費時間,且不靈活,接下來介紹的這種方式,是通過修改Eureka-Server默認的war調試方式,具體如下所示:

 

 

圖中被註釋掉部分,爲源碼提供的war包方式,高亮框起的部分,則爲筆者修改後的調試方式,起意就是換種方式將一個web程序所需的resources配置文件及核心的web.xml通過api方式注入到WebAppContext上下文中,這樣其它服務就可以通過HTTP訪問了。經過測試,此種測試方式的結果與通過war包方式結果相同,但更節省時間,所以推薦使用它。那麼,該如何使用暱?筆者會在本文第四部分的演示中,就使用這種方式來測試Eureka的運行過程,請繼續往下俯瞰。

 

 

三、運行原理

在介紹這個例子之前,我們有必要簡單地瞭解下Eureka的運行過程,並且官方已提供一個不錯的運行架構圖,具體結構如下:

 

Ps:

官方地址位置:https://github.com/Netflix/eureka/wiki/Eureka-at-a-glance 

 

至於上圖的運行原理,本篇僅簡單介紹下運行過程,在後續文章中,會陸續深入的介紹各個環節的核心內容,之所以在這裏談到,只因爲可以讓大家從整體角度認識Eureka註冊中心,也是爲第四部分做鋪墊 。從運行架構圖瞭解到,基本包含了4個核心角色,具體如下所示:

 

1、Eureka Server

Eureka註冊中心,負責服務的註冊與服務發現等功能,其是整個Eureka體系的核心中轉站,與下面提到的幾個角色都有交集,所以,它的負擔一般會比較大,生產環境下HA是必須的。

 

2、Application Service

該角色具體負責將各個服務註冊到Eureka Server,並提供服務續約及註銷等工作,可歸屬爲Eureka Client的一員。

 

3、Application Client

該角色具體負責從Eureka Server中查找需要的服務信息,再請求具體的服務信息,也可歸屬爲Eureka Client的一員。

 

4、Eureka Client

Eureka的客戶端,其分爲2和3兩種角色,從架構圖中可看出。

 

 

 

四、示例調試

在瞭解了第三部分內容後,再介紹本測試效果會更好,具體可分爲註冊中心服務端(Eureka Server)和第三部分所提到Application Service、Application Client及Eureka Client幾個角色,具體如下操作:

 

1、Eureka Server(對應eureka-server項目的test部分)

 

第一步:在setUp後,添加Thread.sleep(Long.MAX_VALUE),其意是讓Eureka-Server存活更長時間,這樣才能方便測試,具體如下:

 

 

 

第二步:修改eureka-server.properties配置文件

根據官方建議,在調試測試(僅限測試環境)時,取消下面的註釋:

eureka.waitTimeInMsWhenSyncEmpty=0

eureka.numberRegistrySyncRetries=0

 

Ps:

關閉掉Eureka Server的Replication的檢測,以免下面測試服務註冊時,頻繁檢測的工作。

 

第三步:選擇第一步測試類中任意@Test運行,此時你會發現它被阻塞,並繼續存活,這爲測試其它功能提供了時間。這裏,我們仍以testRegistration爲例運行Server。

 

 

2、SampleEurekaService(Application Service,對應項目eureka-examples)

 

因爲eureka-examples中的conf配置,只有在編譯時纔會讀取conf下的配置文件:

sample-eureka-client.properties和sample-eureka-service.properties

 

首先,這裏筆者直接將1中的injectEurekaConfiguration複製過來,並做了簡單修改,具體如下(關注註釋部分):

/**
 * This will be read by server internal discovery client. We need to salience it.
 */

private static void injectEurekaConfiguration() throws UnknownHostException {
    String myHostName = InetAddress.getLocalHost().getHostName()
;
    
String myServiceUrl = "http://" + myHostName + ":8080/v2/";

    
System.setProperty("eureka.region""default");     // 區域必須與註冊中心相同
    System.setProperty("eureka.name""sample-servide");    // 服務名修改
    System.setProperty("eureka.vipAddress""sample-service.mydomain.net");    // 虛擬VIP地址,供Application Client查找
    System.setProperty("eureka.port""8001");      // 獨立端口號,如果已被佔用,請換之
    System.setProperty("eureka.preferSameZone""false");       // 禁用Same Zone
    
System.setProperty("eureka.shouldUseDns""false");     // 禁用DNS
    
System.setProperty("eureka.shouldFetchRegistry""true");   // 需要注意,必須設置爲true,否則無法註冊
    System.setProperty("eureka.serviceUrl.defaultZone"myServiceUrl);     // 註冊地址,必須與註冊中心相同
    System.setProperty("eureka.serviceUrl.default.defaultZone"myServiceUrl);     // 註冊地址,必須與註冊中心相同
}

 

 

然後,我們在啓動ExampleEurekaService中的main方法前,在其首行添加injectEurekaConfiguration的調用,直接啓動即。此時,我們在控制檯看到如下信息時,代表服務與Server通信及註冊成功了,並且該服務在不斷的檢測是否有Client發來請求,只要檢測到了一個成功請求,那麼它的工作也就結束,並被停止,如下:

 

 

3、SampleEurekaClient(Application Client,對應項目eureka-examples)

 

首先,injectEurekaConfiguration複製過來,並做了簡單修改,具體如下(關注註釋部分):

/**
 * This will be read by server internal discovery client. We need to salience it.
 */

private static void injectEurekaConfiguration() throws UnknownHostException {
    String myHostName = InetAddress.getLocalHost().getHostName()
;
    
String myServiceUrl = "http://" + myHostName + ":8080/v2/";

    
System.setProperty("eureka.region""default");     // 區域與註冊中心保持一致
    System.setProperty("eureka.vipAddress""sample-service.mydomain.net");    // vip地址必須與服務定義相同,否則找不到服務
    System.setProperty("eureka.shouldFetchRegistry""true");           // 需要注意,必須設置爲true,否則無法註冊
    System.setProperty("eureka.serviceUrl.defaultZone"myServiceUrl);     // 註冊地址,必須與註冊中心相同
    System.setProperty("eureka.serviceUrl.default.defaultZone"myServiceUrl);     // 註冊地址,必須與註冊中心相同
}

 

然後,我們在啓動ExampleEurekaClient中的main方法前,在其首行添加injectEurekaConfiguration的調用,運行結果如下:

 

我們也看到,在ExampleEurekaClient結束之後,ExampleEurekaService也結束了運行,正好驗證了ExampleEurekaService只服務一次哦。

 

 

好了,本篇文章就介紹到這裏,後面會繼續介紹Eureka的源碼分析相關的文章,敬請期待!由於原創文章梳理費時費力,覺得還不錯的讀者朋友,可以關注下面的個人公衆號,以便及時查閱新的文章,謝謝。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章