京東、宅急送的微服務實踐分享(上)| 架構師小組交流會

七牛雲發表於2017-01-19

架構師小組交流會是由國內知名公司技術專家參與的技術交流會,每期選擇一個時下最熱門的技術話題進行實踐經驗分享。

第一期:來自滬江、滴滴、蘑菇街、扇貝架構師的 Docker 實踐分享

第二期:來自滴滴、微博、唯品會、魅族、點評關於高可用架構的實踐分享

本期話題:微服務。微服務架構以其高度的彈性、靈活性和效率的巨大提升,快速受到各領域架構師和技術決策者的關注。我們本期小組交流會邀請到了國內電商領頭羊京東、宅急送技術負責人,一起探討最前線的微服務實踐情況。


自由交流


京東章耿

大家好,我是京東基礎架構部平臺中介軟體的章耿,主要負責京東的服務框架,配置中心等專案。京東 2011 年底進行 .NET 轉 Java 的技術變革,當時就提出了介面的 SOA 化的概念。那時京東業務發展非常快,專案越來越多,服務化是必然的趨勢。

京東的服務化框架是一共有兩代。第一代是 2012 年開始,我們使用開源的一個實現,用 Dubbo 作為 RPC 框架,Zookeeper 作為註冊中心,那時應該算一個主流的技術選型。我們在開源的基礎上做了一些易用性和功能擴充套件,比如說支援 REST、支援 kryo/thrift 等序列化,服務監控等,這個框架在那時的業務規模和節點數量下面還是比較穩定的。

2014 年初我們開始自研服務框架。為什麼這麼做呢?一個是之前的服務框架還是有很多可以提升的地方,不管是效能還是服務治理的一些功能,因為京東的機器數,機房也在不停的建,網路拓撲的複雜直接需要高階的服務治理功能;還有一個就是介面節點等數量級的增加,當時我們的介面規模慢慢的已經好幾千了,介面的例項也幾十萬;另外就是用開源的有些內部系統打通比較麻煩,所以我們要做升級換代。當時也是考量了用開源的改改,還是全部自己重新做的抉擇。後來覺得一個是有時間,二是覺得自己有能力做一個符合京東自己的定製化的服務框架,所以 2014 年我們就開始自研框架,做了整整一年。2015 年初我們上線新版的服務框架。

我們新的註冊中心,是無狀態的一些節點,基於資料庫做最終一致性。為什麼選資料庫,替換以前的 Zookeeper?因為 Zookeeper 是樹狀結構,從某些維度查詢它要遍歷整棵數,用資料庫的好處就是可以從多維度的查詢分析過濾,不管是機房、 IP,都可以去查詢,分析,過濾比較結果。然後 Zookeeper 在跨機房的時候有一個問題,它是半數節點存活才可以用,所以如果跨機房部署的話,最起碼得 3 個機房,才能保證叢集整體可用。還有 Zookeeper 它是強一致性的的,比較依賴網路。如果網路不好,如果跨機房斷網的時候,它其實是不可用的,讀都不能讀,所以會帶來一定的問題。以前用 Zookeeper 註冊服務端,就是寫一個臨時節點,等服務端死了節點自動消失的。但是你在網路抖動的時候,或者是服務端和 Zookeeper 之間有網路故障的時候,它其實會不小心把它摘掉,因為 Zookeeper 認為它死了,但其實它不一定真的死了,所以這個時候就需要有一些輔助的去判斷它是不是真的死了。

第一次遇到的情況是那個臨時節點,後來我們是把它改成永久節點,然後定時的去 telnet 它的埠,像 Dubbo 是直執行 ls 命令,我們也是直接執行一個命令,看它有沒有響應,那是第一代的時候。在 JSF 的框架裡有一個哨兵的元件,我們的 Provider 會定時的發心跳,對於註冊中心來說,它知道最後的心跳時間,然後定時刷到資料庫裡面,看哨兵的情況,如果沒有哨兵,資料庫裡面儲存一個最後心跳時間,這時候你可以用定時任務去找它,這是我們最早的一個版本。

後來我們改進了,因為有時斷網了,這個心跳時間就不準了。所以在每個機房還布了一套獨立的程式,沒心跳的同時再由它來判斷是否可用,如果這個同機房的程式都連不上,我們再把它置成一個不可用的狀態,雙重保證。另外以前 Zookeeper 的時候服務列表是下發的全量列表,例如 1000 臺加一臺,下發的是 1001 臺,而新版註冊中心我們是推變化的部分,只推加 1 臺,大大節省了資料的推送量。

RPC 框架,我們內部叫傑夫,JSF,京東服務框架的簡稱。我們是用基於 Netty 自己研發的。為了相容上一代版本,相容之前的 dubbo 協議,所以我們也是用的無程式碼入侵的;我們在同一個埠支援多協議,包括自定義的 JSF 協議,http 協議,dubbo 協議等。目前我們只有 C++ 和 Java 的客戶端,然後如果是其它語言例如 Golang,Python,PHP 的話,我們都會讓他向我們的一個 HTTP 的閘道器發請求,這個閘道器主要就是轉發。把前面的 http 請求轉換到後面一個 JSF 請求,也是基於 Netty 做的。序列化預設是 Message Pack,是個跨語言的協議,也支援 Hessian,Json,Java,Protobuf 等。註冊中心和 RPC 框架直接是長連線,可以進行一個通訊。

宅急送石廷鑫

大家好,我是石廷鑫,原來在京東、唯品會,現在在宅急送。我基本上都是在倉儲物流這方面。以前在亞洲一號是按照倉庫的整個操作細節分成各個模組來做的服務拆分,每個模組是單獨部署的。倉儲的每個倉,根據種品類不一樣,整個的操作流程是不一樣的。但是核心的東西,如庫存、訂單管理,都是一樣的,只是一些組合不一樣。舉個例子,小件商品上下架都要做的,但是對於大件商品,比如說大家電,基本上都不需要放架。打訂單也不需要,完成配送才需要訂單,實際上是到了最後才繫結訂單。所以每個流程不一樣,需要的組合也不一樣。那時我們就想到了,把每個模組先拆出來,主要是用 KVM 執行整個節點,業務部分就用這些節點來整個串聯起來。核心的東西比如說庫存、訂單、商品資料,基本上都是用這個簡單的模組。

唯品會的倉儲也是按這種思路來做的。當時唯品會用的是 Dropwizard,實際上是我們用了它的一個殼,後面還是用的 Spring,也做了一個模組,把 Spring 整合到一塊。後來 Springboot 做了出來,基本上就把 Dropwizard 拋棄掉了。因為 Dropwizard 版本升級變化比較大。但它有一個好處,就是 Bundle 比較好,服務化拆分的時候可以根據它的大小進行可分可合,如果不需要拆分的時候,我們就把 Bundle 合到一塊,如果需要拆分的話,就把 Bundle 再拆出來,單獨這個應用是非常容易的。

我去年到的宅急送,我們的服務註冊、服務發現、閘道器都使用的 Spring Cloud的這一套。目前來說還是不錯的,基本沒發現大的毛病。唯一的問題是如果前期沒有做好的整個資料抽取,整個報表可能會比較麻煩一些。

七牛雲陳愛珍

大家好,我是七牛雲的陳愛珍。微服務架構的設計理念非常適合七牛雲的業務特點,每個服務只負責單一的職責。比如音視訊的處理服務:音視訊轉碼 ,音視訊拼接 , 視訊幀縮圖,點播流式轉碼,都是以微服務的方式構建,這樣每個服務都擁有獨立的執行環境,並且可以根據自身的業務壓力情況進行獨立擴充套件,動態的彈性擴充套件還可以提高資源的利用率。一個可落地的微服務架構應該為微服務提供獨立的執行環境,排程框架,註冊中心,配置管理中心和監控平臺。 七牛雲採用的是 Mesos+Docker+自研排程系統的架構。Docker 做環境封將,Mesos 做資源排程,自研的排程系統負責對 Docker 進行彈性的排程。

我們使用 Consul 做註冊中心,實現服務的註冊與發現。Consul 自帶 key/value 儲存,可通過 DNS 介面做服務發現,且具體健康檢查的功能,並支援跨資料中心的服務發現。API Gateway 可以通過 Consul 提供的 DNS 介面查詢到服務所有的可用例項的列表資訊,並將請求進行轉發。流量轉發具有負載均衡的功能,採用的是輪詢的方式,服務發現則是基於 Consul 做的。使用者請求進來後通過 Consul 查詢到所有可用節點的訪問地址,再通過輪詢的方式將請求發給後端的服務進行處理,對於返回的結果僅作轉發,由請求方解釋和使用。並且在API閘道器中帶有監控的元件,對請求數,失敗數等進行監控,傳送到 Prometheus 伺服器上。通過監控資料對請求進行流量控制及服務降級等相應的處理。

當需要呼叫多個微服務時,根據七牛雲的資料處理的業務特點我們使用管道(pipeline)來進行序列的處理。每一個微服務的輸出都是下一個微服務的輸入,直到最後一個微服務執行結束才是最終資料處理的內容。比如上傳一個視訊資源後,需要做兩個資料處理操作: 轉成 mp4 資源和進行 HLS 切片,這種場景下不能對原資源同時執行兩個資料處理的操作,必須按序執行。


話題交流


Q:服務與服務之間的依賴關係怎麼管理?服務編排怎麼做?把這些服務節點串起來。

宅急送石廷鑫:比方說不是業務的,基礎服務,如圖片伺服器這些,都是獨立的。唯一相關的是下面的業務層和底下的基礎服務有之間呼叫,還有是業務模組之間有服務的呼叫,在系統裡是沒做體現的,但是我們在服務和服務之間,就加了類似於熔斷的那個預設的東西。系統之間的依賴關係,我們自己做了一個簡單的系統進行維護。Spring cloud eureka 是都是存在記憶體裡,我們改良過,我都改到一個資料庫。我們互動全部都是 Rest ,我們後邊還寫過一套,Google protobuf。我們倉儲的內部的業務模組之間是用 Google protobuf 來做的。我們自個做了一套虛擬化的在裡邊的。

宅急送石廷鑫:我現在用的是 Apache camel 做編排,可以用 DSL 描述把服務串起來。其實更多的是業務的流程怎麼組織,怎麼去把基礎服務讓串起來,形成一個真正大的業務場景。

京東章耿:目前京東大部分一個介面一個方法就已經是操作多張表了,一般認為是一個比較原子性的操作了,如果再在外面有一個東西把它包一下,例如把 3 個方法包成一個方法,其實意義不大,所以我們現在是沒有做這方面的工作。另外京東有彈性雲,用的 Docker,不過不是你們想象中那種微服的那個 Docker 用法,京東現在用叫"胖容器”,更像一個虛擬機器一樣的一個東西。Docker 裡面執行的是一個應用,然後這個應用,它可能包含多個介面多個方法。可能大家理解微服務的 Docker 用法應該是一個容器裡面,他只跑一個獨立的邏輯,對資料的一個操作,或者對一個資源的操作。比如說下訂單、扣庫存,這兩步操作,他可能跑了兩個容器。把它編排成一個 service 的這種,我們好像沒有這種。

Q:服務拆分是怎麼做的?

京東章耿:京東現在的服務其實也是沒拆太細,主要還是業務部門自己控制服務粒度。京東的業務還是比較複雜的,各個應用之間互相依賴,互相呼叫,每個操作基本都會涉及到很多資源的變更,像微服務推崇的那種對單一資源的操作,基本上沒有。說實話,這種大型網際網路公司裡面的服務根本就微不起來的,不可能像 RESTful 一樣,對一個資源的一個 put post 操作基本不可能,我們現在都是力度還是由使用我們框架的研發人員自己定的,然後一般他們都是根據這種方法之間的一些關聯性或者原子性,或者是擴充套件性來進行組合或者拆分。所以我們一般叫自己服務化框架,不叫微服務框架。另外他們可能還會考慮一些服務是否可以獨立部署,拆的時候可能會考慮一下這些。

Q:其實很多大型電商網際網路也拆不開,也不能稱之微服務,是那種服務力度很粗,然後每個服務號有若干個介面,其實耦合性很高的合在一起,相關度很高,資料都在一起。

京東章耿:都差不多,都是業務開發自己來折分服務粒度的。就像剛才說的下單。肯定是一次要操作好多表的。業務開發認為這是幾次表操作其實是一個下單的原子操作,那麼他就拆到這麼細了。

宅急送石廷鑫:我們服務不是說一個部一個,實際上它是和資料域相關的,都擱到一塊。

宅急送石廷鑫:按功能分的,功能跟功能之間呼叫,如果不是同一個資料域裡邊的,還是用 RPC 來呼叫的。

京東章耿:那個聽起來很美好,你想我們現在這樣拆,都已經上萬了介面,方法基本都上十萬了。你這樣拆的話,起碼乘以 10 的介面,而且這樣搞的話就得整個公司都要動用非常非常大的能力去搞好這個事情。

Q:單體他如果拆成像市場上去提倡的那種微服務,其實他對內部的消耗是非常大的,因為我們很多的服務內部的呼叫,其實是非常頻繁,如果都把它拆開了去獨立部署的話,它其實對網路的消耗是要求非常高的。

宅急送石廷鑫:就是最難的點,一開始你是拆不開這個資料。

Q:資料只要能拆開,你什麼都好乾。你只要能把資料粒度拆的很細的話,那就沒問題了,那也能做的很細,但就是拆不開。其實好多資料都是陳年老表,都是很都是從原來系統繼承下來的,就很難拆。

宅急送石廷鑫:所以新做一個系統還可以。要做這個老系統計劃很難的,拆個表就要拆很多天。

京東章耿:而且是資料量比較少,然後邏輯比較簡單的,例如那種創業型公司,使用開源的方案,這種可以很方便。

Q:在雙 11 時各個電商肯定會有大型的促銷,這時服務的壓力肯定會有很大的增長,怎麼解決服務的伸縮的問題?

宅急送石廷鑫:就像我們這邊的話,就看哪個基點的壓力比較大一些,然後你多布幾個基點就可以了。現在基本上都是中泰的那個,就是 Spring cloud 那套。擴充套件都是提前先做一步。自動擴的話,就是相當於我們租了一個類似於簡單的監控。就是根據他的例項,然後因為微服務,我們現在是 Spring cloud ,基本上都是 java -jar,然後自己啟就得了,反正那個 jar,你可以放到一個公用的地,遠端 java -jar 要幹架就起來了,監控的時候根據他的量,然後隨時可以啟就行了。

京東章耿:京東的服務釋出都是掛在應用下面的,而應用釋出的平臺其實會和各個系統打通,比如說資料庫授權的系統,然後自動掛 VIP 的一些系統,掛監控平臺,日誌系統等。然後我們還有個監控平臺,監控一些指標,例如機器情況,應用情況,還有自己業務埋點的效能等資料。至於服務是否有壓力,都是自己評估,自己提前擴充套件。 一般大促前會進行大規模的壓測,他們自己壓測,然後根據結果自己擴就可以了。京東是沒有開自動彈性擴充套件的,基本都是大促前提前申請容器,然後提前擴完。

宅急送石廷鑫:併發量大的業務都是放在公有云,雙十一比平常多了兩倍的機器,我們只買 了一個月的雲主機。臨時用幾天,結束就不用了。

相關文章