您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“如何理解細(xì)數(shù)軟件架構(gòu)中的解耦”,感興趣的朋友不妨來看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“如何理解細(xì)數(shù)軟件架構(gòu)中的解耦”吧!
架構(gòu)是軟件方法學(xué)的范疇,它解決的是軟件組織的問題,不解決軟件算法的問題。兩者的區(qū)別可用下圖的積木做個(gè)類比:
算法就像一個(gè)個(gè)的積木塊,比如綠色的圓柱,藍(lán)色的三角,紅色的方塊等。而架構(gòu)則是把各種積木塊,組裝成一個(gè)城堡,一輛小火車。為搭建這個(gè)城堡或小火車,架構(gòu)師腦子里得有張圖紙,圖紙里既要定義需要哪些形形色色的積木塊,又要考慮如何將它們組裝起來。這工作很像建筑師,英文也的確叫 architect。
這樣類比,很容易讓不太理解技術(shù)的企業(yè)家們陷入誤區(qū),會(huì)覺得架構(gòu)師要比算法工程師更厲害?其實(shí)不然,這是兩個(gè)細(xì)分領(lǐng)域的才能。不知道您注意到小火車車頭上的煙囪沒?它是一個(gè)像雞腿菇一樣的弧線造型,澆灌出這種造型的模子,要比三角形和方塊形要難很多,它需要更深?yuàn)W的幾何學(xué)的支撐,這可以形象的看做是算法工程師解決的問題。
架構(gòu)解決軟件組織的問題,它能給企業(yè)創(chuàng)造什么價(jià)值?換句話說,好的軟件組織,跟差的軟件組織,從商業(yè)價(jià)值創(chuàng)造的角度,有什么不同?筆者以為架構(gòu)的價(jià)值體現(xiàn)在可用性和敏捷性兩個(gè)角度,但今天要講的是敏捷性。敏捷性指的是快速、低成本、高質(zhì)量地應(yīng)對(duì)擴(kuò)張市場(chǎng)的差異化需求。企業(yè)在初創(chuàng)期積累了不少軟件資產(chǎn),這些資產(chǎn)在當(dāng)初的市場(chǎng)環(huán)境下,已被論證取得了市場(chǎng)業(yè)績(jī)。但是伴隨著企業(yè)擴(kuò)張,市場(chǎng)會(huì)更加精細(xì)化、場(chǎng)景化,這些都會(huì)給我們的軟件提出新的需求,企業(yè)需要借助前些年在這個(gè)領(lǐng)域積累的先發(fā)優(yōu)勢(shì),一方面快速占領(lǐng)細(xì)分的市場(chǎng);另一方面復(fù)用曾經(jīng)積累的資產(chǎn),發(fā)揮資產(chǎn)的規(guī)模經(jīng)濟(jì)效應(yīng)。
比如京東電商,從高價(jià)值、標(biāo)準(zhǔn)化的 3C 數(shù)碼起家,建立起自營(yíng)電商模式;緊接著開始擴(kuò)品,做低價(jià)值、但高頻次、依然標(biāo)準(zhǔn)化的日用百貨圈用戶粘性;再做相對(duì)非標(biāo)的服裝發(fā)展女性用戶和生態(tài)模式等,直指行業(yè)競(jìng)爭(zhēng)的關(guān)鍵區(qū);除了擴(kuò)品還伴隨著場(chǎng)景擴(kuò)張,諸如 2B 企業(yè)業(yè)務(wù)、下沉市場(chǎng)拼購(gòu)業(yè)務(wù)、泰國(guó)印尼國(guó)際業(yè)務(wù)等。供給角度的品類擴(kuò)張,需求角度的場(chǎng)景擴(kuò)張,構(gòu)成了京東矩陣式垂直業(yè)務(wù)線。它們正是復(fù)用了零售中臺(tái)的軟件基礎(chǔ)設(shè)施,才在一定程度上做到了快速擴(kuò)張。
既然軟件組織的價(jià)值如此重要,那么好的軟件組織的標(biāo)準(zhǔn)是什么呢?又該如何做到呢?好壞的標(biāo)準(zhǔn)在解耦。解耦的對(duì)立面是耦合,耦合是指阻礙變化的依賴;解耦是要在依賴的基礎(chǔ)上,做到應(yīng)對(duì)可能的變化。依賴是必不可少的,依賴的本質(zhì)是分工,正如亞當(dāng)斯密的《國(guó)富論》論述的那樣,分工有助于專業(yè)化、有助于提高效率。太抽象了!說了這么多,沒講清楚解耦是什么。的確,筆者也認(rèn)為這樣的解釋只能讓已經(jīng)理解了的人再表示一次贊同,無法讓原本不理解的人變得理解,這樣毫無意義!我該如何詮釋?事實(shí)上,很多真理是建立在歸納法基礎(chǔ)上的。歸納法的好處是見得多了自然就會(huì)(歸納似乎是人腦的一種本能),比如詩(shī)詞,只要熟讀唐詩(shī)三百首,不會(huì)吟詩(shī)也會(huì)吟。不信你看,先來一篇叫“大漠孤煙直”的,沒啥概念;再讀一篇叫“空山新雨后”的,有點(diǎn)感覺了;最后“小橋流水人家”你自己就會(huì)了。如何寫出點(diǎn)有意境的詩(shī),你張口就來“床前明月光”,還不是自己寫的?如果你去到草原晚上觸景生情,即興來上一句“明月篝火烤肥羊”,就能媲美“日照香爐生紫煙”了。所以筆者覺得,最好的方式就是細(xì)數(shù)那些軟件架構(gòu)中的解耦,讓讀者從鋪陳式的實(shí)例中,自己找感覺。
筆者分 3 類 6 組(每類分進(jìn)程內(nèi)的應(yīng)用層和進(jìn)程間的架構(gòu)層)給大家舉例:
外加中間的 Naming 解析與 Proxy 代理融合的 CNAME 別名,總共 7 個(gè)案例。
中間層映射
中間層映射的設(shè)計(jì)理念是當(dāng) A 對(duì) B 有依賴時(shí),A 不要直接依賴 B,而是抽象一個(gè)中間層,讓 A 依賴中間層,再由中間層映射到 B,從而當(dāng) B 變成 C 時(shí),不用修改 A,只用調(diào)整中間層的映射關(guān)系。中間層映射,在應(yīng)用層表現(xiàn)為面向接口動(dòng)態(tài)綁定,在架構(gòu)層表現(xiàn)為 Naming 解析動(dòng)態(tài)綁定。
應(yīng)用層 - 面向接口動(dòng)態(tài)綁定
面向接口編程的核心思想是“先想清楚做什么,再想讓誰(shuí)來做”。什么叫想清楚了做什么?就是用接口的形式,描述輸入什么,輸出什么;但接口更多描述的是語(yǔ)法層面,語(yǔ)義層面的刻畫還需配合單元測(cè)試及其斷言(技術(shù)上叫 Test Driven),還有文檔。這跟企業(yè)家們常讀的《高效能人士的 7 個(gè)習(xí)慣》里面講的“以終為始”,思想上如出一轍。讓誰(shuí)來做?就涉及到運(yùn)行時(shí)動(dòng)態(tài)綁定。比如下圖:
在 Java 面向?qū)ο蟮恼Z(yǔ)言里,使用方通過 Provider 接口 Response doService(Request r) 來對(duì)外刻畫它的招標(biāo)文件。然后三個(gè)供應(yīng)方,LocalProvider、RemoteProvider 和 AsyncProvider 來應(yīng)標(biāo)。使用方只使用 Provider 接口,至于它跟哪個(gè)具體的 Provider 綁定,完全可以在“采購(gòu)”時(shí)刻動(dòng)態(tài)替換。
面向接口動(dòng)態(tài)綁定的解耦,體現(xiàn)在使用方把依賴的服務(wù)抽象為一個(gè)接口,依賴這個(gè)抽象的接口,而不依賴于具體的服務(wù)提供者,以便應(yīng)對(duì)服務(wù)提供者變化的可能性。
架構(gòu)層 -Naming 解析動(dòng)態(tài)綁定
上圖是域名服務(wù) DNS 的示意流程??蛻舳瞬⒉恢苯油ㄟ^ IP 地址來訪問 Provider#A 或 B,而是先詢問 Naming 服務(wù),并依據(jù)返回的服務(wù)列表,再訪問 Provider#A 或 B。如果某個(gè) Provider 故障了,可以替換轉(zhuǎn)移到其他的 Provider。出于性能考慮,也可以在客戶端把 Naming 的結(jié)果緩存起來,并配個(gè)緩存更新機(jī)制。
基于 ZooKeeper 的應(yīng)用層名字服務(wù),思想上類似 DNS。不同的是,它基于 TCP 長(zhǎng)鏈接來實(shí)現(xiàn) Server Push,可及時(shí)刷新服務(wù)列表。
Naming 解析動(dòng)態(tài)綁定的解耦,體現(xiàn)在使用方把依賴的對(duì)象或網(wǎng)絡(luò)進(jìn)程,抽象為一個(gè)名字,名字代表的具體服務(wù)提供者則通過 Lookup 機(jī)制返回,進(jìn)而做到如果提供者有變化,只要改變 Lookup 的結(jié)果,無需改變使用方代碼。
前后節(jié)植入
前后節(jié)植入的設(shè)計(jì)理念是服務(wù)器是流程的集合,流程是環(huán)節(jié)的序列。改變一個(gè)流程的行為,可以通過在其前后植入一個(gè)新環(huán)節(jié)來實(shí)現(xiàn)。前后節(jié)植入,在應(yīng)用層表現(xiàn)為 Chain 攔截模式,在架構(gòu)層表現(xiàn)為 Proxy 代理模式。
應(yīng)用層 -Chain 攔截模式
上圖是 Strtus2 的架構(gòu),每個(gè) Action 的執(zhí)行,都會(huì)被包裹在一系列 Interceptor 里面,形成一條處理鏈 Chain,每個(gè) Interceptor 會(huì)進(jìn)行 PreHandler 和 PostHandler 處理。這里的 Interceptor 可以增加、刪除或替換,以此實(shí)現(xiàn)可拓展性。比如可以在 Interceptor 里做鑒權(quán)、日志、性能統(tǒng)計(jì)、限流等。
Chain 插拔的動(dòng)態(tài)綁定,通過增刪替 Interceptor,把過去 URL 與 Action 的 1:1 的處理關(guān)系,轉(zhuǎn)變成了 M:N 的處理鏈。一類請(qǐng)求(某個(gè) URL),可以被多個(gè) Interceptor 處理;一個(gè) Interceptor 也可以處理多類請(qǐng)求。
順便說一下,Strtus2 這里說的“動(dòng)態(tài)綁定”,是配置相對(duì)硬編碼而言的。嚴(yán)格意義上,這里的綁定是編譯期的,不是運(yùn)行期的,是靜態(tài)的綁定。類似的架構(gòu)還有 Spring AOP 和 Servlet Filters 機(jī)制。
架構(gòu)層 -Proxy 代理模式
上圖是一個(gè) Proxy 架構(gòu)模式,這個(gè)應(yīng)用極其廣泛。比如 HTTP 的 Nginx,SQL 的 Apache Calcite,memcached 和 redis 的 twitter/twemproxy。為什么?因?yàn)?Proxy 對(duì)于 Backend 而言就是流量入口,是中間人,能扮演架構(gòu)層面的 AOP 機(jī)制,可拓展性非常強(qiáng)。
當(dāng)一個(gè)請(qǐng)求過來后,剛開始 Proxy 轉(zhuǎn)發(fā)給 Backend#A。但是業(yè)務(wù)發(fā)展了,Proxy 也可以轉(zhuǎn)發(fā)給 Backend#B 以實(shí)現(xiàn)負(fù)載均衡,更重要的是 A 和 B 還可以不同的版本,以實(shí)現(xiàn)灰度發(fā)布。還可以植入 PrePlugin 和 PostPlugin:
在 PrePlugin 里可以做權(quán)限控制、流量控制、請(qǐng)求改寫、緩存加速、惡意流量攔截、PV 統(tǒng)計(jì)、性能 Profile、ChaosMonkey 混沌事件植入等等。
在 PostPlugin 里,還可以做響應(yīng)報(bào)文改寫,安全加密(后端不用考慮數(shù)據(jù)安全,對(duì)外時(shí)統(tǒng)一加密處理)、壓縮加速等等。
兩者融合的實(shí)例 -CNAME 別名
上圖是一種混合模式:既有 Naming 解析,又有 Proxy 代理。而且 Naming 服務(wù),為了支持可拓展,還引入了父子層級(jí),末端的 Naming 服務(wù),完全可以委托給上一層級(jí)的 Naming 服務(wù)。
在 DNS 里面,我們經(jīng)常會(huì)看到 www.example.org 的域名解析,CNAME 別名到 www.example.org.cdnprovider.com (它是 cdnprovider.com 的子域名),這樣客戶端不用修改,依然訪問的是 www.example.org ,但是對(duì)應(yīng)的后端服務(wù),卻不再是直接訪問 Provider#A 或 B,而是中間植入了 CNAME Proxy,再由 Proxy 依據(jù) Plugin 的決策,是否轉(zhuǎn)發(fā)給問 Provider#A 或 B。
這個(gè)設(shè)計(jì)太棒了!它使得商業(yè)公司 cdnprovider.com 給 www.example.org 提供 CDN 服務(wù)時(shí),完全是零侵入,不需要修改任何一段代碼,只需要在域名服務(wù)商那修改 www.example.org 的域名解析,這個(gè)操作代表 www.example.org 同意 cdnprovider.com 為他們提供 CDN 服務(wù),代表授權(quán)。這一切,都源于基于 Naming 解析的動(dòng)態(tài)綁定實(shí)現(xiàn)的解耦。同樣的,除了 CDN,我們的惡意流量清洗、灰度發(fā)布、性能分析等都可以采用這種方式,實(shí)現(xiàn)零侵入插拔。
事件流訂閱的設(shè)計(jì)理念是將瞬間的過程化調(diào)用轉(zhuǎn)變成可回放的指令,對(duì)指令的響應(yīng)可以不用再預(yù)定義。事件流訂閱,在應(yīng)用層表現(xiàn)為 Mediator 中介模式,在架構(gòu)層表現(xiàn)為 Broker 消息模式。
應(yīng)用層 -Mediator 中介模式
A 直接調(diào)用 B,意味著 A 對(duì) B 產(chǎn)生了強(qiáng)依賴。當(dāng)然我們可以通過面向接口編程,把這個(gè)依賴降低,降低到只依賴接口,不依賴實(shí)現(xiàn)。簡(jiǎn)單說,我們只依賴對(duì)事情的處理結(jié)果,不依賴于如何實(shí)現(xiàn)這個(gè)處理結(jié)果。
但是這還不夠,因?yàn)槲覀冞€依賴了接口,接口意味著對(duì)處理語(yǔ)義的刻畫?,F(xiàn)實(shí)中有些情況,連語(yǔ)義的描述都要發(fā)生變化,也就是接口都要發(fā)生變化,如何進(jìn)一步解耦呢?如下圖:
A 不直接調(diào)用 B,而通過中介 Mediator,解耦兩步:
先由 A 調(diào)用 Mediator: A 持有 Mediator 的引用,執(zhí)行 Mediator 的方法,即 mediator.publish(e)。
再由 Mediator 調(diào)用 B: 為了解耦 Mediator 對(duì)外界的依賴,我們用面向接口 EventHandler 來實(shí)現(xiàn)依賴反轉(zhuǎn)。讓 B 來實(shí)現(xiàn) EventHandler,當(dāng)然如果 B 已經(jīng)存在,或更有話語(yǔ)權(quán),依然應(yīng)該遵循依賴反轉(zhuǎn)的原則,只不過 Mediator 模式的推進(jìn)方可以再實(shí)現(xiàn)一個(gè) Adaptor,來幫助既有的 B 適配到 EventHandler。
有了上述的設(shè)計(jì)模式后,具體的執(zhí)行分三步:
訂閱:通過 mediator.subscribe(b) 把未來的事件處理提前注冊(cè)到 Mediator。
發(fā)布:A 向 Mediator 發(fā)布自己的事件。注意這個(gè)理念特別重要,A 僅僅發(fā)布發(fā)生了什么事情,A 并沒有直接調(diào)用 B 聲明對(duì)事情的處理。也就是 A 連對(duì) B 的接口都不再依賴了!舉個(gè)例子,比如新員工入職,剛開始要為員工辦理磁條卡,只是辦理磁條卡的供應(yīng)商可能是甲,也可能是乙。這叫面向接口編程,但這還不夠,因?yàn)殡S著公司的發(fā)展,現(xiàn)在新員工入職,有人臉識(shí)別了,不用再辦磁條卡了,而是要登記人臉識(shí)別,另外員工福利更好了,對(duì)異地公干的新員工入職還會(huì)發(fā)放一筆安家費(fèi),這些都是之前的“接口”沒有描述的。
執(zhí)行:當(dāng) mediator 收到 A 的事件后(A 調(diào)用了 mediator.publish(e)),mediator 會(huì)通過 EventHandler 來回調(diào)預(yù)先通過 mediator.subscribe(b) 注冊(cè)的處理類。
上述 Mediator,有些局限性,對(duì)所有的 Event,只能有一種 EventHandler。如果我們把 Mediator 升級(jí)為一種通用的處理機(jī)制,一種平臺(tái),自然會(huì)有各種各樣的 Event,自然會(huì)我們會(huì)對(duì) Event 做個(gè)分類或分組。我們把 Event 的分類或分組,叫做 Topic;而把 Event 理解為 Topic 這個(gè)類里面的具體實(shí)例。并在 Mediator 里面維護(hù),從 Topic 到 EventHandler 的一組處理器。如下圖所示:
可以看到上述架構(gòu)通過 Map<Topic, List> resolver 來維護(hù)從 Topic 到 EventHandler 的一組處理。為什么是 List,而不是 EventHandler 呢?為了更加靈活,比如上文提到的「現(xiàn)在新員工入職,有人臉識(shí)別了,不用再辦磁條卡了,而是要登記人臉識(shí)別,另外員工福利更好了,對(duì)異地公干的新員工入職還會(huì)發(fā)放一筆安家費(fèi)」。
架構(gòu)層 -Broker 消息模式
上圖的 Broker 模式,跟 Mediator 模式其實(shí)沒有本質(zhì)的不同,只不過 Broker 更加突出了借助消息中間件 MQ 實(shí)現(xiàn)異步??蛻舳颂峤灰粋€(gè)委托,Broker 持久化完成,并回復(fù) ACK,表示委托已收到。接著委托的消費(fèi)處理,可以是離線的。通常需要支持 Group 機(jī)制:Group 內(nèi)部多個(gè) Instance 是負(fù)載均衡的,它們共同瓜分委托消息的處理;而 Group 間是冗余復(fù)制的,它們各自消費(fèi)各自的,相互之間隔離,有助于實(shí)現(xiàn)業(yè)務(wù)可拓展性。
比如一個(gè)新員工入職,它產(chǎn)生一個(gè)“新人入職”事件,然后行政部門會(huì)為其準(zhǔn)備工卡、財(cái)務(wù)部門會(huì)為其準(zhǔn)備工資卡、HR 部門會(huì)為其繳納社保。當(dāng)然,隨著公司業(yè)務(wù)發(fā)展,可能還會(huì)增加,比如業(yè)務(wù)部門的業(yè)務(wù)培訓(xùn),風(fēng)控部門的合規(guī)性培訓(xùn)等。
跟前面說的 Proxy 模式,相同點(diǎn)在于它們都是在架構(gòu)層面實(shí)現(xiàn)可拓展性。不同點(diǎn)是,Proxy 模式支持的是 PreHandler 和 PostHandler;而 Broker 模式支持的是 MidHandler。
到此,相信大家對(duì)“如何理解細(xì)數(shù)軟件架構(gòu)中的解耦”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。