發(fā)布于:2020-12-24 16:16:13
0
72
0
自從我們習(xí)慣于從服務(wù)器上提供靜態(tài)HTML內(nèi)容以來(lái),基于web的應(yīng)用程序已經(jīng)取得了很大的進(jìn)步。如今,應(yīng)用程序更加復(fù)雜,并且使用多個(gè)框架、數(shù)據(jù)中心和技術(shù)。在過(guò)去的幾年里,我們看到了兩個(gè)主導(dǎo)IT市場(chǎng)的概念:
1.將我們的應(yīng)用程序轉(zhuǎn)移到云端;
2.實(shí)現(xiàn)微服務(wù)體系結(jié)構(gòu);
這些想法塑造了我們?cè)O(shè)計(jì)和構(gòu)建軟件的方式。在某種程度上,我們不再構(gòu)建應(yīng)用程序;相反,我們正在構(gòu)建平臺(tái)。應(yīng)用程序不再共享相同的計(jì)算空間。相反,它們必須通過(guò)輕量級(jí)通信協(xié)議(如REST api或RPC調(diào)用)彼此通信。這種模式使得創(chuàng)建一些令人驚嘆的軟件成為可能,比如Facebook、Netflix、Uber等等。
在本文中,我們將討論現(xiàn)代web開(kāi)發(fā)中推動(dòng)創(chuàng)新的一些問(wèn)題。然后我們將深入事件驅(qū)動(dòng)架構(gòu)(EDA)的基礎(chǔ)知識(shí),EDA試圖通過(guò)以一種新的方式思考后端架構(gòu)來(lái)解決這些問(wèn)題。
現(xiàn)代網(wǎng)絡(luò)面臨的問(wèn)題
每一種技術(shù)都必須處理挑戰(zhàn),總是開(kāi)機(jī),多用戶(hù),異步應(yīng)用面臨的今天:
可用性
我們現(xiàn)在有許多、幾十個(gè)甚至數(shù)百個(gè)鏈接服務(wù),而不是一個(gè)應(yīng)用程序,而且每個(gè)服務(wù)都必須全天候待命。我們?cè)趺醋瞿?大多數(shù)情況下,服務(wù)被水平伸縮到多個(gè)實(shí)例,有時(shí)跨越多個(gè)數(shù)據(jù)中心,使其高度可用。進(jìn)入此特定服務(wù)的所有請(qǐng)求將在所有實(shí)例中均勻路由。一些部署工具提供自修復(fù)功能,因此如果一個(gè)實(shí)例宕機(jī),它將創(chuàng)建另一個(gè)實(shí)例來(lái)替代它。
可伸縮性
可伸縮性與可用性有很多共同之處??捎眯跃褪谴_保至少有一個(gè)服務(wù)實(shí)例啟動(dòng)并運(yùn)行,準(zhǔn)備為傳入的請(qǐng)求提供服務(wù)。另一方面,可伸縮性關(guān)注的是性能。如果一個(gè)應(yīng)用程序超載,那么我們可以創(chuàng)建該應(yīng)用程序的新實(shí)例來(lái)適應(yīng)增加的請(qǐng)求數(shù)量。但是擴(kuò)展應(yīng)用程序并不是沒(méi)有挑戰(zhàn)的,特別是當(dāng)我們處理有狀態(tài)應(yīng)用程序時(shí)。
單一真實(shí)來(lái)源
在微型服務(wù)出現(xiàn)之前,這項(xiàng)工作很簡(jiǎn)單。所有數(shù)據(jù)都駐留在一個(gè)地方,通常是某種關(guān)系數(shù)據(jù)庫(kù)。但是,當(dāng)多個(gè)服務(wù)共享一個(gè)數(shù)據(jù)庫(kù)時(shí),您可能會(huì)產(chǎn)生問(wèn)題,比如團(tuán)隊(duì)之間對(duì)模式更改的依賴(lài)關(guān)系或性能問(wèn)題。解決這個(gè)問(wèn)題的一種常見(jiàn)模式是為每個(gè)服務(wù)使用一個(gè)數(shù)據(jù)庫(kù)。一個(gè)分布式的真相來(lái)源確實(shí)有助于維護(hù)干凈的架構(gòu),但是我們現(xiàn)在必須處理分布式事務(wù)和維護(hù)多個(gè)數(shù)據(jù)庫(kù)的復(fù)雜性。
同步
在典型的請(qǐng)求-響應(yīng)場(chǎng)景中,客戶(hù)端等待服務(wù)器響應(yīng);它會(huì)阻塞所有活動(dòng),直到收到響應(yīng)或超時(shí)結(jié)束。如果我們接受這種行為,并使用跨系統(tǒng)的鏈?zhǔn)秸{(diào)用將其放入微服務(wù)體系結(jié)構(gòu)中,我們很容易就會(huì)出現(xiàn)我所稱(chēng)的“微服務(wù)地獄”。“每件事都從一個(gè)服務(wù)調(diào)用開(kāi)始,我們就叫它服務(wù)A吧。但之后,服務(wù)A需要調(diào)用服務(wù)B,有趣的事情就開(kāi)始了?!边@種行為的問(wèn)題是,如果一個(gè)服務(wù)有資源被阻塞(例如一個(gè)線程掛起),超時(shí)現(xiàn)在是指數(shù)級(jí)的。如果我們?cè)试S每個(gè)服務(wù)的超時(shí)時(shí)間為500毫秒,并且鏈中有5個(gè)服務(wù)調(diào)用,那么第一個(gè)服務(wù)的超時(shí)時(shí)間需要為2500毫秒(2.5秒),而最后一個(gè)服務(wù)的超時(shí)時(shí)間需要為500毫秒。
引入事件驅(qū)動(dòng)架構(gòu)
在經(jīng)典的三層應(yīng)用程序中,系統(tǒng)的核心是數(shù)據(jù)(基礎(chǔ))。在EDA中,焦點(diǎn)轉(zhuǎn)移到事件以及它們?nèi)绾卧谙到y(tǒng)中流動(dòng)。這種轉(zhuǎn)變使我們能夠完全改變?cè)O(shè)計(jì)解決上述問(wèn)題的應(yīng)用程序的方式。
在實(shí)際看到EDA是如何做到這一點(diǎn)之前,讓我們先看看事件到底是什么。事件是在應(yīng)用程序狀態(tài)中觸發(fā)通知或某種更改的動(dòng)作。一盞燈已經(jīng)打開(kāi)(通知),溫控器關(guān)閉了加熱系統(tǒng)(通知),一個(gè)用戶(hù)更改了他的地址(狀態(tài)更改),或者你的一個(gè)朋友更改了他的電話(huà)號(hào)碼(狀態(tài)更改)。所有這些都是事件,但這并不意味著我們應(yīng)該將它們添加到事件驅(qū)動(dòng)的解決方案中。要添加一個(gè)事件,它必須與業(yè)務(wù)相關(guān)。用戶(hù)下了新訂單是與該特定業(yè)務(wù)相關(guān)的事件,但他/她午餐吃披薩則不是。
當(dāng)你想到那些與企業(yè)相關(guān)的事件時(shí),它們可能是顯而易見(jiàn)的,但有些可能不是。特別是那些作為對(duì)其他事件的反應(yīng)而發(fā)生的事件。要發(fā)現(xiàn)在系統(tǒng)中流動(dòng)的事件,使用一種叫做事件風(fēng)暴的技術(shù)。將應(yīng)用程序的涉眾聚集在一起(從軟件工程師到業(yè)務(wù)人員和領(lǐng)域?qū)<?,并將所有業(yè)務(wù)流程作為特定事件來(lái)規(guī)劃。在映射了所有業(yè)務(wù)流程之后,工程團(tuán)隊(duì)可以將其結(jié)果用作構(gòu)建其應(yīng)用程序的需求。了解了什么是事件以及如何識(shí)別它們之后,讓我們看看它們?nèi)绾谓鉀Q前面提到的常見(jiàn)問(wèn)題。
事件以單一方向流動(dòng),從生產(chǎn)者到消費(fèi)者。將其與REST調(diào)用進(jìn)行比較。事件生成器從不期望來(lái)自使用者的響應(yīng),而在REST調(diào)用中總會(huì)有響應(yīng)。沒(méi)有響應(yīng),不需要阻止代碼執(zhí)行,直到發(fā)生其他事情。這使得事件本質(zhì)上是異步的,完全消除了超時(shí)運(yùn)行的風(fēng)險(xiǎn)。
事件的發(fā)生是動(dòng)作的結(jié)果,因此沒(méi)有目標(biāo)系統(tǒng);我們不能說(shuō)服務(wù)A觸發(fā)事件到服務(wù)B;我們可以說(shuō),服務(wù)B對(duì)服務(wù)a產(chǎn)生的事件感興趣,但可能還有其他方也感興趣,比如服務(wù)C或服務(wù)D。
那么,我們?nèi)绾未_保由一個(gè)系統(tǒng)觸發(fā)的事件能夠到達(dá)所有感興趣的服務(wù)呢?大多數(shù)情況下,這個(gè)問(wèn)題由消息代理解決。代理只不過(guò)是一個(gè)應(yīng)用程序,充當(dāng)事件生成器(創(chuàng)建事件的應(yīng)用程序)和事件使用者之間的中間人。通過(guò)這種方式,應(yīng)用程序可以很好地解耦,以解決我在文章前面談到的可用性問(wèn)題。如果一個(gè)應(yīng)用程序暫時(shí)不可用,當(dāng)它重新聯(lián)機(jī)時(shí),它將開(kāi)始使用事件并處理它們,趕上當(dāng)應(yīng)用程序宕機(jī)時(shí)觸發(fā)的所有事件。
存儲(chǔ)呢?事件是否可以存儲(chǔ)在數(shù)據(jù)庫(kù)中,或者是否存在其他的存儲(chǔ)方式?事件當(dāng)然可以存儲(chǔ)在數(shù)據(jù)庫(kù)中,但是這樣做會(huì)丟失它們的“事件”方面。事件發(fā)生后,我們不能改變它,所以事件是不可變的。另一方面,數(shù)據(jù)庫(kù)……它們是可變的,我們實(shí)際上可以在插入數(shù)據(jù)后更改數(shù)據(jù)。
存儲(chǔ)事件的更好方法是使用事件日志。事件日志只不過(guò)是一個(gè)集中的數(shù)據(jù)存儲(chǔ),其中每個(gè)事件都存儲(chǔ)為一系列不可變的記錄,也稱(chēng)為日志。將日志想象成一個(gè)日志,其中每個(gè)新事件都附加到列表的末尾。我們總是可以通過(guò)重播日志中從開(kāi)始到現(xiàn)在的所有事件來(lái)重新創(chuàng)建最新的狀態(tài)。
我還沒(méi)有談到的惟一一點(diǎn)是可伸縮性。使用事件驅(qū)動(dòng)思維構(gòu)建的服務(wù)被設(shè)計(jì)為部署在多實(shí)例場(chǎng)景中。由于狀態(tài)本身存儲(chǔ)在事件日志中,因此服務(wù)本身將是無(wú)狀態(tài)的,這允許我們對(duì)任何特定的服務(wù)進(jìn)行擴(kuò)展。
此模式的唯一例外是必須創(chuàng)建物化視圖的服務(wù)。本質(zhì)上,物化視圖表示事件日志中某個(gè)時(shí)間點(diǎn)的狀態(tài)。使用這種方法可以更方便地查詢(xún)數(shù)據(jù)?;氐娇缮炜s性問(wèn)題,物化視圖只不過(guò)是以表的格式聚合的事件,但是我們應(yīng)該將這些表存儲(chǔ)在哪里呢?大多數(shù)情況下,我們看到在內(nèi)存中執(zhí)行這些聚合,它們會(huì)自動(dòng)將我們的服務(wù)轉(zhuǎn)換為有狀態(tài)的服務(wù)。一個(gè)快速而簡(jiǎn)單的解決方案是向創(chuàng)建物化視圖的每個(gè)服務(wù)添加一個(gè)本地?cái)?shù)據(jù)庫(kù)。通過(guò)這種方式,狀態(tài)存儲(chǔ)在數(shù)據(jù)庫(kù)中,服務(wù)再次是無(wú)狀態(tài)的。
盡管事件驅(qū)動(dòng)的架構(gòu)已經(jīng)存在了15年多,但是直到最近它才獲得了巨大的流行,這是有原因的。大多數(shù)公司都在經(jīng)歷“數(shù)字化轉(zhuǎn)型”階段,隨之而來(lái)的是瘋狂的需求。這些需求的復(fù)雜性迫使工程師采用新的設(shè)計(jì)軟件的方法,即減少服務(wù)之間的耦合和降低維護(hù)開(kāi)銷(xiāo)的方法。EDA是解決這些問(wèn)題的一種方法,但不是唯一的方法。另外,您不應(yīng)該期望通過(guò)采用EDA可以解決所有問(wèn)題。有些特性可能仍然需要好的老式同步REST api或在關(guān)系數(shù)據(jù)庫(kù)中存儲(chǔ)數(shù)據(jù)。檢查什么是最適合你的,并適當(dāng)?shù)卦O(shè)計(jì)它!
作者介紹
熱門(mén)博客推薦