分佈式事務node
指事務的每一個操做步驟都位於不一樣的節點上,須要保證事務的 AICD 特性。web
1. 產生緣由算法
數據庫分庫分表;數據庫
SOA 架構,好比一個電商網站將訂單業務和庫存業務分離出來放到不一樣的節點上。瀏覽器
2. 應用場景緩存
下單:減小庫存同時更新訂單狀態。庫存和訂單不在不一樣一個數據庫,所以涉及分佈式事務。tomcat
支付:買家帳戶扣款同時賣家帳戶入帳。買家和賣家帳戶信息不在同一個數據庫,所以涉及分佈式事務。服務器
3. 解決方案網絡
3.1 兩階段提交協議架構
兩階段提交協議能夠很好得解決分佈式事務問題,它可使用 XA 來實現,XA 它包含兩個部分:事務管理器和本地資源管理器。其中本地資源管理器每每由數據庫實現,好比 Oracle、DB2 這些商業數據庫都實現了 XA 接口,而事務管理器做爲全局的協調者,負責各個本地資源的提交和回滾。
3.2 消息中間件
消息中間件也可稱做消息系統 (MQ),它本質上是一個暫存轉發消息的一箇中間件。在分佈式應用當中,咱們能夠把一個業務操做轉換成一個消息,好比支付寶的餘額轉如餘額寶操做,支付寶系統執行減小余額操做以後向消息系統發一個消息,餘額寶系統訂閱這條消息而後進行增長帳戶金額操做。
3.2.1 消息處理模型
點對點
.
發佈/訂閱
.
3.2.2 消息的可靠性
消息的發送端的可靠性:發送端完成操做後必定能將消息成功發送到消息系統。
消息的接收端的可靠性:接收端僅且可以從消息中間件成功消費一次消息。
發送端的可靠性
在本地數據建一張消息表,將消息數據與業務數據保存在同一數據庫實例裏,這樣就能夠利用本地數據庫的事務機制。事務提交成功後,將消息表中的消息轉移到消息中間件,若轉移消息成功則刪除消息表中的數據,不然繼續重傳。
接收端的可靠性
保證接收端處理消息的業務邏輯具備冪等性:只要具備冪等性,那麼消費多少次消息,最後處理的結果都是同樣的。
保證消息具備惟一編號,並使用一張日誌表來記錄已經消費的消息編號。
負載均衡的算法與實現
1. 算法
1.1 輪詢(Round Robin)
輪詢算法把每一個請求輪流發送到每一個服務器上。下圖中,一共有 6 個客戶端產生了 6 個請求,這 6 個請求按 (1, 2, 3, 4, 5, 6) 的順序發送。最後,(1, 3, 5) 的請求會被髮送到服務器 1,(2, 4, 6) 的請求會被髮送到服務器 2。
.
該算法比較適合每一個服務器的性能差很少的場景,若是有性能存在差別的狀況下,那麼性能較差的服務器可能沒法承擔多大的負載。下圖中,服務器 2 的性能比服務器 1 差,那麼服務器 2 可能沒法承擔多大的負載。
.
1.2 加權輪詢(Weighted Round Robbin)
加權輪詢是在輪詢的基礎上,根據服務器的性能差別,爲服務器賦予必定的權值。例以下圖中,服務器 1 被賦予的權值爲 5,服務器 2 被賦予的權值爲 1,那麼 (1, 2, 3, 4, 5) 請求會被髮送到服務器 1,(6) 請求會被髮送到服務器 2。
.
1.3 最少鏈接(least Connections)
因爲每一個請求的鏈接時間不同,使用輪詢或者加權輪詢算法的話,可能會讓一臺服務器當前鏈接數多大,而另外一臺服務器的鏈接多小,形成負載不均衡。例以下圖中,(1, 3, 5) 請求會被髮送到服務器 1,可是 (1, 3) 很快就斷開鏈接,此時只有 (5) 請求鏈接服務器 1;(2, 4, 6) 請求被髮送到服務器 2,它們的鏈接都尚未斷開,繼續運行時,服務器 2 會承擔多大的負載。
.
最少鏈接算法就是將請求發送給當前最少鏈接數的服務器上。例以下圖中,服務器 1 當前鏈接數最小,那麼請求 6 就會被髮送到服務器 1 上。
.
1.4 加權最小鏈接(Weighted Least Connection)
在最小鏈接的基礎上,根據服務器的性能爲每臺服務器分配權重,而後根據權重計算出每臺服務器能處理的鏈接數。
.
1.5 隨機算法(Random)
把請求隨機發送到服務器上。和輪詢算法相似,該算法比較適合服務器性能差很少的場景。
.
2. 實現
2.1 DNS 解析
使用 DNS 做爲負載均衡器,會根據負載狀況返回不一樣服務器的 IP 地址。大型網站基本使用了這種方式最爲第一級負載均衡手段,而後在內部在第二級負載均衡。
.
2.2 修改 MAC 地址
使用 LVS(Linux Virtual Server)這種鏈路層負載均衡器,根據負載狀況修改請求的 MAC 地址。
.
2.3 修改 IP 地址
在網絡層修改請求的目的 IP 地址。
.
2.4 HTTP 重定向
HTTP 重定向負載均衡服務器收到 HTTP 請求以後會返回服務器的地址,並將該地址寫入 HTTP 重定向響應中返回給瀏覽器,瀏覽器收到後再次發送請求。
.
2.5 反向代理
正向代理:發生在客戶端,是由用戶主動發起的。好比FQ,客戶端經過主動訪問代理服務器,讓代理服務器得到須要的外網數據,而後轉發回客戶端。
反向代理:發生在服務器端,用戶不知道發生了代理。
.
分佈式鎖
Java 提供了兩種內置的鎖的實現,一種是由 JVM 實現的 synchronized 和 JDK 提供的 Lock,當你的應用是單機或者說單進程應用時,可使用 synchronized 或 Lock 來實現鎖。當應用涉及到多機、多進程共同完成時,那麼這時候就須要一個全局鎖來實現多個進程之間的同步。
1. 使用場景
例如一個應用有手機 APP 端和 Web 端,若是在兩個客戶端同時進行一項操做時,那麼就會致使這項操做重複進行。
2. 實現方式
2.1 數據庫分佈式鎖
基於 MySQL 鎖表
該實現方式徹底依靠數據庫惟一索引來實現。當想要得到鎖時,就向數據庫中插入一條記錄,釋放鎖時就刪除這條記錄。若是記錄具備惟一索引,就不會同時插入同一條記錄。這種方式存在如下幾個問題:
鎖沒有失效時間,解鎖失敗會致使死鎖,其餘線程沒法再得到鎖。
只能是非阻塞鎖,插入失敗直接就報錯了,沒法重試。
不可重入,同一線程在沒有釋放鎖以前沒法再得到鎖。
採用樂觀鎖增長版本號
根據版本號來判斷更新以前有沒有其餘線程更新過,若是被更新過,則獲取鎖失敗。
2.2 Redis 分佈式鎖
基於 SETNX、EXPIRE
使用 SETNX(set if not exist)命令插入一個鍵值對時,若是 Key 已經存在,那麼會返回 False,不然插入成功並返回 True。所以客戶端在嘗試得到鎖時,先使用 SETNX 向 Redis 中插入一個記錄,若是返回 True 表示得到鎖,返回 False 表示已經有客戶端佔用鎖。
EXPIRE 能夠爲一個鍵值對設置一個過時時間,從而避免了死鎖的發生。
RedLock 算法
ReadLock 算法使用了多個 Redis 實例來實現分佈式鎖,這是爲了保證在發生單點故障時還可用。
嘗試從 N 個相互獨立 Redis 實例獲取鎖,若是一個實例不可用,應該儘快嘗試下一個。
計算獲取鎖消耗的時間,只有當這個時間小於鎖的過時時間,而且從大多數(N/2+1)實例上獲取了鎖,那麼就認爲鎖獲取成功了。
若是鎖獲取失敗,會到每一個實例上釋放鎖。
2.3 Zookeeper 分佈式鎖
Zookeeper 是一個爲分佈式應用提供一致性服務的軟件,例如配置管理、分佈式協同以及命名的中心化等,這些都是分佈式系統中很是底層並且是必不可少的基本功能,可是若是本身實現這些功能並且要達到高吞吐、低延遲同時還要保持一致性和可用性,實際上很是困難。
抽象模型
Zookeeper 提供了一種樹形結構級的命名空間,/app1/p_1 節點表示它的父節點爲 /app1。
.
節點類型
永久節點:不會由於會話結束或者超時而消失;
臨時節點:若是會話結束或者超時就會消失;
有序節點:會在節點名的後面加一個數字後綴,而且是有序的,例如生成的有序節點爲 /lock/node-0000000000,它的下一個有序節點則爲 /lock/node-0000000001,依次類推。
監聽器
爲一個節點註冊監聽器,在節點狀態發生改變時,會給客戶端發送消息。
分佈式鎖實現
建立一個鎖目錄 /lock。
在 /lock 下建立臨時的且有序的子節點,第一個客戶端對應的子節點爲 /lock/lock-0000000000,第二個爲 /lock/lock-0000000001,以此類推。
客戶端獲取 /lock 下的子節點列表,判斷本身建立的子節點是否爲當前子節點列表中序號最小的子節點,若是是則認爲得到鎖,不然監聽本身的前一個子節點,得到子節點的變動通知後重復此步驟直至得到鎖;
執行業務代碼,完成後,刪除對應的子節點。
會話超時
若是一個已經得到鎖的會話超時了,由於建立的是臨時節點,所以該會話對應的臨時節點會被刪除,其它會話就能夠得到鎖了。能夠看到,Zookeeper 分佈式鎖不會出現數據庫分佈式鎖的死鎖問題。
羊羣效應
在步驟二,一個節點未得到鎖,須要監聽監聽本身的前一個子節點,這是由於若是監聽全部的子節點,那麼任意一個子節點狀態改變,其它全部子節點都會收到通知,而咱們只但願它的下一個子節點收到通知。
分佈式 Session
若是不作任何處理的話,用戶將出現頻繁登陸的現象,好比集羣中存在 A、B 兩臺服務器,用戶在第一次訪問網站時,Nginx 經過其負載均衡機制將用戶請求轉發到 A 服務器,這時 A 服務器就會給用戶建立一個 Session。當用戶第二次發送請求時,Nginx 將其負載均衡到 B 服務器,而這時候 B 服務器並不存在 Session,因此就會將用戶踢到登陸頁面。這將大大下降用戶體驗度,致使用戶的流失,這種狀況是項目毫不應該出現的。
1. 粘性 Session
原理
粘性 Session 是指將用戶鎖定到某一個服務器上,好比上面說的例子,用戶第一次請求時,負載均衡器將用戶的請求轉發到了 A 服務器上,若是負載均衡器設置了粘性 Session 的話,那麼用戶之後的每次請求都會轉發到 A 服務器上,至關於把用戶和 A 服務器粘到了一塊,這就是粘性 Session 機制。
優勢
簡單,不須要對 Session 作任何處理。
缺點
缺少容錯性,若是當前訪問的服務器發生故障,用戶被轉移到第二個服務器上時,他的 Session 信息都將失效。
適用場景
發生故障對客戶產生的影響較小;
服務器發生故障是低機率事件。
2. 服務器 Session 複製
原理
任何一個服務器上的 Session 發生改變,該節點會把這個 Session 的全部內容序列化,而後廣播給全部其它節點,無論其餘服務器需不須要 Session,以此來保證 Session 同步。
優勢
可容錯,各個服務器間 Session 可以實時響應。
缺點
會對網絡負荷形成必定壓力,若是 Session 量大的話可能會形成網絡堵塞,拖慢服務器性能。
實現方式
設置 Tomcat 的 server.xml 開啓 tomcat 集羣功能。
在應用裏增長信息:通知應用當前處於集羣環境中,支持分佈式,即在 web.xml 中添加 選項。
3. Session 共享機制
使用分佈式緩存方案好比 Memcached、Redis,可是要求 Memcached 或 Redis 必須是集羣。
使用 Session 共享也分兩種機制,兩種狀況以下:
3.1 粘性 Session 共享機制
和粘性 Session 同樣,一個用戶的 Session 會綁定到一個 Tomcat 上。Memcached 只是起到備份做用。
.
3.2 非粘性 Session 共享機制
原理
Tomcat 自己不存儲 Session,而是存入 Memcached 中。Memcached 集羣構建主從複製架構。
.
優勢
可容錯,Session 實時響應。
實現方式
用開源的 msm 插件解決 Tomcat 之間的 Session 共享:Memcached_Session_Manager(MSM)
4. Session 持久化到數據庫
原理
拿出一個數據庫,專門用來存儲 Session 信息。保證 Session 的持久化。
優勢
服務器出現問題,Session 不會丟失
缺點
若是網站的訪問量很大,把 Session 存儲到數據庫中,會對數據庫形成很大壓力,還須要增長額外的開銷維護數據庫。
5. Terracotta 實現 Session 複製
原理
Terracotta 的基本原理是對於集羣間共享的數據,當在一個節點發生變化的時候,Terracotta 只把變化的部分發送給 Terracotta 服務器,而後由服務器把它轉發給真正須要這個數據的節點。它是服務器 Session 複製的優化。
.
優勢
這樣對網絡的壓力就很是小,各個節點也沒必要浪費 CPU 時間和內存進行大量的序列化操做。把這種集羣間數據共享的機制應用在 Session 同步上,既避免了對數據庫的依賴,又能達到負載均衡和災難恢復的效果。
分庫與分錶帶來的分佈式困境與應對之策
.
1. 事務問題
使用分佈式事務。
2. 查詢問題
使用匯總表。
3. ID 惟一性
使用全局惟一 ID:GUID;
爲每一個分片指定一個 ID 範圍。