來自京東、宅急送對微服務編排、API網關、持續集成的實踐分享(下)

架構師小組交流會:每期選一個時下最熱門的技術話題進行實踐經驗分享。

第三期:微服務。微服務架構以其高度的彈性、靈活性和效率的巨大提升,快速受到各領域架構師和技術決策者的關注。它的基本理念是將一個肥大的系統拆分成若干小的服務組件,組件之間的通訊採用輕量的協議完成。我們本期小組交流會來探討一下,現在互聯網公司的微服務實踐情況。

嘉賓:京東章耿、宅急送石廷鑫、七牛陳愛珍
本文是對此次交流的整理,分了上下兩篇文章。

第一輪:話題交流
主持人:API 網關是怎麼設計的?
京東章耿:
我們有個 HTTP 的網關,但不是一個對外網服務的一個網關。對外的話業務自己都有一些網關,例如無線有自己的網關,網站有自己的網關,而另外一個公共的開放的是一個叫京東開放平臺的網關。然後我們的網關幹嘛用的?主要做的就是一個跨語言支持用的,協議轉發和限流。這個網關存在的意義,主要是爲了讓有的調用端不用傑夫協議,或者不依賴服務端的情況下,來調服務端的服務。

最主要就是剛纔說那個 HTTP 轉成內部協議的一個轉發的功能,其次我們在上面做了一個接口級隔離,不要一個接口就把網關搞掛。還有一個就是限流,每分鐘調多少次。還有授權,以前的網關轉發的做法,一般是比如說類似於一個 VIP,然後前面掛個域名。然後那個虛 IP 後面掛的服務列表一般都是要手動維護上。而我們的網購自動就掛到這個網關上面,就是一個服務發現,還有就是我們的結果,統計方面,我們會統一包裝一下,我們的網關主要是做這個功能,現在一天應該是幾十億的調用量,九十臺,差不多這些。

服務發現是到數據庫裏面去讀服務列表,從註冊中心讀出來以後會推給我們的網關。網關跟調用者是類似的。它其實也是一個調用者,只不過它是一個代理的調用者。它的服務列表,也是從我們的註冊中心訂閱的,不直接連數據庫,可以認爲自己是調用者。網關第一次去註冊中心去讀,後面的話我們可以推變化的部分。比如說你原來 1000 臺,你要是加了一臺,按以前拉的思路你會拉 1001 臺,然後你自己比較一下,多了哪一臺。但我們現在不是,我們現在是反向給他推加一臺。這樣的話,大量的減少那個,還有網絡 IO 的推送。

京東章耿:我們想用 nginx+luaw 做一個 HTTP 轉我們內部 JSF 協議的,但不是 worker 就是一個進程。會產生很多長鏈接,所以我們後來就放棄了,我們現在是基於 nitty 做了一個轉發,就是對外是 HTTP,對內就 JSF 協議。也做了一些授權,限流,還有服務之間的線程隔離,服務發現,還有一個是結果的包裝,包裝成標準的 HTTP 的響應。因爲像對外網的那些其實都是有自己的系統,不管你是無線,還是 PC 他都有自己的系統,那個不需要我們做。對第三方的話,它也有其中京東一個開發平臺,還有更嚴格的那個驗證,我們這個主要還是做協議轉換的 API 網關。


主持人:你們怎麼驗證請求的合法性的,你們採用什麼方法?就是就那種效率與安全性的這種平衡你們怎麼做的?

京東章耿:我們是有個授權,就是有個應用 ID,京東是每個啓動的都有個應用 ID,帶着那個應用 ID 過來,我們也可以支持頭上帶 token。京東開放的那種是對外比較嚴格,我們這個不需要那麼嚴格,反正就看你對象,就看你的網關給誰用了。

主持人:你們現在有兩種類型,一種是內部之間調用的,另外一部分是外部調用內部的調用你們系統。
京東章耿:
那個是開放服務,有些供應商內部的系統,想要調京東的系統,那種就是京東開放服務,是需要 Oauth 認證。

京東章耿:HTTP+keepalive 也挺快的,因爲它無非就是頭上大一點,HTTP 的頭大了一點。但如果後臺是調 redis 那就比較明顯的感覺,那如果後臺是一個有個幾百毫秒的,那你感覺不到那麼明顯。如果後臺,你這就是讀不取一下,讀一下 redis,你感覺比較明顯。我們這邊是用 netty 做的 HTTP 跟二進制都是在同一個端口支持的。


主持人:你怎麼劃分,哪些用二進制的,那些用 restful 協議的呢?

京東章耿:那個我們沒有強制要求,業務它自己想用什麼用什麼。

京東章耿:對我們來說,它一啓動端線口它就支持這兩種協議。啓動同一個端口,兩種協議都支撐的。

主持人:你們是怎麼區分一種端口種協議的呢?

京東章耿:每個數據包括頭上前兩位不是模數位嗎?它們都有自己的模數位。然後我們自己協議有自己的模數位,你 HTTP 就是那幾個打頭的 H,然後我們的 decode 是自動裝載的,它不是說你可以一開始裝載一個什麼那是適配器 decode。當你請求來的時候,你再自動裝載量,因爲我們是超鏈接,不管你是 HTTP,我們一般都默認開啓 keepalive 也是個超鏈接。其實,你可以知道這個長鏈接對應的是什麼協議。


主持人:它一般保持穩定的一個超鏈接,肯定是一種協議持續下去,不可能說動態的變質。

京東章耿:是,看效率要求,其實 HTTP keepalive 也還可以,性能也還可以,如果不是那種調量特別特別大的話,它效率也還是可以的。然後 debug 的時候可能可讀性會好一點。二進制最大問題還是比較麻煩,特別是,我們現在用 message pack,然後會生成一堆的代理類,模板類,反正問題也比較麻煩。

宅急送石廷鑫:我們都用 Spring cloud 的那一套,然後自個改了一部分東西。像 Consul 好像也和 Zookeeper 一樣的問題,所以說後邊都改造數據庫了。我現在用的是開源的 eureka,只是後邊從屬變了。目前來說還沒發現問題,因爲我沒有跨機房的問題。如果是跨機房的話,基本上都是數據庫同步,兩個數據之間同步的問題。

京東章耿:一般我們是有一個功能服務降級,其實最主要還是業務部門自己的代碼實現,那我們其實有提供一個 mok 功能,就是那在我們配置這邊直接配上一個,如果這個接口不可用返回的什麼東西有沒有開關,這也是可以。但是這個用起來比較少,一般他們自己在業務代碼上也是容錯的,就是沒有說從平臺這種角度去搞,一般都是自己考慮。

然後如果是有一個目視跟蹤系統的話,就一般的也可以跟蹤整個調用鏈,就會看出來這個。比如說這個接口它依賴其他的接口,然後京東其實是沒有投資這麼細,因爲目前我們分公司跟蹤還沒有上,我們現在主要是依賴我們內部的一個應用管理系統,我們叫 JOne,有點像自動部署。我們每個進程啓動的時候都會帶上這個應用 ID,在我們管理端是能看到這個接口是屬於哪個應用的,我們只能看到應用級別的,這個應用調了哪些接口?哪些接口依賴?調的那些接口還被誰調用了?到這個級別。

宅急送石廷鑫:我們用 Springcloud,熔斷機制的降級處理的話,它有一個統計的接口,基本上按那個規則來做。調用關係的話,一個是我們做了一個 trace ID,就是 google zipkin,它本身有自帶的工具。還有一部分就是服務的排層,我們現在就是用 camel 來做的,把這個業務整個來排層次做,大體是這樣。目前來說,大的情況就是監控時會有一些出經常會出現一些抖動。就比方說 trace ID 那部分,不能用它自帶的 stream 的模式。我們現在還是用 elk 來做,然後把 trace ID 打出來,然後做了一套簡單的監控,類似於模仿它整個 trace 的路徑。當然不要用 google 自帶的監控,我們發現機器多了自帶的監控不太可靠。我們都是做到日誌裏面,然後用 elk 收集起來。說起來自個做一個監控的調用量,這個就是稍微有點延遲。

京東章耿:我們這邊最近正在做,然後我們的思路是這樣的。有個包放在應用裏面,它會輸出日誌,然後我們有一個日誌收集,原來就有日誌收集的我們只是擴展了一下。在每臺機子上把它收到一個 kafka 裏面,然後後面是用一個 storm 去把它讀出來,寫到 H base 裏做分析,然後我們是有個採樣率的一個概念,比如說一千次,才寫一次,或者是一萬次才寫一次,做個採樣率。然後最終我們現在是分兩部分,剛纔說寫 H base 是一個離線數據,其實我們還有一些簡單例子,就是直接做一些統計,實時的,大概延遲在一分鐘左右。

主持人:關於服務的 Docker 化有什麼進展?

京東章耿:我們主要還是應用級別的 Docker。現在只是說,可能這種發佈模式會改一下。現在是基於一個 Docker VM,比如說你起來以後,其實整個鏡像文件都在那裏。然後你彈的時候其實還是比較慢的。比如我要擴的話,得先創建一個 Docker 的 VM,再把那些東西複製進去,纔能有個裝機的過程。就是比較慢一點,可能得分鐘級別才能把它給提起來。但是未來我們希望把它改用鏡像的那種形式,就你上線完成以後,生成一個鏡像。每次上線,你只需要布一臺機器,後面全是複製的一個過程。未來會改成這樣,估計今年開發,明年推。現在相當於要布 20 個節點,那相當於是給你 20 個 Docker VM,你上線發佈 20 次,未來是希望給你一個,然後你發佈一次以後,系統自動給你複製 19 個。而且反正後面服務發現什麼這些都是原生的,都是無所謂的。

京東章耿:京東的 Docker 主要解決資源調度的問題。就相當於現在部物理機,你可能自己要需要部署機器 。但 Docker 可以把資源分配均勻一點,用算法給算出來,在分配時不會分到同一個機架上,不會分到同一個主機上,還有不會分到很繁忙的機器上。這些都會幫你考慮一下。

京東章耿:京東這邊是自己有一套部署系統,雖然他沒有像你說就鏡像這樣發佈,雖然沒這麼快,但對於我們開發人員上線來說,其實是一樣的,他只需要配一下,然後一點,他 24 臺自動就上去了,就是有一套工具,也很快。只不過,他需要提前創建好,比如說你剛纔說 20 個,你要提前創建 20 個 VM。就比鏡像的話肯定是要慢在這一步,你鏡像的話,你直接拉下來一起,然後可以調度到哪臺機子上到個 Docker API 一調,他直接就提起來了,那也是我們未來的改變方向。

七牛陳愛珍:我們的數據處理系統系統上運行的都是 CPU 密集型的計算,獲取一個原文件,進行數據處理算法的執行,比如對一個視頻進行轉碼,而在轉碼的過程中需要大量的 CPU 資源來進行處理,處理完後就可以獲得預設的文件樣式。不同的數據處理對資源的需求是不一樣的,比如獲取一個文件的 hash 值,這個處理邏輯非常簡單,也沒有大量的運算,配置的資源就相對小一些,而視頻轉碼則非常的複雜,配置的資源就會相對多一些。

現在在我們的平臺上運行了數千種數據處理應用,每種處理的的請求量不一樣,比如有些圖片處理每秒可以達到數十萬的請求量,而有一些則可能是每秒幾萬的請求量,幾千種數據處理應用的高峯期也不一樣,有些可能在早上,有些可能在晚上,並且每種處理都會存在突發流量的情況,比如一些電商型的客戶,在做大促銷時,就會導致圖片處理的請求量突增,而一些音視頻的客戶在會在一些活動時會突然增長對音視頻的處理。

這個系統的核心問題是如何把硬件資源對每一種應用不同高峯期的請求量和突發流量做合理的資源分配,不合理的資源分配就可能會造成資源的浪費,或導致負載過重的機器會宕機,不能及時響應用戶的需求。原有的系統架構的資源是靜態規劃的,也就是把指定的資源給指定的應用使用,而這種資源的分配往往是按照每個應用的業務高峯進行規劃的,爲了應對突發的流量並會預設一定的冗餘,那麼這樣就會需要準備很多的資源。後來我們使用容器,因爲容器可以封裝環境,動態遷移,底層使用 Mesos 做資源的調度,這就可以對整個環境按需動態分配,很好的解決了資源利用率的問題。


主持人:關於服務測試、持續集成,大家分享一下實踐經驗吧。

京東章耿:持續集成我們這邊其實在編譯環節就做了,上線我們這邊是有一個灰度上線功能。我們有一個預發佈環節,它可以直接把它標記爲預發,然後有個測試平臺可以對它進行一個服務的測試。那如果是正式環境的話,那就他就得自己想辦法,因爲我們現在這個環節是不能隨便測試的,因爲我無法判斷你這個是讀還是寫,我不能讓你隨便測。

主持人:因爲你們是把一個業務系統拆成了很多這種服務化的服務去跑,那肯定是要涉及到這種單元測試、集成測試和業務流的這種測試,那這種測試的話你們都是怎麼做的呢?

京東章耿:這邊都是提前測的,就是你測都沒測,那你根本就提不到上線這一步。你上線的時候必須有一個測試審批通過,其實他其實就是已經在線下就測過了。

七牛陳愛珍:我們是基於 Jenkins 做的持續集成,把代碼上傳到 Github 後,會做自動的代碼靜態檢查和單元測試,再做自動的集成測試。這樣的好處是開發只需要開發代碼,不需要構建環境,都是自動完成的,而且反饋的速度也會非常快速。

宅急送石廷鑫:測試環境是由開發人員去部署,線上正式環節,是從這個測試環境把報測試通過去的,直接拷貝過去。我覺得 Docker 是解決整個配置的問題,因爲生產環境測試環境和開發環境不一樣。配置環境這個東西很難很麻煩做的,儘可能就是 UAT 的環境和測試環境,就是用戶測試的環境和線上的環境儘量是一樣。現在不是有配置管理嗎?這三個環境能來回切換。比方說 spring boot 這種,實際上就是一個 Jar 包命令。Jar 包命令唯一不同的就是它的配置問題,你只要一上線,那邊一監控就可以看到。因爲你啓動時候他有註冊,註冊基本上他要調哪些東西就能看到他註冊的配置。

京東章耿:京東測試環境,預發,線上都是從源碼庫裏編譯爲準。主幹編譯爲準,所以說代碼應該是一樣的。線上配置我們是管理在配置中心,但是目前我們測試環境就沒有這個東西。預發,線上有配置中心,這個配置中心也集成到我們那個發佈平臺 JOne 上。


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