在過去的幾年中,我已經(jīng)完成了對(duì)處于數(shù)字化轉(zhuǎn)型過程中的多個(gè)產(chǎn)品團(tuán)隊(duì)的架構(gòu)審查。 大多數(shù)團(tuán)隊(duì)都在按照微服務(wù)架構(gòu)構(gòu)建產(chǎn)品。 他們有使用基于微服務(wù)的體系結(jié)構(gòu)的所有正確意圖-更快的開發(fā),更好的可伸縮性,更小的獨(dú)立團(tuán)隊(duì),獨(dú)立的部署,使用正確的技術(shù)來完成工作,等等。但是,我經(jīng)常發(fā)現(xiàn)團(tuán)隊(duì)在微服務(wù)方面苦苦掙扎。 他們未能充分利用微服務(wù)的優(yōu)勢(shì)。 在這篇文章中,我將分享我認(rèn)為團(tuán)隊(duì)在微服務(wù)方面苦苦掙扎的原因。
對(duì)于微服務(wù)新手,我建議閱讀Martin Fowler的微服務(wù)文章。 我喜歡本文中提到的微服務(wù)架構(gòu)定義。
微服務(wù)架構(gòu)風(fēng)格是一種將單個(gè)應(yīng)用程序開發(fā)為一組小型服務(wù)的方法,每個(gè)小型服務(wù)都在自己的進(jìn)程中運(yùn)行并與輕量級(jí)機(jī)制(通常是HTTP資源API)進(jìn)行通信。 這些服務(wù)圍繞業(yè)務(wù)功能構(gòu)建,并且可以由全自動(dòng)部署機(jī)制獨(dú)立部署。 這些服務(wù)的集中管理幾乎沒有,可以用不同的編程語(yǔ)言編寫并使用不同的數(shù)據(jù)存儲(chǔ)技術(shù)。
原因1:管理層低估了開發(fā)微服務(wù)的復(fù)雜性
我曾與多個(gè)對(duì)微服務(wù)非常看好的客戶一起工作。 對(duì)他們來說,微服務(wù)是解決所有問題的靈丹妙藥。 在討論中,我發(fā)現(xiàn)大多數(shù)團(tuán)隊(duì)及其管理層低估了微服務(wù)開發(fā)的復(fù)雜性。
要開發(fā)微服務(wù),開發(fā)人員需要高效的本地環(huán)境設(shè)置。
隨著系統(tǒng)中服務(wù)的增加,變得越來越難在一臺(tái)計(jì)算機(jī)上運(yùn)行應(yīng)用程序的子集。 當(dāng)您使用消耗相對(duì)較多內(nèi)存的語(yǔ)言(例如Java)構(gòu)建應(yīng)用程序時(shí),尤其會(huì)發(fā)生這種情況。
以下是與本地開發(fā)設(shè)置有關(guān)的要點(diǎn)。
· 本地開發(fā)的第一個(gè)重要方面是好的開發(fā)機(jī)器。 我發(fā)現(xiàn)大多數(shù)組織都很難使用所有最新和最先進(jìn)的技術(shù),但是他們不想替換性能低下的Windows開發(fā)機(jī)器。 開發(fā)人員受其開發(fā)機(jī)器的限制。 我已經(jīng)看到開發(fā)人員使用VDI映像或配置較差的機(jī)器來構(gòu)建基于微服務(wù)的系統(tǒng)。 這降低了他們的生產(chǎn)力,他們無法完全應(yīng)用自己。 使用劣質(zhì)的開發(fā)機(jī)器的副作用是,開發(fā)人員無法獲得快速反饋。 如果您知道必須等待幾分鐘才能運(yùn)行集成測(cè)試套件,那么您寧愿不用編寫更多內(nèi)容也不會(huì)增加工作量。 不良的開發(fā)機(jī)器會(huì)導(dǎo)致不良的開發(fā)實(shí)踐。
· 一旦有了您的開發(fā)人員,合理的機(jī)器就可以工作。 接下來的事情是確保所有服務(wù)都使用構(gòu)建工具。 您應(yīng)該能夠在無需太多配置的情況下在一臺(tái)新計(jì)算機(jī)上構(gòu)建整個(gè)應(yīng)用程序。 以我在微服務(wù)方面的經(jīng)驗(yàn),即使使用根構(gòu)建腳本也可以構(gòu)建整個(gè)應(yīng)用程序,這很有幫助。
· 下一個(gè)重點(diǎn)是使開發(fā)人員能夠輕松地在系統(tǒng)上運(yùn)行應(yīng)用程序的某些部分。 您應(yīng)該使用多個(gè)docker-compose文件來啟動(dòng)配置了所有端口和卷的不同服務(wù)。
· 接下來,如果您使用的是Kubernetes之類的容器編排工具,則應(yīng)該投資于Telepresence之類的工具,該工具可以輕松調(diào)試Kubernetes集群中的應(yīng)用程序。
如果組織不了解微服務(wù)的開發(fā)復(fù)雜性,那么團(tuán)隊(duì)速度會(huì)隨著時(shí)間的推移而下降。
原因2:沒有將庫(kù)和工具更新到最新版本的過程
在我的評(píng)論中,我發(fā)現(xiàn)新平臺(tái)已經(jīng)成為歷史。 團(tuán)隊(duì)無法確保依賴關(guān)系保持最新狀態(tài),或者數(shù)據(jù)庫(kù)等工具是否為最新版本。 因此,兩年前開始的現(xiàn)代化工作如今已經(jīng)有數(shù)月的技術(shù)債務(wù)。
幾年前,許多團(tuán)隊(duì)開始將Spring Cloud Netflix OSS項(xiàng)目用于微服務(wù)。 他們使用的是Kubernetes之類的容器編排工具,但由于它們是從Netflix OSS開始的,因此并未使用Kubernetes提供的所有功能。 當(dāng)Kubernetes內(nèi)置服務(wù)發(fā)現(xiàn)時(shí),他們?nèi)詫ureka用作服務(wù)發(fā)現(xiàn)。 此外,借助Istio這樣的服務(wù)網(wǎng)格,您可以擺脫Netflix OSS提供的大多數(shù)功能。 這有助于降低復(fù)雜性,并將很多交叉問題轉(zhuǎn)移到平臺(tái)上。
要記住的另一點(diǎn)是,要使所有服務(wù)的依賴項(xiàng)版本保持同步。 我最近在幫助一個(gè)使用Spring Boot構(gòu)建微服務(wù)的客戶。 在過去的兩年中,他們已經(jīng)構(gòu)建了20多個(gè)Spring Boot服務(wù)。 在他們的環(huán)境中,他們使用的Spring Boot版本范圍從1.5到2.1。 這意味著當(dāng)有人設(shè)置他們的機(jī)器時(shí),他們必須下載多個(gè)版本的Spring Boot。 此外,他們?nèi)鄙僮?.5以來在Spring Boot中所做的許多改進(jìn)。
我們的建議是組織應(yīng)在積壓的訂單中為這些升級(jí)創(chuàng)建技術(shù)債務(wù)項(xiàng)目。 這些技術(shù)債務(wù)項(xiàng)目應(yīng)在架構(gòu)委員會(huì)會(huì)議上進(jìn)行討論并定期解決。 在我的上一個(gè)項(xiàng)目中,我們每三個(gè)月設(shè)置一個(gè)星期的沖刺,以將所有依賴項(xiàng)更新到最新版本。
此外,團(tuán)隊(duì)?wèi)?yīng)該花時(shí)間在升級(jí)工具上,例如數(shù)據(jù)庫(kù),消息隊(duì)列和緩存,以升級(jí)到最新版本。
原因3:將共享服務(wù)用于本地開發(fā)
由于本地開發(fā)不佳,大多數(shù)團(tuán)隊(duì)開始依靠共享環(huán)境提供關(guān)鍵服務(wù)。 開發(fā)人員機(jī)器中的第一件事就是數(shù)據(jù)庫(kù)。 大多數(shù)年輕的開發(fā)人員都沒有意識(shí)到基于共享數(shù)據(jù)庫(kù)的開發(fā)是邪惡的。 以下是我在共享數(shù)據(jù)庫(kù)中遇到的主要問題:
· 團(tuán)隊(duì)成員必須建立工作的社會(huì)契約,以避免最后的作家勝出問題。 開發(fā)人員可以清除另一位開發(fā)人員為其工作編寫的數(shù)據(jù)。 這種工作方式既痛苦又容易失敗。 遲早這會(huì)咬傷團(tuán)隊(duì)。
· 開發(fā)人員擔(dān)心實(shí)驗(yàn)會(huì)影響他們的其他團(tuán)隊(duì)成員。 我們都知道,更好的學(xué)習(xí)方法是實(shí)驗(yàn)和快速反饋。 有了共享數(shù)據(jù)庫(kù),就可以進(jìn)行實(shí)驗(yàn)了。 我們需要進(jìn)行實(shí)驗(yàn)以提出數(shù)據(jù)庫(kù)架構(gòu)并執(zhí)行性能調(diào)整之類的任務(wù)。
· 另一個(gè)副作用是很難單獨(dú)測(cè)試更改。 您的集成測(cè)試變得不穩(wěn)定。 從而進(jìn)一步降低了顯影速度。
· 共享數(shù)據(jù)庫(kù)必須像寵物一樣對(duì)待,因?yàn)槟幌M蚕頂?shù)據(jù)庫(kù)的狀態(tài)不一致且不可預(yù)測(cè)。 您可能有一個(gè)開發(fā)人員想要在表為空但其他人需要表具有記錄的情況下測(cè)試邊緣情況。
· 僅共享數(shù)據(jù)庫(kù)具有系統(tǒng)正常工作所需的所有數(shù)據(jù)。 隨著時(shí)間的流逝,團(tuán)隊(duì)成員失去了更改的可追溯性,因此沒人知道他們?nèi)绾螐?fù)制他們計(jì)算機(jī)上相同的設(shè)置。 唯一的方法是獲取完整的數(shù)據(jù)庫(kù)轉(zhuǎn)儲(chǔ)并使用它。
· 未連接到網(wǎng)絡(luò)時(shí)很難工作。 通勤時(shí)間長(zhǎng)或乘飛機(jī)時(shí),通常會(huì)發(fā)生這種情況。
數(shù)據(jù)庫(kù)只是共享服務(wù)的一個(gè)示例,但它也可以是消息傳遞隊(duì)列,像Redis這樣的集中式緩存或服務(wù)可以改變的任何其他服務(wù)。
解決此問題的最佳方法是使開發(fā)人員可以輕松地在其計(jì)算機(jī)上運(yùn)行數(shù)據(jù)庫(kù)(作為docker容器),并投資創(chuàng)建SQL腳本來設(shè)置架構(gòu)和初始主數(shù)據(jù)。 這些SQL腳本應(yīng)保留在版本控制中,并像其他任何代碼一樣進(jìn)行維護(hù)。
原因4:版本控制托管平臺(tái)缺乏可見性
我正在與一個(gè)在其版本控制系統(tǒng)中具有1000多個(gè)存儲(chǔ)庫(kù)的客戶端一起工作。 他們正在使用Gitlab版本控制平臺(tái)。 他們有5個(gè)產(chǎn)品,每個(gè)產(chǎn)品都由多個(gè)微服務(wù)組成。 我問他們的第一個(gè)問題是幫助我們了解哪些服務(wù)及其各自的代碼存儲(chǔ)庫(kù)是產(chǎn)品A的一部分。他們的首席架構(gòu)師不得不花一天的時(shí)間弄清楚構(gòu)成產(chǎn)品A的所有存儲(chǔ)庫(kù)。 不知道她是否涵蓋了所有服務(wù)。
解決此問題的最佳方法是從一開始就以某種方式對(duì)微服務(wù)進(jìn)行分組,以使您始終對(duì)產(chǎn)品生態(tài)系統(tǒng)具有可見性。 Gitlab提供了一種創(chuàng)建組,然后在其中創(chuàng)建項(xiàng)目存儲(chǔ)庫(kù)的方法。 Github沒有組功能,因此您可以使用主題或命名約定來實(shí)現(xiàn)它。
我個(gè)人更喜歡mono repos,因?yàn)槲野l(fā)現(xiàn)它們真的很方便。 我遇到的大多數(shù)開發(fā)人員都將其視為反模式。 我同意Dan Lua的帖子,他在文章中提到了mono repo的以下好處
*簡(jiǎn)化的組織
*簡(jiǎn)化的依賴關(guān)系
*工具
*跨項(xiàng)目變更
原因5:沒有明確的服務(wù)定義
大多數(shù)團(tuán)隊(duì)都不知道應(yīng)該將什么視為服務(wù)。 關(guān)于實(shí)際上構(gòu)成單個(gè)微服務(wù)的東西有很多困惑和困惑。 讓我們舉一個(gè)例子,您的應(yīng)用程序具有類似插件的架構(gòu),您將在其中與多個(gè)第三方服務(wù)集成。 每個(gè)集成都應(yīng)該是微服務(wù)嗎? 我已經(jīng)看到多個(gè)團(tuán)隊(duì)正在為每個(gè)集成創(chuàng)建一個(gè)微服務(wù)。 隨著集成數(shù)量的增加,這很快變得難以管理。 這些服務(wù)通常太小,以至于它們作為單獨(dú)的進(jìn)程運(yùn)行會(huì)增加更多的開銷。
我認(rèn)為擁有少量大型服務(wù)總比擁有太多小型服務(wù)好。我將首先創(chuàng)建一個(gè)對(duì)業(yè)務(wù)組織中整個(gè)部門建模的服務(wù)。這也符合DDD。我將一個(gè)域分為子域和有界上下文。有界上下文表示公司內(nèi)部的部門,例如財(cái)務(wù)和市場(chǎng)營(yíng)銷。您可能認(rèn)為這可能會(huì)導(dǎo)致大型微服務(wù),這是正確的。但是,以我的經(jīng)驗(yàn)來看,將單片重構(gòu)為微服務(wù)總是比反之容易。隨著您獲得更多的知識(shí),您可以轉(zhuǎn)向代表較小關(guān)注點(diǎn)的細(xì)粒度微服務(wù)。您可以應(yīng)用"單一責(zé)任原則"來了解您的微服務(wù)是否變得太大而做太多的事情。然后,您可以將其分解為較小的獨(dú)立服務(wù)。任何服務(wù)都不應(yīng)直接與另一個(gè)服務(wù)的數(shù)據(jù)庫(kù)對(duì)話。他們只能通過已發(fā)布的合同進(jìn)行溝通。您可以閱讀Microservices.io網(wǎng)站上提到的有關(guān)按子域模式分解的更多信息。
我也遵循后端文檔中提到的建議。 該建議可以幫助限制服務(wù)之間的通信,這是基于微服務(wù)的系統(tǒng)性能低下的首要原因。
如果兩條信息相互依賴,則它們應(yīng)屬于一臺(tái)服務(wù)器。 換句話說,服務(wù)的自然邊界應(yīng)該是其數(shù)據(jù)的自然邊界。
原因6:沒有明確的代碼重用策略
我正在與一個(gè)客戶一起工作,該客戶已在其所有基于Java的微服務(wù)中復(fù)制了四個(gè)與特定問題相關(guān)的Java文件。 因此,如果在該代碼中發(fā)現(xiàn)錯(cuò)誤,則需要將其應(yīng)用到所有地方。 我們都知道,在時(shí)間壓力下,我們會(huì)錯(cuò)過將更改應(yīng)用于一項(xiàng)或多項(xiàng)服務(wù)的機(jī)會(huì)。 這將浪費(fèi)更多時(shí)間并增加挫敗感。
開發(fā)團(tuán)隊(duì)并不了解正確的事情。 但是,組織的結(jié)構(gòu)化方式人們總是默認(rèn)使用簡(jiǎn)單且容易出錯(cuò)的做事方式。
正確的方法是使用工件管理器(如Bintray或Nexus)并在其中發(fā)布依賴項(xiàng)。 然后,每個(gè)微服務(wù)都應(yīng)依賴該庫(kù)。 您需要構(gòu)建工具,以便在發(fā)布新版本的庫(kù)時(shí),應(yīng)更新并重新部署所有微服務(wù)。
使用微服務(wù)并不意味著您不應(yīng)使用迄今為止對(duì)我們有用的最佳實(shí)踐。 您需要在工具上進(jìn)行投資,以使其易于升級(jí)微服務(wù),從而使人類不必這樣做。
在沒有適當(dāng)工具和自動(dòng)化的情況下使用微服務(wù)是災(zāi)難的根源。
原因7:多種語(yǔ)言編程
我找到了使用多種編程語(yǔ)言,多個(gè)數(shù)據(jù)庫(kù),多個(gè)緩存的團(tuán)隊(duì),這是工作的最佳工具。 所有這些都在項(xiàng)目的初始階段起作用,但是當(dāng)您的產(chǎn)品投入生產(chǎn)時(shí),這些選擇就開始顯示出它們的真實(shí)色彩。 諸如我們?cè)跇?gòu)建Java Spring Boot應(yīng)用程序之類的原因,但我們意識(shí)到Java占用了更多內(nèi)存,并且性能很差,因此我們決定切換到Node.js。 在上一個(gè)任務(wù)中,我向團(tuán)隊(duì)解釋說他們的推理能力很弱。
· Node.js的性能優(yōu)于Java。 如果您有基于IO的工作負(fù)載,則Node.js通常會(huì)表現(xiàn)更好。 Java在任何計(jì)算密集型工作負(fù)載上均勝過node.js。 通過使用響應(yīng)式范例,Java for IO工作負(fù)載可以具有更好的性能。 Spring Boot Reactor在IO工作負(fù)載方面的性能與Node.js相當(dāng)。
· Node.js消耗的內(nèi)存少于Java。 這部分是正確的,因?yàn)镹ode.js應(yīng)用程序通常使用的內(nèi)存少于Java。 Java Spring Boot應(yīng)用程序并不像大多數(shù)人想象的那樣糟糕。 我在其中一個(gè)Spring Boot Java Microservice上進(jìn)行了負(fù)載測(cè)試,并且內(nèi)存消耗仍然少于1 GB。 您可以通過OpenJVM,限制對(duì)類路徑的依賴性以及通過調(diào)整默認(rèn)JVM參數(shù)來優(yōu)化Java內(nèi)存利用率。 另外,Java中的Spring Boot替代品還有Micronaut和Quarkus等,它們消耗的內(nèi)存等于Node.js。
· Node.js比Java更高效。 這取決于開發(fā)人員編寫代碼。 帶有靜態(tài)類型和靜態(tài)分析工具的Java可以幫助在開發(fā)生命周期的早期發(fā)現(xiàn)問題。
大多數(shù)時(shí)候,這全都取決于上下文。 如果您的開發(fā)人員不成熟,則無論使用哪種編程語(yǔ)言,您都將開發(fā)出不良的產(chǎn)品。
我建議組織發(fā)布團(tuán)隊(duì)可以使用的語(yǔ)言列表。 我認(rèn)為2–3是個(gè)好數(shù)字。 另外,列出為什么應(yīng)使用一種語(yǔ)言代替另一種語(yǔ)言的原因。
選擇語(yǔ)言之前,應(yīng)考慮以下多種原因:
· 輕松找到成熟的企業(yè)軟件開發(fā)人員有多容易?
· 重新培訓(xùn)開發(fā)人員使用新技術(shù)有多容易? 我們發(fā)現(xiàn)Java開發(fā)人員可以相對(duì)輕松地學(xué)習(xí)Golang。
· 最初團(tuán)隊(duì)之外的開發(fā)人員可以多么容易地貢獻(xiàn),傳輸和維護(hù)他人編寫的代碼?
· 就工具和庫(kù)而言,生態(tài)系統(tǒng)的成熟程度如何?
這不僅限于編程語(yǔ)言。 這也適用于數(shù)據(jù)庫(kù)。 如果您的系統(tǒng)中已經(jīng)有MongoDB,那么為什么要在生態(tài)系統(tǒng)中使用ArangoDB? 它們都主要是文檔數(shù)據(jù)庫(kù)。
始終考慮使用多種技術(shù)的維護(hù)和操作方面。
原因8:人們依賴性
這并非特定于微服務(wù),但在微服務(wù)生態(tài)系統(tǒng)中變得更加普遍。 原因是大多數(shù)團(tuán)隊(duì)專注于他們的特定服務(wù),因此他們不了解完整的生態(tài)系統(tǒng)。 在與不同客戶的合作中,我發(fā)現(xiàn)只有一小部分了解整體情況的建筑師。 但是,這些架構(gòu)師的問題在于他們?cè)谌粘;顒?dòng)中不活躍,因此對(duì)開發(fā)的影響有限。
我認(rèn)為最好的辦法是確保所有團(tuán)隊(duì)在架構(gòu)小組中只有一個(gè)代表性的部分,以便他們可以使團(tuán)隊(duì)與整體架構(gòu)團(tuán)隊(duì)的路線圖和目標(biāo)保持一致。 要成為一個(gè)成熟的組織,您需要投資建立輕量級(jí)的治理。
原因9:缺少文檔
過去幾年中與我們互動(dòng)的大多數(shù)組織都在文檔方面苦苦掙扎。 大多數(shù)開發(fā)人員和架構(gòu)師要么不編寫文檔,要么他們編寫的文檔沒有用。 即使他們想寫,他們也不知道應(yīng)該如何記錄其體系結(jié)構(gòu)。
至少我們應(yīng)該記錄以下內(nèi)容:
· 設(shè)計(jì)文件
· C4模型中的上下文和容器圖
· 以架構(gòu)決策記錄的形式跟蹤關(guān)鍵架構(gòu)決策
· 開發(fā)人員入門指南
我建議所有文檔都保留在版本控制系統(tǒng)中。
原因10:功能超過平臺(tái)成熟度
我在其他方面簡(jiǎn)要地談到了這個(gè)原因,但是我認(rèn)為值得一提的是它的首要原因。 微服務(wù)比傳統(tǒng)的單片應(yīng)用程序更為復(fù)雜,因?yàn)槟跇?gòu)建具有許多活動(dòng)部件的分布式系統(tǒng)。 大多數(shù)開發(fā)人員尚未理解系統(tǒng)的不同故障模式。 大多數(shù)微服務(wù)在構(gòu)建時(shí)都考慮了一條愉快的路。 因此,如果您的管理層只想早于專注于功能,那么您將失敗。 在弱平臺(tái)上構(gòu)建的功能無法帶來價(jià)值。
組織需要進(jìn)入平臺(tái)心態(tài)。 平臺(tái)心態(tài)不僅意味著使用容器和Kubernetes。 它們是解決方案的一部分,但本身并不是完整的解決方案。 您需要考慮分布式跟蹤,可觀察性,混亂測(cè)試,函數(shù)調(diào)用與網(wǎng)絡(luò)調(diào)用,服務(wù)到服務(wù)通信的安全服務(wù),可調(diào)試性等。這需要大量的精力和投資來建立合適的平臺(tái)和工具團(tuán)隊(duì)。
如果您是一家資源有限的初創(chuàng)公司,我的建議是重新考慮您的微服務(wù)策略。 請(qǐng)了解您正在進(jìn)入什么。
原因11:缺乏自動(dòng)化測(cè)試
大多數(shù)團(tuán)隊(duì)都知道自動(dòng)化測(cè)試對(duì)產(chǎn)品的整體質(zhì)量有多重要,但他們?nèi)匀粵]有做到。 微服務(wù)架構(gòu)為在何處以及如何進(jìn)行測(cè)試提供了更多選擇。 如果您不進(jìn)行全面的自動(dòng)化測(cè)試,那么您將嚴(yán)重失敗。
關(guān)于這一點(diǎn),我不會(huì)寫太多,因?yàn)榫W(wǎng)絡(luò)上許多其他人已經(jīng)討論過這一點(diǎn)。 我從Martin Fowler網(wǎng)站上發(fā)布的Microservices測(cè)試文章中獲取的下圖討論了基于Microservices的系統(tǒng)的測(cè)試金字塔。
Microservices Test Pyramid
-
API
+關(guān)注
關(guān)注
2文章
1499瀏覽量
61975 -
微服務(wù)
+關(guān)注
關(guān)注
0文章
137瀏覽量
7341
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論