Eureka原始碼分析-環境構建篇
承接上一篇文章《什麼是微服務》,我們已經對微服務有一定了解,並且以一個實現了註冊中心、服務提供者及消費者的例子作為文章的結尾,而本篇文章,主要介紹Eureka原始碼的環境構建及示例除錯。
- 環境構建
- 除錯方式
- 執行原理
- 示例除錯
一、環境構建
學習一門新技術的原理和根本,最好的方式就是研究它的原始碼,搞懂它是如何誕生的,同時,也考慮下為什麼這樣去設計?我們要相信,從每段程式碼中,都能看出設計者當時的設計思路,通過原始碼,就能與設計者進行心靈上的溝通。
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分鐘),命令如下:
第二步,原始碼拿到後,我們可以使用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後,我們選擇上圖中任意測試單元測試,比如:選擇測試
@Testpublic 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的原始碼分析相關的文章,敬請期待!由於原創文章梳理費時費力,覺得還不錯的讀者朋友,可以關注下面的個人公眾號,以便及時查閱新的文章,謝謝。