1. 程式人生 > >使用微服務,你考慮好了嗎?

使用微服務,你考慮好了嗎?

劉思賢

微服務已經成為了當前最熱門的架構模式,然而也許微服務對你來說並不適用,繼續閱讀來尋找答案吧。

目前,幾乎所有人都對微服務趨之若鶩。開啟你的新聞聚合客戶端你會發現,幾乎每篇文章都在講微服務架構,沒有一個公司不是通過微服務技術拯救了他們的研發部門。也許你曾經在這麼一家公司工作過,通過這種輕量魔術般的服務來解決了所有遺留系統帶來的問題。

當然,事後看來,我們反而離真相更遠了。就像我們能看到視力表最末行,其實是由於提前看過了視力表導致的。

作為一個嘗試將單體應用拆分為微服務來拯救世界的公司員工,我將討論微服務運動的一些主要謬誤和陷阱。當然這篇部落格並非簡單的將微服務與不好劃等號,只是希望大家通過閱讀它能夠思考微服務架構是否真的適合你們。

到底什麼是微服務?

世界上沒有標準的定義,微服務應該包含什麼、不應該包含什麼。當然一些人已經成功實施微服務的人將規範編撰成冊。

我再強調一點,微服務不是一個單體。確切的說,單個微服務只處理一個非常有限的領域,它只處理它在你的技術棧中應當處理的工作。一個更確切一點的例子比如:一個銀行線上銀行業務中有一個“交易服務”(起名太困難,你懂就好),現在你打算增加一個訪問使用者交易記錄的業務,你應當把它放到交易服務之外。

另外,大家談論微服務時,總是隱含地討論微服務彼此之間需要遠端通訊。儘管他們是顯而易見的不同程序,並且通常執行在不同的計算節點上。所以通常服務之間使用REST或者其他RPC協議進行通訊。

剛剛開始時,這一切看起來還非常簡單。似乎我們只需要簡單的把領域服務通過RESTAPI暴露給其他微服務,讓微服務之間通過網路訪問即可。然而根據我的經驗,大多數人認為的五項事實實際上並非實情:

  1. 微服務能讓程式碼更簡潔;
  2. 只完成一項任務的程式碼更容易實現;
  3. 微服務比單體應用要快;
  4. 工程師不必同時在一個程式碼倉庫裡面開工;
  5. 更容易自動擴容縮容(在Docker的幫助下)。

謬誤1:微服務能讓我的程式碼更簡潔

“不要把網路邊界當作寫更好程式碼的藉口。”

一個簡單的事實是,任何技術棧,包括微服務在內,都不能成為寫更簡潔更易於維護的程式碼的動力。雖然當條件減少時,你寫出來不科學的程式碼的可能性會降低,但是這就像是通過減少商店裡的貴重物品來降低犯罪率一樣,沒有解決任何問題。

一個流行的方法是圍繞一個領域的邏輯服務構建你的程式碼結構。這反映了微服務的理念:它可以幫助你顯式地管理領域所需的依賴關係,並使得關鍵業務邏輯不會蔓延到多個地方。此外這些服務之間的呼叫不再過度使用網路,也避免了一些潛在問題的發生。

這種方法的好處在於,基於良好的前期設計和對領域的準確理解,你很容易將它抽取出來,一旦你決定了採用微服務架構,它能幫助你非常準確地圍繞微服務構建面向服務架構。一個堅固的SOA方法開始於程式碼本身,並隨著時間地推移,將它擴充套件到整個技術棧當中。

謬誤2:微服務容易實現

“分散式事務從來不簡單。”

雖然最開始看起來非常簡單,但是大多數領域(尤其是對那些初創公司需要經過多次原型迭代和改造的領域)並不適合將他們規規矩矩地劃分成為不同的領域。通常一個特定的子領域需要訪問其他子域的資料來正確地完成相應的工作。當需要向它自己以外的領域寫入資料時,事情會變得更加的複雜。一旦你打破了自己的業務範圍,並且需要其他人蔘與請求流來儲存修改資料時,那你就必須面對分散式事務(有時被稱作Sagas)。

在一個請求中涉及到多個遠端服務需要通訊時,複雜的問題便隨之而來了。例如呼叫是否可以並行,或者必須序列完成?你是否意識到這些呼叫中任何一個環節都有潛在地發生錯誤的可能性(包括應用錯誤和網路錯誤)?這對請求本身來說意味著什麼?通常每個分散式事務都需要他們自己來處理可能發生的故障,這很可能帶來巨大的工作量,不僅僅是錯誤的解析、錯誤的處理,更包括如何在出現錯誤時恢復事務。

謬誤3:微服務更快

“一些簡單的優化可以在單體系統中獲得巨大的效能提升。”

這條似乎很難被否定,因為事實上你可以通過減少單個系統的依賴或者做事情的數量來提高他們的速度。但這是一個看起來如此的方法。我並不懷疑轉向微服務架構後通過程式碼路徑的隔離讓系統速度提升,但是你也應該意識到,系統內增加了通過網路的呼叫。網路請求永遠不會像同一系統中呼叫那樣快,儘管通常它可以足夠快。

此外,許多關於效能提升的故事實際上是在宣傳一種新的語言或者技術棧帶來的好處,而不僅僅是通過將程式碼架構遷移到微服務上。將原有系統從RubyonRails、Django或者NodeJS遷移至像Scala和Go(微服務架構中兩種流行的選擇),這樣語言技術本身就能帶來很多效能改善。但是這些語言並不真的“關心”是否工作在“微”服務裡面,他們只是單純的為了提高效能而存在。

此外,對於大多數應用來講,CPU和記憶體永遠不是效能瓶頸,I/O才是。通過網路進行的呼叫,會為你的系統增加更多的I/O請求。

謬誤4:對開發人員更簡單

“開發人員工作在彼此隔離的程式碼庫時容易導致‘不是我的問題’綜合症。”

雖然聽起來一個小團隊專注於一個問題上,處理問題會更加簡單,然而這會帶來許多其他的問題,比如更難以從根本上解決問題。

最大的問題是你必須多做許多工作,你必須執行越來越多的服務,即使為了很小的一個變化。這樣你不得不投入一些精力來建立和維護能讓工程師在本地執行他們的辦法。Docker技術可以讓著更容易些,但是仍需要有人來維護他們。

另外,這讓寫測試也變得更加困難,因為一個恰當的整合測試意味著需要理解並覆蓋所有服務之間的彼此呼叫,以及捕獲處理相應的錯誤等。這也意味著需要花更多的時間來理解系統才能更好的開發。雖然我永遠不會說工程師花時間理解系統是在浪費時間,但是我要警告大家的是,不要過早地新增這些複雜性,除非你真的認為你需要他們。

最後,它也會造成一些社交上的問題。跨越多個服務的Bug需要多處修改才能解決,多個團隊需要同步協調他們解決問題的程序。它也有可能造成一種情況,就是人們對Bug缺乏責任感,並儘可能地將問題推給其他團隊。當工程師在同一個程式碼庫中進行工作時,他們共享所有的知識,承擔共同的責任,而不是成為孤立王國的國王和王后。

謬誤5:可擴充套件性更強

“擴容微服務就像擴容單體應用一樣簡單。”

將服務打包成離散單元,然後通過類似Docker的方式進行擴充套件,這似乎是一個水平伸縮的好方法,不過遺憾的是,這種說法並不對。

說它不正確,是因為這種方法不僅僅能用於微服務,更可以用於單體服務。你可以為你的單體應用建立邏輯叢集,只處理某項業務的流量。比如API請求、前端儀表盤和後臺任務伺服器可以在相同的程式碼庫裡面,但並不需要每個都處理三個不同的業務。

好處就像是微服務一樣,不同的叢集承擔不同的工作負載,可以單獨擴容一個叢集來應對流量的激增。所以,雖然微服務提供了很多方法,你同樣可以把這些方法引入到單體應用中去。

到底什麼時候應該使用微服務架構呢?

“當你準備好成為一個工程化組織時。”

最後我想說,時間恰當時,可以遷移至微服務(或者你知道這是否是正確的開始方式)。

實現微服務的一個至關重要最堅實可行的重要步驟是,你能夠正確理解你正在工作的領域。如果你不理解它,或者你正在試圖弄清它,那麼微服務則是有害無利。當你真正深刻地理解你的領域,知道邊界在哪裡依賴是什麼,那麼遷移至微服務架構才有可能是正確的選擇。

另一個重要的事情是處理你的業務流,具體來說,就是業務如何進行分散式事務。如果你知道每一類請求通過系統的路徑,並且瞭解每個路徑上可能失敗的位置、方式和原因,那麼你可以著手開始構建一個處理請求的分散式模型。

除了瞭解您的業務流之外,還需要監控你的工作流程。監控是微服務和單體之爭的事情,但是它應當是你的工程化的核心工作之一。因為你需要大量的資料來發現系統為何效能不佳甚至引發錯誤。只有你能找到一個可靠的方法來監視系統的各個部分,你才可以知道如何來優化它。

最後,當你真正可以向研發團隊和業務方展示價值時,遷移到微服務才可以幫助你承擔增長的業務、擴容和賺錢。雖然說嘗試新鮮事物是有趣的,但是你真正開始前,應當明白對於許多公司來說,什麼是他們的底線。如果因為一篇部落格文章告訴你單體應用不科學,而導致新功能的延期交付進而影響到業務收入時,你必須去迎合你的業務。有時候這些權衡是值得的,有時候不是。能正確認識到當前應當著力解決的問題,並以此來做技術的權衡,長期來看才是真正有益的。

最後的最後

希望下次有人提出微服務架構時,你能想明白實施微服務架構的條件是否成熟。就像我一開始所說的,本文並不是來說微服務是糟糕的,是希望你開始之前,能夠避免這條道路上會出現的一些坑。

如果你問我應當怎麼做,我建議通過程式碼中清晰定義的模組結構來建立內部服務,並且隨著時間的推移,逐漸將他們劃分成獨立的服務。這種方法不一定是最好,但也不失為避免爛程式碼進入系統的一個方法。當你準備好時,它能很快地幫你遷移到微服務架構上。

文章出處:Docker(訂閱號ID:dockerone)