1. 結算頁商品清單
一.需求分析
- 在訂單結算頁顯示送貨清單、合計金額、優惠金額。
顯示購物清單應該重新整理購物車,從資料庫中查詢最新價格計算。
下方顯示合計件數與合計金額。
二.實作思路
- 後端新增方法,擷取購物車清單循環更新每個商品的價格,重新整理資料後更新購物車,并傳回更新後的購物車資料。
- 前端擷取清單後循環顯示,顯示合計件數與合計金額參考購物車頁面的實作方式。
三.後端代碼
- CartService新增方法定義
- CartServiceImpl實作該方法
- CartController新增方法
四.前端代碼
- 将order.html拷貝至qingcheng_web_portal
- 修改order.html,為div添加id 添加js代碼
- 修改頁面中購物車清單部分 修改頁面合計數部分
2. 收貨位址選擇
一.需求分析
- 在結算頁上列出收貨位址 點選收貨人姓名後,即選中該收貨人,在結算頁下方顯示位址、收貨人姓名和電話
二.實作思路
- 首先我們先看一下user資料庫的收貨位址表 ( tb_address 表)表結構
後端實作根據目前登陸人查詢收貨位址清單的方法,前端使用Vue.js進行渲染。
選擇收貨位址通過vue.js實作。
三.收貨位址清單
- AddressService新增方法定義
- AddressServiceImpl實作方法
- CartController新增方法
- 修改 cart.html ,增加屬性,用于存儲位址清單
- order.html新增方法,用于查詢位址清單
- 修改order.html的位址清單部分
四.選擇收貨位址
- order.html 新增屬性,用于儲存訂單(訂單包含了位址、手機号、聯系人等資訊)
- order.html新增方法,用于選擇位址
- 修改order.html位址顯示區域
- 修改order.html的findAddressList方法,在回調後添加以下代碼,實作預設位址的顯示
- 修改位址清單,增加樣式和點選事件的調用
3. 送出訂單
一.需求分析
- 選擇支付方式 點選“送出訂單”按鈕,完成訂單儲存動作。
将購物車中選中項儲存為訂單。如果是線上支付則跳轉到支付頁面,如果是貨到付款則直接跳轉到完成頁。
儲存訂單前需要進行庫存進行檢查和扣減,如果庫存不足則不能下單。
注意:在送出儲存訂單時,需要再次調用重新整理購物車的方法。
二.實作思路
-
首先我們先看一下相關的表結構
tb_sku 表
- tb_order 表(訂單主表)
- tb_order_item 表(訂單明細表)
- 重新整理并擷取購物車
- 檢查庫存與扣減庫存,增加銷量
- 儲存訂單主表與明細表,明細表的資料來自購物車
- 清除選中的購物車資料
三.庫存扣減邏輯
- SkuMapper新增方法
- SkuService新增方法
- SkuServiceImpl實作此方法
四.儲存訂單邏輯
- 修改OrderService的add方法的定義,修改傳回值,用于傳回訂單号和金額
- 修改OrderServiceImpl的add方法
- 訂單服務添加 applicationContext-service.xml
- CartController新增方法
五.前端代碼
- 修改cart.html的order屬性,增加payType并設定預設值為1
- 修改cart.html的支付方式選擇部分
- 對留言文本框進行綁定
- 新增方法
- 調用方法
六.結果頁面
- 從靜态原型中拷貝三個頁面到web_portal 分别是pay.html 、order-success.html 、order-fail.html
- pay.html 和order-success.html 添加vue.js代碼
- 綁定
4. 分布式事務解決方案
剛才我們編寫的扣減庫存與儲存訂單是在兩個服務中存在的,如果扣減庫存後訂單儲存失敗了是不會復原的,這樣就會造成資料不一緻的情況,這其實就是我們所說的分布式事務的問題,接下來我們來學習分布式事務的解決方案。
一.本地事務與分布式事務
- 事務
- 資料庫事務(簡稱:事務,Transaction)是指資料庫執行過程中的一個邏輯機關,由一個有限的資料庫操作序列構成。
-
事務擁有以下四個特性,習慣上被稱為ACID特性:
1.原子性(Atomicity):事務作為一個整體被執行,包含在其中的對資料庫的操作要麼全部被執行,要麼都不執行。
2.一緻性(Consistency):事務應確定資料庫的狀态從一個一緻狀态轉變為另一個一緻狀态。一緻狀态是指資料庫中的資料應滿足完整性限制。除此之外,一緻性還有另外一層語義,就是事務的中間狀态不能被觀察到(這層語義也有說應該屬于原子性)。
3.隔離性(Isolation):多個事務并發執行時,一個事務的執行不應影響其他事務的執行,如同隻有這一個操作在被資料庫所執行一樣。
4.持久性(Durability):已被送出的事務對資料庫的修改應該永久儲存在資料庫中.在事務結束時,此操作将不可逆轉。
-
本地事務
起初,事務僅限于對單一資料庫資源的通路控制,架構服務化以後,事務的概念延伸到了服務中。倘若将一個單一的服務操作作為一個事務,那麼整個服務操作隻能涉及一個單一的資料庫資源,這類基于單個服務單一資料庫資源通路的事務,被稱為本地事務(LocalTransaction)。
- 分布式事務
- 分布式事務指事務的參與者、支援事務的伺服器、資源伺服器以及事務管理器分别位于不同的分布式系統的不同節點之上,且屬于不同的應用,分布式事務需要保證這些操作要麼全部成功,要麼全部失敗。本質上來說,分布式事務就是為了保證不同資料庫的資料一緻性。
- 最早的分布式事務應用架構很簡單,不涉及服務間的通路調用,僅僅是服務内操作涉及到對多個資料庫資源的通路。
- 當一個服務操作通路不同的資料庫資源,又希望對它們的通路具有事務特性時,就需要采用分布式事務來協調所有的事務參與者。
- 對于上面介紹的分布式事務應用架構,盡管一個服務操作會通路多個資料庫資源,但是畢竟整個事務還是控制在單一服務的内部。如果一個服務操作需要調用另外一個服務,這時的事務就需要跨越多個服務了。在這種情況下,起始于某個服務的事務在調用另外一個服務的時候,需要以某種機制流轉到另外一個服務,進而使被調用的服務通路的資源也自動加入到該事務當中來。
- 如果将上面這兩種場景(一個服務可以調用多個資料庫資源,也可以調用其他服務)結合在一起,對此進行延伸,整個分布式事務的參與者将會組成如下圖所示的樹形拓撲結構。在一個跨服務的分布式事務中,事務的發起者和送出均系同一個,它可以是整個調用的用戶端,也可以是用戶端最先調用的那個服務。
- 較之基于單一資料庫資源通路的本地事務,分布式事務的應用架構更為複雜。在不同的分布式應用架構下,實作一個分布式事務要考慮的問題并不完全一樣,比如對多資源的協調、事務的跨服務傳播等,實作機制也是複雜多變。
二.分布式事務相關理論
- CAP定理
-
CAP定理是在 1998年加州大學的計算機科學家 Eric Brewer (埃裡克.布魯爾)提出,分布式系統有三個名額
Consistency 一緻性
Availability 可用性
Partition tolerance 分區容錯
它們的第一個字母分别是 C、A、P。Eric Brewer 說,這三個名額不可能同時做到。這個結論就叫做 CAP 定理。
-
分區容錯 Partition tolerance
大多數分布式系統都分布在多個子網絡。每個子網絡就叫做一個區(partition)。分區容錯的意思是,區間通信可能失敗。比如,一台伺服器放在中國,另一台伺服器放在美國,這就是兩個區,它們之間可能無法通信。
上圖中,G1 和 G2 是兩台跨區的伺服器。G1 向 G2 發送一條消息,G2 可能無法收到。系統設計的時候,必須考慮到這種情況。
一般來說,分區容錯無法避免,是以可以認為 CAP 的 P 總是成立。CAP 定理告訴我們,剩下的 C 和 A 無法同時做到。
-
可用性 Availability
Availability 中文叫做"可用性",意思是隻要收到使用者的請求,伺服器就必須給出回應。使用者可以選擇向 G1 或 G2 發起讀操作。不管是哪台伺服器,隻要收到請求,就必須告訴使用者,到底是 v0 還是 v1,否則就不滿足可用性。
-
一緻性 Consistency
Consistency 中文叫做"一緻性"。意思是,寫操作之後的讀操作,必須傳回該值。舉例來說,某條記錄是 v0,使用者向 G1 發起一個寫操作,将其改為 v1。
問題是,使用者有可能向 G2 發起讀操作,由于 G2 的值沒有發生變化,是以傳回的是v0。G1 和 G2 讀操作的結果不一緻,這就不滿足一緻性了。
為了讓 G2 也能變為 v1,就要在 G1 寫操作的時候,讓 G1 向 G2 發送一條消息,要求G2 也改成 v1。
-
一緻性和可用性的沖突
一緻性和可用性,為什麼不可能同時成立?答案很簡單,因為可能通信失敗(即出現分區容錯)。
如果保證 G2 的一緻性,那麼 G1 必須在寫操作時,鎖定 G2 的讀操作和寫操作。隻有資料同步後,才能重新開放讀寫。鎖定期間,G2 不能讀寫,沒有可用性。如果保證 G2 的可用性,那麼勢必不能鎖定 G2,是以一緻性不成立。
- 綜上所述,G2 無法同時做到一緻性和可用性。系統設計時隻能選擇一個目标。如果追求一緻性,那麼無法保證所有節點的可用性;如果追求所有節點的可用性,那就沒法做到一緻性。
-
- BASE理論
- BASE:全稱:Basically Available(基本可用),Soft state(軟狀态),和 Eventuallyconsistent(最終一緻性)三個短語的縮寫,來自 ebay 的架構師提出。BASE 理論是對CAP 中一緻性和可用性權衡的結果,其來源于對大型網際網路分布式實踐的總結,是基于CAP 定理逐漸演化而來的。其核心思想是:
-
Basically Available(基本可用)
什麼是基本可用呢?假設系統,出現了不可預知的故障,但還是能用,相比較正常的系統而言:
1. 響應時間上的損失:正常情況下的搜尋引擎 0.5 秒即傳回給使用者結果,而基本可用的搜尋引擎可以在 1 秒作用傳回結果。
2. 功能上的損失:在一個電商網站上,正常情況下,使用者可以順利完成每一筆訂單,但是到了大促期間,為了保護購物系統的穩定性,部分消費者可能會被引導到一個降級頁面。
-
Soft state(軟狀态)
什麼是軟狀态呢?相對于原子性而言,要求多個節點的資料副本都是一緻的,這是一種“硬狀态”。
軟狀态指的是:允許系統中的資料存在中間狀态,并認為該狀态不影響系統的整體可用性,即允許系統在多個不同節點的資料副本存在資料延時。
-
Eventually consistent(最終一緻性)
系統能夠保證在沒有其他新的更新操作的情況下,資料最終一定能夠達到一緻的狀态,是以所有用戶端對系統的資料通路最終都能夠擷取到最新的值。
三.分布式事務解決方案
-
基于XA協定的兩階段送出
首先我們來簡要看下分布式事務處理的XA規範 :
可知XA規範中分布式事務有AP,RM,TM組成:
- 1 - 其中應用程式(Application Program ,簡稱AP):AP定義事務邊界(定義事務開始和結束)并通路事務邊界内的資源。
- 2 - 資料總管(Resource Manager,簡稱RM):Rm管理計算機共享的資源,許多軟體都可以去通路這些資源,資源包含比如資料庫、檔案系統、列印機伺服器等。
- 3 - 事務管理器(Transaction Manager ,簡稱TM):負責管理全局事務,配置設定事務唯一辨別,監控事務的執行進度,并負責事務的送出、復原、失敗恢複等。
二階段協定:
- 第一階段TM要求所有的RM準備送出對應的事務分支,詢問RM是否有能力保證成功的送出事務分支,RM根據自己的情況,如果判斷自己進行的工作可以被送出,那就就對工作内容進行持久化,并給TM回執OK;否者給TM的回執NO。RM在發送了否定答複并復原了已經的工作後,就可以丢棄這個事務分支資訊了。
- 第二階段TM根據階段1各個RM prepare的結果,決定是送出還是復原事務。如果所有的RM都prepare成功,那麼TM通知所有的RM進行送出;如果有RM prepare回執NO的話,則TM通知所有RM復原自己的事務分支。
- 也就是TM與RM之間是通過兩階段送出協定進行互動的。
-
優點: 盡量保證了資料的強一緻,适合對資料強一緻要求很高的關鍵領域。(其實也不能100%保證強一緻)
缺點: 實作複雜,犧牲了可用性,對性能影響較大,不适合高并發高性能場景。
-
TCC補償機制
TCC 其實就是采用的補償機制,其核心思想是:針對每個操作,都要注冊一個與其對應的确認和補償(撤銷)操作。它分為三個階段:
- Try 階段主要是對業務系統做檢測及資源預留
- Confirm 階段主要是對業務系統做确認送出,Try階段執行成功并開始執行 Confirm階段時,預設 Confirm階段是不會出錯的。即:隻要Try成功,Confirm一定成功。
- Cancel 階段主要是在業務執行錯誤,需要復原的狀态下執行的業務取消,預留資源釋放。
- 例如: A要向 B 轉賬,思路大概是:
優點: 相比兩階段送出,可用性比較強
缺點: 資料的一緻性要差一些。TCC屬于應用層的一種補償方式,是以需要程式員在實作的時候多寫很多補償的代碼,在一些場景中,一些業務流程可能用TCC不太好定義及處理。
-
消息最終一緻性
消息最終一緻性應該是業界使用最多的,其核心思想是将分布式事務拆分成本地事務進行處理,這種思路是來源于ebay。我們可以從下面的流程圖中看出其中的一些細節:
基本思路就是:
- 1 - 消息生産方,需要額外建一個消息表,并記錄消息發送狀态。消息表和業務資料要在一個事務裡送出,也就是說他們要在一個資料庫裡面。然後消息會經過MQ發送到消息的消費方。如果消息發送失敗,會進行重試發送。
- 2 - 消息消費方,需要處理這個消息,并完成自己的業務邏輯。此時如果本地事務處理成功,表明已經處理成功了,如果處理失敗,那麼就會重試執行。如果是業務上面的失敗,可以給生産方發送一個業務補償消息,通知生産方進行復原等操作。
- 3 - 生産方和消費方定時掃描本地消息表,把還沒處理完成的消息或者失敗的消息再發送一遍。如果有靠譜的自動對賬補賬邏輯,這種方案還是非常實用的。
優點: 一種非常經典的實作,避免了分布式事務,實作了最終一緻性。
缺點: 消息表會耦合到業務系統中,如果沒有封裝好的解決方案,會有很多雜活需要處理。
5. 庫存扣減分布式事務的實作
一.需求分析
- 如果訂單在建立時發生了異常,此時庫存已經扣減了,這樣庫存就比實際中的數量要少,造成資料不一緻,為了避免這種情況,我們需要采用消息最終一緻性的分布式事務解決方案。
二.實作思路
- 當訂單服務發生異常時,發送消息給mq ,消息内容為購物車資料,用于恢複庫存
- 商品服務從mq提取消息,儲存到庫存復原表中
- 在管理背景開啟定時任務,定時掃描庫存復原表執行庫存復原。
- 下面我們來看一下庫存復原表tb_stock_back的設計 為什麼要設定成聯合主鍵的形式呢? 因為消息在發送時,可能會因為失敗而重複發送,而重複發送可能造成復原資料的重複。訂單編号+sku編号組合起來如果相同一定是同一筆資料 ,是以我們将其設定成聯合主鍵,這樣在插入重複資料時就會因為主鍵唯一沖突而無法插入。
三.發送庫存復原消息
- qingcheng_service_order工程pom.xml引入依賴
- 添加配置檔案applicationContext-rabbitmq-producer.xml
- 修改OrderServiceImpl的add方法
四.生成庫存復原記錄
- 實作思路:從消息隊列中取出購物車清單json,轉換為清單後查詢到庫存復原表。
- qingcheng_service_goods工程pom.xml引入依賴
- 新增StockBack實體類
- 新增StockBack資料通路接口
- StockBackService新增方法定義
- StockBackServiceImpl實作方法
- 編寫消費端,從消息中取出消息,轉換json字元串并調用服務方法
- qingcheng_service_goods添加配置檔案applicationContext-rabbitmqconsumer.xml
五.定時執行庫存復原
- 實作思路:編寫定時任務,間隔一小時執行庫存復原。查詢庫存復原記錄表中狀态為0的記錄,執行復原邏輯。
- StockBackService新增方法定義
- StockBackServiceImpl實作方法
- qingcheng_web_manager添加任務排程類