在最近的一次Microservices Practitioner Summit中,原Netflix工程師介紹了一種越來越常見的對Microservice的誤用。簡單地說,大家在搭建一個基于Microservice的服務時常常依賴同一套類庫,進而使得Microservice中的各個子服務無法選擇最适合的技術。
如果您不知道Microservice是什麼,請首先閱讀我的另一篇文章《Microservice簡介》。
在本文中,我們就将以該演講的内容作為引子,介紹一下目前業界對于Microservice的一系列誤用方式。
Distributed Monolith
不知道大家是否喜歡看看各公司所辦的各種會議。現在不像10年前隻有微軟,Intel等幾家公司辦的TechED,IDF等會議了。越來越多的公司喜歡舉辦會議、論壇等活動,以吸引越來越多的優秀開發人員一起讨論最新技術和業界發展趨勢。是以我先說點題外話,希望如果大家感興趣請自己尋找一下自己覺得有趣的會議。我相信大家都是喜歡技術的人。這樣一方面能提高自己的能力,另一方面也對國内的技術環境有幫助,利人利己。
好,讓我們回到原題。在該工程師的演講中,他提出了近期較為常見的一種對Microservice的誤用:很多公司或個人都認為将一個Monolith的服務拆分成一系列子服務,就能夠被稱為是基于Microservice的服務了。的确,這種變化引入了Microservice的一系列優點:由于各個子服務之間擁有了确切的邊界,是以它們将能夠更容易地獨立發展,而且我們也可以更容易地對該子服務進行擴容:

但是這種子服務組織方式并不能完全地稱為Microservice:其丢掉了Microservice的技術靈活性。在Microservice中建立一個子服務的方式主要分為兩種:從原有的服務上剝離,以及建立服務。
在從原有服務上剝離時,我們常常會選擇讓新建立的子服務使用原有的技術,以減少建立子服務的工作量。這其實并沒有太多的問題,畢竟這種剝離在原有服務的基礎上提高了各子服務的獨立性。雖然說此時我們所使用的技術并不是最合适的,但是的确是最有效率的建立子服務的方式。
而建立一個Microservice子服務就不一樣了。在建立一個新的子服務時,軟體開發人員常常需要自行編寫該子服務所包含的所有代碼。是以此時我們應該盡量選擇最合适的技術,以擷取最高的開發效率。
但是誤用就出現在這裡。在建立一個新的Microservice子服務時,軟體開發人員常常偏向于使用之前所使用的各種技術。這是軟體開發人員對已使用技術所保留的一種慣性:軟體開發人員在之前對這些類庫的使用中知曉了其可以用來完成某些功能,而并沒有過多地關注實作這些功能的複雜程度。就像我在《Cassandra簡介》一文中說的那樣,技術選型是一個非常嚴謹的過程。該過程常常需要進行大量的研究,并根據開發的難易程度,使用者基數,活躍程度以及成功案例來決定是否使用該技術。而軟體開發人員的這種技術慣性則常常是導緻項目最終失去控制的一個原因。
在這些共有的技術越積越多的情況下,我們的Microservice服務就越來越容易引入剛剛Netflix工程師所提到的錯誤:在Microservice中廣泛使用類庫特有技術。我們知道,某些類庫會提供一系列特有的功能,以用來提高執行效率,提供更高的安全性等。但是這些技術常常并不是行業規範,是以也會導緻其無法與其它類庫相容。這會導緻Microservice中的各個子服務之間存在着一種隐式的契約:為了有效地在各個子服務之間進行通訊,Microservice中的各個子服務需要使用特定的技術。
如果我們在Microservice中引入這種特定的技術,那麼該技術将會逐漸擴充到Microservice的所有子服務中:
這樣Microservice中的所有子服務最終将使用同一個技術集,進而限制了其它子服務所能夠選擇的技術。如果我們任由這種情況發展,那麼到最後Microservice中的所有子服務都将綁定于一整套固定的技術集之上,并無法再選用最為合适的技術。而最終的結果就是,我們的Monolith服務的确轉化為基于Microservice的服務,但是其使用的技術集還是原來的技術集。這種服務組織方式并沒有提高開發效率,反而增加了在各個Microservice子服務之間互相溝通的成本,降低了整個服務對單一請求的響應速度。總體來說,得不償失。
而真正應該在Microservice子服務之間存在的,是明确指定的契約和協定,而不應該是如何實作這些子服務。
其實一個決策常常是在權衡各方面優劣才做出的。Microservice的優勢無非就是具有更好的橫向擴充能力(Scalability),更靈活的技術選擇,更簡單的子服務實作邏輯,更清晰的子服務邊界以及更好的子服務複用性。但是缺點也一樣明顯:子服務之間互相通訊所導緻的單一請求執行效率降低,子服務邊界所帶來的代碼組織靈活性降低等。如果我們丢掉了靈活的技術選擇,那麼更簡單的子服務實作邏輯所帶來的高效開發及更好的維護性就将無從說起。最終所導緻的結果就是:使用Microservice組織子服務不會為我們帶來額外的好處。
這裡還有一個懸而未決的問題,那就是從Monolith剝離出來的各個子服務常常使用同一個技術集。是的。在這種情況下,我們要盡量控制這些技術集的擴散,并在需要時逐漸移除對這些技術集的依賴。
功能邊界不清
我相信有些讀者已經對Microservice有些研究了,甚至嘗試過在服務實作中使用它。最容易遇到麻煩的一點便是對單個請求進行處理時的性能問題。
無論是浏覽器還是JS類庫,都擁有一個請求逾時的概念。在發送一個請求之後,如果服務長時間沒有對該請求進行響應,那麼浏覽器或JS類庫都會将該請求識别為逾時。而且從使用者使用的角度來講,他也并不希望一個請求長時間沒有響應。是以在一個基于Microservice的服務中,我們常常需要讓整個服務能夠快速有效地響應使用者的請求。
一個提升性能的方法就是合并請求。如果說使用者對一個服務的使用需要固定順序的一系列調用,那麼我們就可以通過将這些調用合并到一起,進而減少反複通訊所造成的損耗:
上圖展示了這種通過合并調用提高性能的方法。在使用者嘗試找到特定種類下的所有商品時,我們需要通過查找所有的商品種類以确定該商品種類的ID,進而通過該商品種類的ID得到該商品種類下的各種商品。為了能夠合并調用,我們需要提供一個新的API,以允許軟體開發人員通過商品種類名稱直接完成調用,而對商品ID的查詢等執行邏輯都放在服務端去執行。這樣使用者就隻需要發送一個請求,而不是三個請求,進而縮短了整個流程的運作時間。
但是這樣還是有問題,那就是我們所新引入的API已經将商品種類以及商品這兩個概念包含在同一個子服務中了。這種包含了過多概念的子服務常常成長為一個包含了過多功能的子服務,也即是Microservice中的Monolith。
而一個較為正确的解決方案則是:就像我在《Microservice簡介》一文中已經提到過的那樣,我們在必要時應該提供一個Gateway,并在Gateway中添加合并在一起的API。這樣由于Gateway常常和與其關聯的各個子服務處于同一個資料中心中,是以其内部通訊效率常常比使用者通過浏覽器通路快很多。這樣我們既保證了各個子服務可以獨立地發展,又能提高使用者的通路效率:
而且在該Gateway中,我們也常常可以通過緩存等一系列技術手段來提高運作效率。
反過來啊,我們也别走到另外一個誤區裡面,那就是建立太多的層次。我能了解一個資料庫作為一個獨立的層次的必要性,畢竟資料庫常常與其它服務執行個體處于不同的服務執行個體上。但是如果在縱向上添加太多的層次,那麼在處理使用者所發出的一個請求時就需要經過過多的消息轉發,進而使得對消息的處理時間變長:
當然,Gateway也是其中的一個層次,是以不要為每個子服務添加一個Gateway,而是要有一個整體的規劃:
總之,在如何切割子服務時我們常常需要在腦中保持是否使用者的請求能被快速響應這一個問題。在Microservice中,API的粒度過細,以及内部調用過多是最具有殺傷力,也最容易出現的問題。
過于強調Microservice
的确Microservice這個詞在國外很火。很多公司都将自己的産品是基于Microservice來組織的作為一個賣點。這的确能夠幫助很多銷售人員解釋公司産品所具有的一系列優勢。但是作為一個開發人員,我們不要被這個觀點所迷惑。原因就是因為,直接開發基于Microservice的服務所需要的初始開發成本較Monolith的開發成本高很多。直到産品的規模達到一定程度,Microservice的優勢才能夠發揮出來:
而一個産品常常需要經由PoC才能變成真正的版本,而且在産品的初期,我們所需要解決的常常不是這個産品需要具有多大的擴充性,而是我們需要有足夠的錢來支撐這個産品的持續研發。是以在産品演變的過程中,功能性需求常常在開發的初期占首要地位,而像橫向擴充能力等非功能性需求在後期才會變得越來越重要。是以在開發一個全新産品時,如果我們過于注重通過Microservice來組織各個子服務,那麼軟體開發人員在整個項目的初始階段的開發效率将非常低下。相反地,我們需要在開始實作時盡量注意各個組成之間的隔離。這樣一旦需要從Monolith轉化為Microservice,我們隻需要将這些代碼根據其所在的包進行剝離即可。
當然,如果我們已經開發過按照Microservice組織的服務,而且有一系列子服務可以被重用,那麼我們完全可以通過重用之前Microservice服務所使用的架構來開始這個服務的開發。此時Microservice服務的開發效率并不會低多少,甚至還可能在非常短的時間之内擁有比Monolith開發更高的效率:
是以說,我們的第一要務并不是使用Microservice,而是通過它來為業務目标服務。将Microservice置于業務目标之前,反而常常在項目初期成為一塊絆腳石。
沒有使用Continuous Delivery
因為我一直在外企,是以對Continuous Delivery平台的使用還是相對較多的。但是從國内某些廠商,甚至是較大的廠商使用Microservice搭建服務時,常常會出現不使用Continuous Delivery平台的情況出現。這樣就會導緻一個問題,搭建測試環境及生産環境會非常麻煩。
Microservice的一個重要優勢就是提高了開發效率。反過來,如果軟體開發人員和測試人員每天需要為如何搭建一個開發及測試環境發愁,那麼使用Microservcie開發又得到了什麼好處呢?
當然啊,工作上的事情有些說不清,是以咱們也不多說什麼。隻能說,Continuous Delivery能夠提高我們的開發效率。如果您是個決策者,而且剛好看到這篇文章,那麼請您一定認真地考慮這一點。其實搭建一個Jenkins環境也并不是非常困難,所需要的實體機也并不是很多。而且您還能夠在災難恢複等衆多非功能性需求上獲得一系列好處。
對,最後提一句,這篇文章和InfoQ上最近的一篇文章有關:http://www.infoq.com/news/2016/03/microservices-anti-patterns。熟悉我的讀者可能知道我喜歡攢文章,隔幾個月再釋出,因為這樣可以根據一段時間的經驗對自己所寫的内容進行一次校驗。隻不過我看這篇文章同樣提到了這個演講,是以為了避免不必要的解釋,就提前發了
轉載請注明原文位址并标明轉載:http://www.cnblogs.com/loveis715/p/5315860.html
商業轉載請事先與我聯系:[email protected]
公衆号一定幫忙别标成原創,因為協調起來太麻煩了。。。