分佈式專題-01漫談分佈式架構

前言

從今天我們開始進入分佈式專題,分佈式作爲互聯網企業技術人員必備的技能之一。也是我們本套分享內容所有專題都是圍繞分佈式展開的,不可謂不重要~

分佈式架構的演進過程

Tips:

1.瞭解分佈式架構中的相關概念

2.初始分佈式架構及意義

3.分佈式架構的發展過程和歷史

4.分佈式架構的演進過程

5.構建分佈式架構最重要的因素

分佈式架構的發展歷史

1946 年情人節(2.14) , 世界上第一臺電子數字計算機誕生在美國賓夕法尼亞大學大學,它的名字是:ENIAC; 這臺計算機佔地 170 平米、重達 30 噸,每秒可進行 5000 次加法運算。第一臺電子計算機誕生以後,意味着一個日新月異的 IT 時代的到來。一方面單臺計算機的性能每年都在提升:從最早的 8 位 CPU 到現在的 64 位 CPU;從早期的 MB 級內存到現在的 GB 級別內存;從慢速的機械存儲到現在的固態 SSD 硬盤存儲。

tips:馮諾依曼模型
在這裏插入圖片描述
ENIAC 之後,電子計算機便進入了 IBM 主導的大型機時代,IBM 大型機之父吉恩.阿姆達爾被認爲是有史以來最偉大的計算機設計師之一。1964 年 4 月 7 日,在阿姆達爾的帶領下,歷時三年,耗費 50 億美元,第一臺 IBM 大型機 SYSTEM/360 誕生。這使得 IBM 在 20 實際 50~60 年代統治整個大型計算機工業,奠定了 IBM 計算機帝國的江山。

2.1 IBM 大型機曾支撐美國航天登月計劃

2.2 IBM 主機一直服務於金融等核心行業的關鍵領域

由於高可靠性和超強的計算能力,即便在 X86 和雲計算飛速發

展的情況下,IBM 的大型機依然牢牢佔據着一定的高端市場份額

20 世紀 80 年代,在大型機霸主的時代,計算機架構同時向兩個方向發展

3.1 以 CISC (微處理器執行的計算機語言指令集) CPU 爲架構

的價格便宜的面向個人的 PC

3.2 以 RISC (精簡指令集計算機) CPU 爲架構的價格昂貴的面

向企業的小型 UNIX 服務器

分佈式架構發展的里程碑

大型主機的出現。憑藉着大型機超強的計算和 I/O 處理能力、穩定性、安全性等,在很長一段時間內,大型機引領了計算機行業及商業計算領域的發展。而集中式的計算機系統架構也成爲了主流。隨着計算機的發展,這種架構越來越難以適應人們的需求,比如說

1.由於大型主機的複雜性,導致培養一個能夠熟練運維大型主機的人的成本很高

2.大型主機很貴,一般只有土豪(政府、金融、電信)才能用得起

3.單點問題,一臺大型主機出現故障,那麼整個系統將處於不可用狀態。而對於大型機的使用羣體來說,這種不可用導致的損失是非常大的

4.科技在進步,技術在進步。PC 機性能不斷提升,很多企業放棄大型機改用小型機及普通 PC 來搭建系統架構

阿里巴巴在 2009 年發起了一項"去 IOE"運動

IOE 指的是 IBM 小型機、Oracle 數據庫、EMC 的高端存儲 2009 年“去 IOE”戰略透露,到 2013 年 5 月 17 日最後一臺 IBM 小型機在支付寶下線。

爲什麼要去 IOE?

阿里巴巴過去一直採用的是 Oracle 數據庫,並利用小型機和高端存儲設備提供高性能的數據處理和存儲服務。隨着業務的不斷髮展,數據量和業務量呈爆發性增長,傳統的集中式 Oracle 數據庫架構在擴展性方面遭遇瓶頸。

傳統的商業數據庫軟件(Oracle,DB2),多以集中式架構爲主,這些傳統數據庫軟件的最大特點就是將所有的數據都集中在一個數據庫中,依靠大型高端設備來提供高處理能力和擴展性。集中式數據庫的擴展性主要採用向上擴展(Scale up)的方式,通過增加 CPU,內存,磁盤等方式提高處理能力。這種集中式數據庫的架構,使得數據庫成爲了整個系統的瓶頸,已經越來越不適應海量數據對計算能力的巨大需求

分佈式系統的意義

  1. 升級單機處理能力的性價比越來越低

單機的處理能力主要依靠 CPU、內存、磁盤。通過更換硬件做垂直擴展的方式來提升性能,成本會越來越高。

  1. 單機處理能力存在瓶頸

單機處理能力存在瓶頸,CPU、內存都會有自己的性能瓶頸,也就是說就算你是土豪不惜成本去提升硬件,但是硬件的發展速度和性能是有限制的。

  1. 穩定性和可用性這兩個指標很難達到

單機系統存在可用性和穩定性的問題,這兩個指標又是我們必須要去解決的

分佈式架構的常見概念

集羣

小飯店原來只有一個廚師,切菜洗菜備料炒菜全乾。後來客人多了,廚房一個廚師忙不過來,又請了個廚師,兩個廚師都能炒一樣的菜,這兩個廚師的關係是集羣

在這裏插入圖片描述

分佈式

爲了讓廚師專心炒菜,把菜做到極致,又請了個配菜師負責切菜,備菜,備料,廚師和配菜師的關係是分佈式,一個配菜師也忙不過來了,又請了個配菜師,兩個配菜師關係是集羣

在這裏插入圖片描述

節點

節點是指一個可以獨立按照分佈式協議完成一組邏輯的程序個體。在具體的項目中,一個節點表示的是一個操作系統上的進程。

副本機制

副本(replica/copy)指在分佈式系統中爲數據或服務提供的冗餘。

數據副本指在不同的節點上持久化同一份數據,當出現某一個節點的數據丟失時,可以從副本上讀取到數據。數據副本是分佈式系統中解決數據丟失問題的唯一手段。

服務副本表示多個節點提供相同的服務,通過主從關係來實現服務的高可用方案

中間件

中間件位於操作系統提供的服務之外,又不屬於應用,他是位於應用和系統層之間爲開發者方便的處理通信、輸入輸出的一類軟件,能夠讓用戶關心自己應用的部分。

架構的發展過程

一個成熟的大型網站系統架構並不是一開始就設計的非常完美,也不是一開始就具備高性能、高可用、安全性等特性,而是隨着用戶量的增加、業務功能的擴展逐步完善演變過來的。在這個過程中,開發模式、技術架構等都會發生非常大的變化。而針對不同業務特徵的系統,會有各自的側重點,比如像淘寶這類的網站,要解決的是海量商品搜索、下單、支付等問題;

像騰訊,要解決的是數億級別用戶的實時消息傳輸;百度所要解決的是海量數據的搜索。每一個種類的業務都有自己不同的系統架構。我們簡單模擬一個架構演變過程。

我們以 javaweb 爲例,來搭建一個簡單的電商系統,從這個系統中來看系統的演變歷史;要注意的是,接下來的演示模型,關注的是數據量、訪問量提升,網站結構發生的變化, 而不是具體關注業務功能點。其次,這個過程是爲了讓大家更好的瞭解網站演進過程中的一些問題和應對策略。假如我們系統具備以下功能:

用戶模塊:用戶註冊和管理

商品模塊:商品展示和管理

交易模塊:創建交易及支付結算

階段一,單應用架構

在這裏插入圖片描述
網站的初期也可以認爲是互聯網發展的早起,我們經常會在單機上跑我們所有的程序和軟件。

把所有軟件和應用都部署在一臺機器上,這樣就完成一個簡單系統的搭建,這個時候的講究的是效率。

階段二,應用服務器和數據庫服務器分離

隨着網站的上線,訪問量逐步上升,服務器的負載慢慢提高,在服務器還沒有超載的時候,我們應該做好規劃,提升網站的負載能力。假如代碼層面的優化已經沒辦法繼續提高,在不提高單臺機器的性能,增加機器是一個比較好的方式,投入產出比非常高。這個階段增加機器的主要目的是講 web 服務器和數據庫服務器拆分,這樣不僅提高了單機的負載能力,也提高了容災能力
在這裏插入圖片描述

階段三,應用服務器集羣

應用服務器負載告警,如何讓應用服務器走向集羣

隨着訪問量的繼續增加,單臺應用服務器已經無法滿足需求。在假設數據庫服務器還沒有遇到性能問題的時候,我們可以增加應用服務器,通過應用服務器集羣將用戶請求分流到各個服務器中,從而繼續提升負載能力。此時多臺應用服務器之間沒有直接的交互,他們都是依賴數據庫各自對外提供服務
在這裏插入圖片描述
架構發展到這個階段,各種問題也會慢慢呈現

1.用戶請求由誰來轉發到具體的應用服務器

2.用戶如果每次訪問到的服務器不一樣,那麼如何維護 session
在這裏插入圖片描述

階段四,數據庫壓力變大,數據庫讀寫分離

架構演變到這裏,並不是終點。上面我們把應用層的性能拉上來了,但是數據庫的負載也在慢慢增大,那麼怎麼去提高數據庫層面的負載呢?有了前面的思路以後,自然會想到增加服務器。但是假如我們單純的把數據庫一分爲二,然後對於後續數據庫的請求,分別負載到兩臺數據庫服務器上,那麼一定會造成數據庫不統一的問題。所以我們一般先考慮讀寫分離的方式

在這裏插入圖片描述
這個架構的變化會帶來幾個問題

1.主從數據庫之間的數據同步 ; 可以使用 mysql 自帶的 master-slave 方式實現主從複製

2.對應數據源的選擇 ; 採用第三方數據庫中間件,例如 mycat

階段五,使用搜索引擎緩解讀庫的壓力

數據庫做讀庫的話,嚐嚐對模糊查找效率不是特別好,像電商類的網站,搜索是非常核心的功能,即便是做了讀寫分離,這個問題也不能有效解決。那麼這個時候就需要引入搜索引擎了使用搜索引擎能夠大大提高我們的查詢速度,但是同時也會帶來一些附加的問題,比如維護索引的構建。
在這裏插入圖片描述

階段六,引入緩存機制緩解數據庫的壓力

隨着訪問量的持續增加,逐漸出現許多用戶訪問統一部分內容的情況,對於這些熱點數據,沒必要每次都從數據庫去讀取,我們可以使用緩存技術,比如 memcache、redis 來作爲我們應用層的緩存;另外在某些場景下,比如我們對用戶的某些 IP 的訪問頻率做限制,那這個放內存中又不合適,放數據庫又太麻煩,這個時候可以使用Nosql 的方式比如 mongDB 來代替傳統的關係型數據庫

在這裏插入圖片描述

階段七,數據庫的水平/垂直拆分

我們的網站演進的變化過程,交易、商品、用戶的數據都還在同一個數據庫中,儘管採取了增加緩存,讀寫分離的方式,但是隨着數據庫的壓力持續增加,數據庫的瓶頸仍然是個最大的問題。因此我們可以考慮對數據的垂直拆分和水平拆分

垂直拆分:把數據庫中不同業務數據拆分到不同的數據庫

在這裏插入圖片描述

水平拆分:把同一個表中的數據拆分到兩個甚至跟多的數據庫中

水平拆分的原因是某些業務數據量已經達到了單個數據庫的瓶頸,

這時可以採取講表拆分到多個數據庫中

在這裏插入圖片描述

階段八,應用的拆分

隨着業務的發展,業務越來越多,應用的壓力越來越大。工程規模也越來越龐大。這個時候就可以考慮講應用拆分,按照領域模型講我們的用戶、商品、交易拆分成多個子系統

在這裏插入圖片描述

這樣拆分以後,可能會有一些相同的代碼,比如用戶操作,在商品和交易都需要查詢,所以會導致每個系統都會有用戶查詢訪問相關操作。這些相同的操作一定是要抽象出來,否則就會是一個坑。所以通過走服務化路線的方式來解決

在這裏插入圖片描述

那麼服務拆分以後,各個服務之間如何進行遠程通信呢?

通過 RPC 技術,比較典型的有:webservice、hessian、http、RMI

等等

前期通過這些技術能夠很好的解決各個服務之間通信問題,but,互聯網的發展是持續的,所以架構的演變和優化還在持續

在這裏插入圖片描述
成熟的互聯網架構模型:
在這裏插入圖片描述
前面我們講過經典理論-馮.諾依曼體系,計算機硬件由運算器、控制器、存儲器、輸入設備、輸出設備五大部分組成。不管架構怎麼變化,計算機仍沒有跳出該體系的範疇;

輸入設備的變化

在分佈式系統架構中,輸入設備可以分兩類,第一類是互相連接的多個節點,在接收其他節點傳來的信息作爲該節點的輸入;另一種就是傳統意義上的人機交互的輸入設備了輸出設備的變化

輸出和輸入類似,也有兩種,一種是系統中的節點向其他節點傳輸信息時,該節點可以看作是輸出設備;另一種就是傳統意義上的人際交互的輸出設備,比如用戶的終端

控制器的變化

在單機中,控制器指的是 CPU 中的控制器,在分佈式系統中,控制器主要的作用是協調或控制節點之間的動作和行爲;比如硬件負載均衡器;LVS 軟負載;規則服務器等

運算器

在分佈式系統中,運算器是由多個節點來組成的。運用多個節點的計算能力來協同完成整體的計算任務

存儲器

在分佈式系統中,我們需要把承擔存儲功能的多個節點組織在一起,組成一個整體的存儲器;比如數據庫、redis(key-value 存儲)

分佈式系統的難點

毫無疑問,分佈式系統對於集中式系統而言,在實現上會更加

複雜。分佈式系統將會是更難理解、設計、構建 和管理的,同時意味着應用程序的根源問題更難發現。

三態

在集中式架構中,我們調用一個接口返回的結果只有兩種,成功或者失敗,但是在分佈式領域中,會出現“超時”這個狀態。

分佈式事務

這是一個老生常談的問題,我們都知道事務就是一些列操作的原子性保證,在單機的情況下,我們能夠依靠本機的數據庫連接和組件輕易做到事務的控制,但是分佈式情況下,業務原子性操作很可能是跨服務的,這樣就導致了分佈式事務,例如 A 和 B 操作分別是不同服務下的同一個事務操作內的操作,A 調用 B,A 如果可以清楚的知道 B 是否成功提交從而控制自身的提交還是回滾操作,但是在分佈式系統中調用會出現一個新狀態就是超時,就是 A 無法知道 B 是成功還是失敗,這個時候 A 是提交本地事務還是回滾呢?其實這是一個很難的問題,如果強行保證事務一致性,可以採取分佈式鎖,但是那樣會增加系統複雜度而且會增大系統的開銷,而且事務跨越的服務越多,消耗的資源越大,性能越低,所以最好的解決方案就是避免分佈式事務。

還有一種解決方案就是重試機制,但是重試如果不是查詢接口,
必然涉及到數據庫的變更,如果第一次調用成功但是沒返回成功結果,那調用方第二次調用對調用方來說依然是重試,但是對於被調用方來說是重複調用,例如 A 向 B 轉賬,A-100,B + 100,這樣會導致 A 扣了 100,而 B 增加 200。這樣的結果不是我們期望的,因此需在要寫入的接口做冪等設計。多次調用和單次調用是一樣的效果。通常可以設置一個唯一鍵,在寫入的時候查詢是否已經存在,避免重複寫入。但是冪等設計的一個前提就是服務是高可用,否則無論怎麼重試都不能調用返回一個明確的結果調用方會一直等待,雖然可以限制重試的次數,但是這已經進入了異常狀態了,甚至到了極端情況還是需要人肉補償處理。其實根據 CAP 和 BASE 理論,不可能在高可用分佈式情況下做到一致性,一般都是最終一致性保證。

負載均衡

每個服務單獨部署,爲了達到高可用,每個服務至少是兩臺機器,因爲互聯網公司一般使用可靠性不是特別高的普通機器,長期運行宕機概率很高,所以兩臺機器能夠大大降低服務不可用的可能性,這正大型項目會採用十幾臺甚至上百臺來部署一個服務,這不僅是保證服務的高可用,更是提升服務的 QPS,但是這樣又帶來一個問題,一個請求過來到底路由到哪臺機器?路由算法很多,有 DNS 路由,如果 session 在本機,還會根據用戶 id 或則 cookie 等信息路由到固定的機器,當然現在應用

服務器爲了擴展的方便都會設計爲無狀態的,session 會保存到專有的 session 服務器,所以不會涉及到拿不到 session 問題。那路由規則是隨機獲取麼?這是一個方法,但是據我所知,實際情況肯定比這個複雜,在一定範圍內隨機,但是在大的範圍也會分爲很多個域,例如如果爲了保證異地多活的多機房,誇機房調用的開銷太大,肯定會優先選擇同機房的服務,這個要參考具體的機器分佈來考慮。

一致性

數據被分散或者複製到不同的機器上,如何保證各臺主機之間的數據的一致性將成爲一個難點。

故障的獨立性

分佈式系統由多個節點組成,整個分佈式系統完全出問題的概率是存在的,但是在時間中出現更多的是某個節點出問題,其他節點都沒問題。這種情況下我們實現分佈式系統時需要考慮得更加全面些

分佈式架構設計

主流架構模型-SOA

架構和微服務架構

SOA 全稱(Service Oriented Architecture),中文意思爲

“面向服務的架構”,他是一種設計方法,其中包含多個服務,服務之間通過相互依賴最終提供一系列的功能。一個服務通常以獨立的形式存在與操作系統進程中。各個服務之間通過網絡調用

跟 SOA 相提並論的還有一個 ESB(企業服務總線),簡單來說 ESB 就是一根管道,用來連接各個服務節點。爲了集成不同系統,不同協議的服務,ESB 做了消息的轉化解釋和路由工作,讓不同的服務互聯互通;
在這裏插入圖片描述
在這裏插入圖片描述

SOA 所解決的核心問題

1.系統集成:站在系統的角度,解決企業系統間的通信問題,把原先散亂、無規劃的系統間的網狀結構,梳理成規整、可治理的系統間星形結構,這一步往往需要引入一些產品,比如 ESB、以及技術規範、服務管理規範;這一步解決的核心問題是【有序】

2.系統的服務化:站在功能的角度,把業務邏輯抽象成可複用、可組裝的服務,通過服務的編排實現業務的快速再生,目的:把原先固有的業務功能轉變爲通用的業務服務,實現業務邏輯的快速複用;這一步解決的核心問題是【複用】

3.業務的服務化:站在企業的角度,把企業職能抽象成可複用、可組裝的服務;把原先職能化的企業架構轉變爲服務化的企業架構,進一步提升企業的對外服務能力;“前面兩步都是從技術層面來解決系統調用、系統功能複用的問題”。第三步,則是以業務驅動把一個業務單元封裝成一項服務。這一步解決的核心問題是【高效】

微服務架構

微服務架構其實和 SOA 架構類似,微服務是在 SOA 上做的昇華,微服務架構強調的一個重點是“業務需要徹底的組件化和服務化”,原有的單個業務系統會拆分爲多個可以獨立開發、設計、運行的小應用。這些小應用之間通過服務完成交互和集成。

組件表示一個可以獨立更換和升級的單元,就像 PC 中的CPU、內存、顯卡、硬盤一樣,獨立且可以更換升級而不影響其他單元。如果我們把 PC 作爲組件以服務的方式構建,那麼這臺 PC 只需要維護主板和一些必要的外部設備。CPU、內存、硬盤都是以組件方式提供服務,PC 需要調用 CPU 做計算處理,只需要知道 CPU 這個組件的地址即可。

微服務的特徵

1.通過服務實現組件化

2.按業務能力來劃分服務和開發團隊

3.去中心化

4.基礎設施自動化(devops、自動化部署)

SOA 和微服務架構的差別

1.微服務不再強調傳統 SOA 架構裏面比較重的 ESB 企業服務總線,同時 SOA 的思想進入到單個業務系統內部實現真正的組件化

2.Docker 容器技術的出現,爲微服務提供了更便利的條件,比如更小的部署單元,每個服務可以通過類似 Node 或者 Spring Boot 等技術跑在自己的進程中。

3.還有一個點大家應該可以分析出來,SOA 注重的是系統集成方面,而微服務關注的是完全分離

領域驅動設計及業務驅動劃分

我拿來項目實戰專題的系統架構圖先講一下:
在這裏插入圖片描述
我們傳統的代碼模型就是基於Controller、Service、Dao層來實現解耦,但是通常都是Controller作爲空實現,數據透傳後,所有的業務邏輯都在Service裏去寫,這樣Service層就會顯得很重。所以,本小節提出領域概念。實現高內聚,低耦合的效果!

領域驅動設計的概念

領域驅動設計(DDD,Domain-Driven Design),軟件開發不是一蹴而就的事情,我們不可能在不瞭解產品(或行業領域)的前提下進行軟件開發,在開發前,通常需要進行大量的業務知識梳理,然後纔到軟件設計的層面,最後纔是開發。而在業務知識梳理的過程中,我們必然會形成某個領域知識,根據領域知識來一步步驅動軟件設計,就是領域驅動設計的基本概念

爲什麼需要 DDD

業務初期,功能大都非常簡單,普通的 CRUD 就能滿足,此時系統是清晰的。隨着產品不斷迭代和演化,業務邏輯變得越來越複雜,我們的系統也越來越冗雜。各個模塊之間彼此關聯,甚至到後期連作者都很難說清模塊的具體功能意圖是啥。導致在修改一個功能時,要追溯到這個功能需要的修改點就需要很長時間,更別提修改帶來的不可預知的影響面。 比如說:

在這裏插入圖片描述
訂單服務接口中提供了查詢、創建訂單相關的接口,也提供了訂單評價、支付的接口。同時訂單表是個大表,包含了非常多字段。在我們維護代碼時,將會導致牽一髮而動全身,很可能只是想改下評價相關的功能,卻影響到了創建訂單的核心路徑。雖然我們可以通過測試來保證功能完備性,但當我們在訂單領域有大量需求同時並行開發時,改動重疊、惡性循環、疲於奔命修改各種問題。

絕大部分公司都是這樣一個狀態,然後一般的解決方案是不斷的重構系統,讓系統的設計隨着業務成長也進行不斷的演進。通過重構出一些獨立的類來存放某些通用的邏輯解決混亂問題,但是我們很難給它一個業務上的含義,只能以技術緯度進行描述,這個帶來的問題就是其他人接手這塊代碼的時候不知道這個的含義或者可以通過修改這塊通用邏輯來達到某些需求

領域模型追本溯源

實領域模型本身就不是一個陌生的單詞,說直白點,在早期領域模型就是數據庫設計. 我們做傳統項目的流程或者說包括現在我們做項目的流程,都是首先討論需求,然後是數據庫建模, 在需求逐步確定的過程不斷的去更新數據庫的設計。接着我們在項目開發階段,發現有些關係沒有建、有些字段少了、有些表結構設計不合理,又在不斷的去調整設計。最後上線。在傳統項目中,數據庫是整個項目的根本,數據模型出來以後後續的開發都是圍繞着數據展開;然後形成如下的一個架構
在這裏插入圖片描述
1.service 很重,所有邏輯處理基本都放在 service 層。

2.POJO()作爲 service 層的非常重要的一個實體,會因爲不同場景的需求做不同的變化和組合,就會早成 POJO 的幾種不同模型(失血、貧血、充血),用來形容領域模型太胖或者太瘦

隨着業務變得複雜以後,包括數據結構的變化,那麼各個模塊就需要進行修改,原本清晰的系統經過不斷的演化變得複雜、冗餘、耦合度高。後果就非常嚴重

我們試想一下如果一個軟件產品不依賴數據庫存儲設備,那我們怎麼去設計這個軟件呢?如果沒有了數據存儲,那麼我們的領域模型就得基於程序本身來設計。那這個就是 DDD 需要去考慮的問題

以抽獎設計爲例
抽獎活動代碼見本文後記

BD理解起來有點抽象, 這個有點像設計模式,感覺很有用,但是不知道怎麼應用到自己寫的代碼裏面,或者生搬硬套最後看起來又很彆扭,那麼接下來以一個簡單的轉盤抽獎案例來分析一下 DDD 的應用

針對功能層面劃分邊界

這個系統可以劃分爲運營管理平臺和用戶使用層,運營平臺對於抽獎的配置比較複雜但是操作頻率會比較低。而用戶對抽獎活動頁面的使用是高頻率的但是對於配置規則來說是誤感知的,根據這樣的特點,我們把抽獎平臺劃分針對 C 端抽獎和 M 端抽獎兩個子域

在確認了 M 端領域和 C 端的限界上下文後,我們再對各自上下文內部進行限界上下文的劃分,接下來以 C 端用戶爲例來劃分界限上下文

確認基本需求

首先我們要來了解該產品的基本需求

1.抽獎資格(什麼情況下會有抽獎機會、抽獎次數、抽獎的活動起始時間)

2.抽獎的獎品(實物、優惠券、理財金、購物卡…)

3.獎品自身的配置,概率、庫存、某些獎品在有限的概率下還只能被限制抽到多少次等

4.風控對接, 防止惡意薅羊毛

針對產品功能劃分邊界
在這裏插入圖片描述
抽獎上下文是整個領域的核心,負責處理用戶抽獎的核心業務。

1.對於活動的限制,我們定義了活動資格的通用語言,將活動開始/

結束時間,活動可參與次數等限制條件都收攏到活動資格子域中。

2.由於 C 端存在一些刷單行爲,我們根據產品需求定義了風控上下文,用於對活動進行風控

3.由於抽獎和發放獎品其實可以認爲是兩個領域,一個負責根據概率去抽獎、另一個負責將選中的獎品發放出去。所以對於這一塊也獨立出來一個領域

細化上下文
通過上下文劃分以後,我們還需要進一步梳理上下文之間的關係,梳理的好處在於:

1.任務更好拆分(一個開發人員可以全身心投入到相關子

域的上下文中),

2.方便溝通,明確自身上下文和其他上下文之間的依賴關

系,可以實現更好的對接

然後是基於上下文的更進一步細化建模,在 DDD 中存在一

些名字定義

實體

當一個對象由其標識(而不是屬性)區分時,這種對象稱

爲實體(Entity)。

值對象

當一個對象用於對事物進行描述而沒有唯一標識時,它被

稱作值對象

聚合根

聚合根屬於實體對象,它是領域對象中一個高度內聚的核心對象。(聚合根具有全局的唯一標識,而實體只有在聚合內部有唯一的本地標識,值對象沒有唯一標識,不存在這個值對象或那個值對象的說法)

領域服務

一些重要的領域行爲或操作,可以歸類爲領域服務。它實現了全部業務邏輯並且通過各種校驗手段保證業務的正確性。

資源庫

資源封裝了基礎設施來提供查詢和持久化聚合操作。這樣能夠讓我們始終關注在模型層面,把對象的存儲和訪問都委託給資源庫來完成。他不是數據庫的封裝,而是領域層與基礎設施之間的橋樑。DDD 關心的是領域內的模型,而不是數據庫的操作。

代碼設計

詳見文末

在實際開發中,我們一般會採用模塊來表示一個領域的界限上下文,比如

com.test.michael.bussiness.lottery.;//抽獎上下文
com.test.michael.bussiness.riskcontrol.;// 風控上下文
com.test.michael.bussiness.prize.
;//獎品上下文
com.test.michael.bussiness.qualification.;// 活動資格上下文
com.test.michael.bussiness.stock.
;//庫存上下文*

對於模塊內的組織結構,一般情況下我們是按照領域對象、

領域服務、領域資源庫、防腐層等組織方式定義的。

com.test.michael.bussiness.lottery.domain.valobj.;//領域對象-值對象
com.test.michael.bussiness.lottery.domain.entity.;//領域對象-實體
com.test.michael.bussiness.lottery.domain.aggregate.
;//領域對象-聚合根
com.test.michael.bussiness.lottery.service.;//領域服務
com.test.michael.bussiness.lottery.repo.
;//領域資源庫*

領域驅動的好處

用 DDD 可以很好的解決領域模型到設計模型的同步、演進最後映射到實際的代碼邏輯。總的來說,DDD 有幾個好處

1.DDD 能夠讓我們知道如何抽象出限界上下文上下文以及如何去分而治之

分而治之:把複雜的大規模軟件拆分成若干個子模塊,每一個模塊都能獨立運行和解決相關問題。並且分割後各個部分可以組裝成爲一個整體。
抽象:使用抽象能夠精簡問題空間,而且問題越小越容易理解,比如說我們要對接支付,我們抽象的緯度應該是支付,而不是具體的微信支付還是支付寶支付

2.DDD 的限界上下文可以完美匹配微服務的要求

在系統複雜之後,我們都需要用分治來拆解問題。一般有兩種方式,技術維度和業務維度。技術維度是類似 MVC 這樣,業務維度則是指按業務領域來劃分系統。

微服務架構更強調從業務維度去做分治來應對系統複雜度,而 DDD 也是同樣的着重業務視角

總結

領域驅動設計其實我們可以簡單認爲是一種指導思想,是一種軟件開發方法,通過 DDD 可以將系統解構更加合理,最終滿足高內聚低耦合的本質。在我的觀點來看,有點類似數據庫的三範式,我們開始在學的時候並不太理解,當有足夠的設計經驗以後慢慢發現三範式帶來的好處。同時我們也並不一定需要嚴格按照這三範式去進行實踐,有些情況下是可以靈活調整。

分佈式架構的基本理論 CAP、BASE 以及應用

說 CAP、BASE 理論之前,先要了解下分佈式一致性的這個問題

實際上,對於不同業務的產品,我們對數據一致性的要求是不一樣的,比如 12306,他要求的是數據的嚴格一致性,不能說把票賣給用戶以後發現沒有座位了;比如銀行轉賬,你們通過銀行轉賬的時候,一般會收到一個提示:轉賬申請將會在 24 小時內到賬;實際上這個場景滿足的是最終錢只要匯出去了即可,同時以及如果錢沒匯出去要保證資金不丟失就行;所以說,用戶在使用不同的產品的時候對數據一致性的要求是不一樣的

關於分佈式一致性問題

在分佈式系統中要解決的一個重要問題就是數據的複製。在我們的日常開發經驗中,相信很多開發人員都遇到過這樣的問題:在做數據庫讀寫分離的場景中,假設客戶端 C1 將系統中的一個值 K 由 V1 更新爲 V2,但客戶端 C2 無法立即讀取到 K 的最新值,需要在一段時間之後才能 讀取到。這很正常,因爲數據庫複製之間存在延時。
在這裏插入圖片描述
所謂的分佈式一致性問題,是指在分佈式環境中引入數據複製機制之後,不同數據節點之間 可能出現的,並無法依靠計算機應用程序自身解決的數據不一致的情況。簡單講,數據一致性就是指在對一個副本數據進行更新的時候,必須確保也能夠更新其他的 副本,否則不同副本之間的數據將不一致。

那麼如何去解決這個問題?按照正常的思路,我們可能會想,既然是因爲網絡延遲導致的問題,那麼我們可以把同步動作進行阻塞,用戶 2 在查詢的時候必須要等到數據同步完成以後再來做。但是這個方案帶來的問題是性能會收到非常大的影響。如果同步的數據比較多或者比較頻繁,
那麼因爲阻塞操作可能將導致整個新系統不可用的情況;總結: 所以我們沒有辦法找到一種能夠滿足數據一致性、又不影響系統運行的性能的方案,所以這個地方就誕生了一個一致性的級別:

1.強一致性:這種一致性級別是最符合用戶直覺的,它要求系統寫入什麼,讀出來的也會是什麼,用戶體驗好,但實現起來往往對系統的性能影響大

2.弱一致性:這種一致性級別約束了系統在寫入成功後,不承諾立即可以讀到寫入的值,也不久承諾多久之後數據能夠達到一致,但會儘可能地保證到某個時間級別(比如秒級別)後,數據能夠達到一致狀態

3.最終一致性:最終一致性是弱一致性的一個特例,系統會保證在一定時間內,能夠達到一個數據一致的狀態。這裏之所以將最終一致性單獨提出來,是因爲它是弱一致性中非常推崇的一種一致性模型,也是業界在大型分佈式系統的數據一致性上比較用的多的模型

CAP 理論

一個經典的分佈式系統理論。CAP 理論告訴我們:一個分佈式系統不可能同時滿足一致性(C:Consistency)、可用性(A:Availability)和分區容錯性(P:Partition tolerance)這三個基本需求,最多隻能同時滿足其中兩項。CAP 理論

在互聯網界有着廣泛的知名度,也被稱爲“帽子理論”,它是由 Eric Brewer 教授在 2000 年舉行的 ACM 研討會提出的一個著名猜想:

一致性(Consistency)、可用性(Availability)、分區容錯(Partition-tolerance)三者無法在分佈式系統中同時被滿足,並且最多隻能滿足兩個!

一致性:所有節點上的數據時刻保持同步

可用性:每個請求都能接收一個響應,無論響應成功或失敗

分區容錯:系統應該持續提供服務,即時系統內部(某個節點分區)有消息丟失。比如交換機失敗、網址網絡被分成幾個子網,形成腦裂;服務器發生網絡延遲或死機,導致某些 server 與集羣中的其他機器失去聯繫

分區是導致分佈式系統可靠性問題的固有特性,從本質上來看,CAP 理論的準確描述不應該是從 3 個特性中選取兩個,所以我們只能被迫適應,根本沒有選擇權;

總結一下:CAP 並不是一個普適性原理和指導思想,它僅適用於原子讀寫的 NoSql 場景中,並不適用於數據庫系統。

BASE 理論

從前面的分析中知道:在分佈式(數據庫分片或分庫存在的多個實例上)系統下,CAP 理論並不適合數據庫事務(因爲更新一些錯誤的數據而導致的失敗,無論使用什麼樣的高可用方案都是徒勞,因爲數據發生了無法修正的錯誤)。此外 XA 事務雖然保證了數據庫在分佈式系統下的 ACID (原子性、一致性、隔離性、持久性)特性,但也帶來了一些性能方面的代價,對於併發和響應時間要求比較高的電商平臺來說,是很難接受的。

eBay 嘗試了另外一條完全不同的路,放寬了數據庫事務的 ACID 要求,提出了一套名爲 BASE 的新準則。BASE 全稱是 Basically available,soft-state,Eventually Consistent.

系統基本可用、軟狀態、數據最終一致性。相對於 CAP 來說,它大大降低了我們對系統的要求。

Basically available(基本可用),在分佈式系統出現不可預知的故障時,允許瞬時部分可用性

1.比如我們在淘寶上搜索商品,正常情況下是在 0.5s 內返回查詢結果,但是由於後端的系統故障導致查詢響應時間變成了 2s

2.再比如數據庫採用分片模式,100W 個用戶數據分在 5 個數據庫實例上,如果破壞了一個實例,那麼可用性還有 80%,也就是 80%的用戶都可以登錄,系統仍然可用
3.電商大促時,爲了應對訪問量激增,部分用戶可能會被引導到降級頁面,服務層也可能只提供降級服務。這就是損失部分可用性的體現

soft-state(軟狀態). 表示系統中的數據存在中間狀態,並且這個中間狀態的存在不會影響系統的整體可用性,也就是表示系統允許在不同節點的數據副本之間進行數據同步過程中存在延時;比如訂單狀態,有一個待支付、支付中、支付成功、支付失敗, 那麼支付中就是一箇中間狀態,這個中間狀態在支付成功以後,在支付表中的狀態同步給訂單狀態之前,中間會存在一個時間內的不一致。

Eventually consistent(數據的最終一致性),表示的是所有數據副本在一段時間的同步後最終都能達到一個一直的狀態,因此最終一致性的本質是要保證數據最終達到一直,而不需要實時保證系統數據的強一致

BASE 理論的核心思想是:即使無法做到強一致性,但每個應用都可以根據自身業務特點,採用適當的方式來使系統達到最終一致性

分佈式架構下的高可用設計

避免單點故障

a)負載均衡技術(failover/選址/硬件負載/

軟件負載 / 去中心化的軟件負載( gossip(redis-cluster)))

b)熱備(linux HA)

c)多機房(同城災備、異地災備)

應用的高可用性

a)故障監控(系統監控(cpu、內存)/鏈路監控/日誌監控) 自動預警

b)應用的容錯設計、(服務降級、限流)自我保護能力

c)數據量(數據分片、讀寫分離)

分佈式架構下的可伸縮設計

垂直伸縮 提升硬件能力

水平伸縮 增加服務器

加速靜態內容訪問速度的 CDN

CDN 是 Content Delivery Network 的縮寫,表示的是內容分發網絡。CDN 的作用是把用戶需要的內容分發到離用戶最近的地方,這樣可以是用戶能夠快熟獲取所需要的內容。 CDN 其實就是一種網絡緩存技術,能夠把一些相對穩定的資源放到距離最終用戶較近的地方,一方面可以節省整個

廣域網的貸款消耗,另外一方面可以提升用戶的訪問速度,改進用戶體驗。我們一般會把靜態的文件(圖片、腳本、靜態頁面)放到 CDN 中

在這裏插入圖片描述

1.當用戶點擊網站頁面上的內容 URL,經過本地 DNS 系統解析,DNS系統會最終將域名的解析權交給 CNAME 指向的 CDN 專用 DNS 服務器

2.CDN 的 DNS 服務器將 CDN 的全局負載均衡設備 IP 地址返回用戶

3.用戶向 CDN 的全局負載均衡設備發起內容 URL 訪問請求

4.CDN 全局負載均衡設備根據用戶 IP 地址,以及用戶請求的內容 URL,選擇一臺用戶所屬區域的區域負載均衡設備,告訴用戶向這臺設備發起請求。

5.區域負載均衡設備會爲用戶選擇一臺合適的緩存服務器提供服務,選擇的依據包括:根據用戶 IP 地址,判斷哪一臺服務器距用戶最近;根據用戶所請求的 URL 中攜帶的內容名稱,判斷哪一臺服務器上有用戶所需內容;查詢各個服務器當前的負載情況,判斷哪一臺服務器尚有服務能力。基於以上這些條件的綜合分析之後,區域負載均衡設備會向全局負載均衡設備返回一臺緩存服務器的 IP 地址

6.局負載均衡設備把服務器的 IP 地址返回給用戶

用戶向緩存服務器發起請求,緩存服務器響應用戶請求,將用戶所需內容傳送到用戶終端。如果這臺緩存服務器上並沒有用戶想要的內容,而區域均衡設備依然將它分配給了用戶,那麼這臺服務器就要向它的上一級緩存服務器請求內容,直至追溯到網站的源服務器將內容拉到本地。

什麼情況下用 CDN

最適合的是那些不會經常變化的內容,比如圖片,JS 文件, CSS 文件,圖片文件包括程序模板中的,CSS 文件中用到的背景圖片,還有就是作爲網站內容組成部分的那些圖片,都可以;

灰度發佈

發佈的時候一般會採用灰度發佈,也就是會對新應用進行分批發布,逐步擴大新應用在整個及羣衆的比例直到最後全部完成。灰度發佈是針對新引用在用戶體驗方面完全無感知。

灰度發佈系統的作用在於,可以根據自己的配置,來將用戶的流量導到新上線的系統上,來快速驗證新的功能修改,而一旦出問題,也可以馬上的恢復,簡單的說,就是一套 A/BTest 系統.
在這裏插入圖片描述

後記

本節抽獎活動項目代碼:
抽獎活動 github項目地址

本節相關推薦書籍:
《大型網站系統與JAVA中間件實踐》
提取碼:nzot

下節預告:

分佈式架構的基礎

  • 分佈式架構基石- TCP通信協議
  • 分佈式系統的基礎HTTP及HTTPS協議
  • 分佈式架構原理之序列化與反序列化
發佈了57 篇原創文章 · 獲贊 5 · 訪問量 5017
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章