『壹』 java的框架spring如何配置分布式事務
分布式事務本身不是程序做的,我們不需要在代碼中明確地做這些事,因為是不是分布式對於代碼來說,代碼寫起來完全相同。
只是選擇支持 JTA XA (也叫 2-Phase Commit, 2PC) 的數據源就可以了,你默認使用的 DataSource 可能不是 XA ( Weblogic 把它叫 TX)。
一般在網站編程時多數人可能是用 Spring 搭配 tomcat commons-dbcp 那個數據源,那個可能就不是支持 XA 的數據源,如果你打算在復雜企業應用生態系統中使用J2EE 就不要用 Spring 提供 commonbs-dbcp 那種小作坊式的做法,因為它是假設自己的程序就是獨立生態系統,當你需要與外界打交道時就碰到諸多問題,這也是為什麼很多大企業依然還是會使用 EJB 的原因(EJB 已經考慮到這點,並把它寫入到J2EE 標准中),我們推薦用伺服器自己的數據源,也就是 lookup JNDI,這樣的話,是不是 XA 事務就由伺服器的配置來定製,代碼就不需要任何配置來決定是不是 XA 了 ;事務本身是不是 XA (分布式的)是伺服器的事,伺服器來管理「資源」 (包括數據源,JMS 連接等,一個資源(JDBC連接)如何參與事務是「資源管理器」(驅動程序)的職責,跟程序無關),伺服器提供事務管理並作為「事務協調者」來處理多個「資源管理器」(不同的資料庫連接)之間的事務一致性,,而 Spring 的職責很簡單,對於我們希望 Spring 自動提交或回滾事務時,在配置中指定需要回滾的異常的類型。
不過我沒有實際使用過 Spring,我有多年的 EJB 經驗,這其中的原理是相同的,因為這是 J2EE 標准規范要求達到的。
『貳』 java 事務
Java中的事務處理
一般情況下,應用伺服器支持JDBC事務、JTA(Java Transaction API)事務、容器管理事務。一般情況下,最好不要在程序中同時使用上述三種事務類型,比如在JTA事務中嵌套JDBC事務。第二方面,事務要在盡可能短的時間內完成,不要在不同方法中實現事務的使用。下面我們列舉兩種事務處理方式。
1、JavaBean中使用JDBC方式進行事務處理
在JDBC中怎樣將多個SQL語句組合成一個事務呢?在JDBC中,打開一個連接對象Connection時,預設是auto-commit模式,每個SQL語句都被當作一個事務,即每次執行一個語句,都會自動的得到事務確認。為了能將多個SQL語句組合成一個事務,要將auto-commit模式屏蔽掉。在auto-commit模式屏蔽掉之後,如果不調用commit()方法,SQL語句不會得到事務確認。在最近一次commit()方法調用之後的所有SQL會在方法commit()調用時得到確認。
public int delete(int sID) {
dbc = new DataBaseConnection();
Connection con = dbc.getConnection();
try {
con.setAutoCommit(false);// 更改JDBC事務的默認提交方式
dbc.executeUpdate("delete from bylaw where ID=" + sID);
dbc.executeUpdate("delete from bylaw _content where ID=" + sID);
dbc.executeUpdate("delete from bylaw _affix where bylawid=" + sID);
con.commit();//提交JDBC事務
con.setAutoCommit(true);// 恢復JDBC事務的默認提交方式
dbc.close();
return 1;
}
catch (Exception exc) {
con.rollBack();//回滾JDBC事務
exc.printStackTrace();
dbc.close();
return -1;
}
}
2、SessionBean中的JTA事務
JTA 是事務服務的 J2EE 解決方案。本質上,它是描述事務介面(比如 UserTransaction 介面,開發人員直接使用該介面或者通過 J2EE 容器使用該介面來確保業務邏輯能夠可靠地運行)的 J2EE 模型的一部分。JTA 具有的三個主要的介面分別是 UserTransaction 介面、TransactionManager 介面和 Transaction 介面。這些介面共享公共的事務操作,例如 commit() 和 rollback(), 但是也包含特殊的事務操作,例如 suspend(),resume() 和 enlist(),它們只出現在特定的介面上,以便在實現中允許一定程度的訪問控制。例如,UserTransaction 能夠執行事務劃分和基本的事務操作,而 TransactionManager 能夠執行上下文管理。
應用程序可以調用UserTransaction.begin()方法開始一個事務,該事務與應用程序正在其中運行的當前線程相關聯。底層的事務管理器實際處理線程與事務之間的關聯。UserTransaction.commit()方法終止與當前線程關聯的事務。UserTransaction.rollback()方法將放棄與當前線程關聯的當前事務。
public int delete(int sID) {
DataBaseConnection dbc = null;
dbc = new DataBaseConnection();
dbc.getConnection();
UserTransaction transaction = sessionContext.getUserTransaction();//獲得JTA事務
try {
transaction.begin(); //開始JTA事務
dbc.executeUpdate("delete from bylaw where ID=" + sID);
dbc.executeUpdate("delete from bylaw _content where ID=" + sID);
dbc.executeUpdate("delete from bylaw _affix where bylawid=" + sID);
transaction.commit(); //提交JTA事務
dbc.close();
return 1;
}
catch (Exception exc) {
try {
transaction.rollback();//JTA事務回滾
}
catch (Exception ex) {
//JTA事務回滾出錯處理
ex.printStackTrace();
}
exc.printStackTrace();
dbc.close();
return -1;
}
}
『叄』 Java分布式系統處理分布式事務有哪些經典解決方
當我們在生產線上用一台伺服器來提供數據服務的時候,我會遇到如下的兩個問題:
1)一台伺服器的性能不足以提供足夠的能力服務於所有的網路請求。
2)我們總是害怕我們的這台伺服器停機,造成服務不可用或是數據丟失。
於是我們不得不對我們的伺服器進行擴展,加入更多的機器來分擔性能上的問題,以及來解決單點故障問題。 通常,我們會通過兩種手段來擴展我們的數據服務:
1)數據分區:就是把數據分塊放在不同的伺服器上(如:uid % 16,一致性哈希等)。
2)數據鏡像:讓所有的伺服器都有相同的數據,提供相當的服務。
對於第一種情況,我們無法解決數據丟失的問題,單台伺服器出問題時,會有部分數據丟失。所以,數據服務的高可用性只能通過第二種方法來完成——數據的冗餘存儲(一般工業界認為比較安全的備份數應該是3份,如:Hadoop和Dynamo)。 但是,加入更多的機器,會讓我們的數據服務變得很復雜,尤其是跨伺服器的事務處理,也就是跨伺服器的數據一致性。這個是一個很難的問題。 讓我們用最經典的Use Case:「A帳號向B帳號匯錢」來說明一下,熟悉RDBMS事務的都知道從帳號A到帳號B需要6個操作:
從A帳號中把余額讀出來。
對A帳號做減法操作。
把結果寫回A帳號中。
從B帳號中把余額讀出來。
對B帳號做加法操作。
把結果寫回B帳號中。
為了數據的一致性,這6件事,要麼都成功做完,要麼都不成功,而且這個操作的過程中,對A、B帳號的其它訪問必需鎖死,所謂鎖死就是要排除其它的讀寫操作,不然會有臟數據的問題,這就是事務。那麼,我們在加入了更多的機器後,這個事情會變得復雜起來:
1)在數據分區的方案中:如果A帳號和B帳號的數據不在同一台伺服器上怎麼辦?我們需要一個跨機器的事務處理。也就是說,如果A的扣錢成功了,但B的加錢不成功,我們還要把A的操作給回滾回去。這在跨機器的情況下,就變得比較復雜了。
2)在數據鏡像的方案中:A帳號和B帳號間的匯款是可以在一台機器上完成的,但是別忘了我們有多台機器存在A帳號和B帳號的副本。如果對A帳號的匯錢有兩個並發操作(要匯給B和C),這兩個操作發生在不同的兩台伺服器上怎麼辦?也就是說,在數據鏡像中,在不同的伺服器上對同一個數據的寫操作怎麼保證其一致性,保證數據不沖突?
同時,我們還要考慮性能的因素,如果不考慮性能的話,事務得到保證並不困難,系統慢一點就行了。除了考慮性能外,我們還要考慮可用性,也就是說,一台機器沒了,數據不丟失,服務可由別的機器繼續提供。 於是,我們需要重點考慮下面的這么幾個情況:
1)容災:數據不丟、節點的Failover
2)數據的一致性:事務處理
3)性能:吞吐量 、 響應時間
前面說過,要解決數據不丟,只能通過數據冗餘的方法,就算是數據分區,每個區也需要進行數據冗餘處理。這就是數據副本:當出現某個節點的數據丟失時可以從副本讀到,數據副本是分布式系統解決數據丟失異常的唯一手段。所以,在這篇文章中,簡單起見,我們只討論在數據冗餘情況下考慮數據的一致性和性能的問題。簡單說來:
1)要想讓數據有高可用性,就得寫多份數據。
2)寫多份的問題會導致數據一致性的問題。
3)數據一致性的問題又會引發性能問題
這就是軟體開發,按下了葫蘆起了瓢。
一致性模型
說起數據一致性來說,簡單說有三種類型(當然,如果細分的話,還有很多一致性模型,如:順序一致性,FIFO一致性,會話一致性,單讀一致性,單寫一致性,但為了本文的簡單易讀,我只說下面三種):
1)Weak 弱一致性:當你寫入一個新值後,讀操作在數據副本上可能讀出來,也可能讀不出來。比如:某些cache系統,網路游戲其它玩家的數據和你沒什麼關系,VOIP這樣的系統,或是網路搜索引擎(呵呵)。
2)Eventually 最終一致性:當你寫入一個新值後,有可能讀不出來,但在某個時間窗口之後保證最終能讀出來。比如:DNS,電子郵件、Amazon S3,Google搜索引擎這樣的系統。
3)Strong 強一致性:新的數據一旦寫入,在任意副本任意時刻都能讀到新值。比如:文件系統,RDBMS,Azure Table都是強一致性的。
從這三種一致型的模型上來說,我們可以看到,Weak和Eventually一般來說是非同步冗餘的,而Strong一般來說是同步冗餘的,非同步的通常意味著更好的性能,但也意味著更復雜的狀態控制。同步意味著簡單,但也意味著性能下降。 好,讓我們由淺入深,一步一步地來看有哪些技術:
Master-Slave
首先是Master-Slave結構,對於這種加構,Slave一般是Master的備份。在這樣的系統中,一般是如下設計的:
1)讀寫請求都由Master負責。
2)寫請求寫到Master上後,由Master同步到Slave上。
從Master同步到Slave上,你可以使用非同步,也可以使用同步,可以使用Master來push,也可以使用Slave來pull。 通常來說是Slave來周期性的pull,所以,是最終一致性。這個設計的問題是,如果Master在pull周期內垮掉了,那麼會導致這個時間片內的數據丟失。如果你不想讓數據丟掉,Slave只能成為Read-Only的方式等Master恢復。
當然,如果你可以容忍數據丟掉的話,你可以馬上讓Slave代替Master工作(對於只負責計算的節點來說,沒有數據一致性和數據丟失的問題,Master-Slave的方式就可以解決單點問題了) 當然,Master Slave也可以是強一致性的, 比如:當我們寫Master的時候,Master負責先寫自己,等成功後,再寫Slave,兩者都成功後返回成功,整個過程是同步的,如果寫Slave失敗了,那麼兩種方法,一種是標記Slave不可用報錯並繼續服務(等Slave恢復後同步Master的數據,可以有多個Slave,這樣少一個,還有備份,就像前面說的寫三份那樣),另一種是回滾自己並返回寫失敗。(註:一般不先寫Slave,因為如果寫Master自己失敗後,還要回滾Slave,此時如果回滾Slave失敗,就得手工訂正數據了)你可以看到,如果Master-Slave需要做成強一致性有多復雜。
Master-Master
Master-Master,又叫Multi-master,是指一個系統存在兩個或多個Master,每個Master都提供read-write服務。這個模型是Master-Slave的加強版,數據間同步一般是通過Master間的非同步完成,所以是最終一致性。 Master-Master的好處是,一台Master掛了,別的Master可以正常做讀寫服務,他和Master-Slave一樣,當數據沒有被復制到別的Master上時,數據會丟失。很多資料庫都支持Master-Master的Replication的機制。
另外,如果多個Master對同一個數據進行修改的時候,這個模型的惡夢就出現了——對數據間的沖突合並,這並不是一件容易的事情。看看Dynamo的Vector Clock的設計(記錄數據的版本號和修改者)就知道這個事並不那麼簡單,而且Dynamo對數據沖突這個事是交給用戶自己搞的。就像我們的SVN源碼沖突一樣,對於同一行代碼的沖突,只能交給開發者自己來處理。(在本文後後面會討論一下Dynamo的Vector Clock)
Two/Three Phase Commit
這個協議的縮寫又叫2PC,中文叫兩階段提交。在分布式系統中,每個節點雖然可以知曉自己的操作時成功或者失敗,卻無法知道其他節點的操作的成功或失敗。當一個事務跨越多個節點時,為了保持事務的ACID特性,需要引入一個作為協調者的組件來統一掌控所有節點(稱作參與者)的操作結果並最終指示這些節點是否要把操作結果進行真正的提交(比如將更新後的數據寫入磁碟等等)。