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