2018阿里雲所有產品優惠券(好東東,強烈推薦)
領取地址:https://promotion.aliyun.com/ntms/act/ambassador/sharetouser.html?userCode=gh9qh5ki&utm_source=gh9qh5kicss
講座html
本話題已收入視頻講座《Spring Cloud分佈式事務解決方案》你們不妨圍觀下java
開源項目git
咱們利用消息隊列實現了分佈式事務的最終一致性解決方案,請你們圍觀。能夠參考Github CoolMQ源碼,項目支持網站: http://rabbitmq.org.cn,最新文章或實現會更新在上面github
阿里2017雲棲大會《破解世界性技術難題!GTS讓分佈式事務簡單高效》中,阿里聲稱提出了一種破解世界性難題之分佈式事務的終極解決方案,不管是可靠性、仍是處理速率都領先於市面上全部的技術。但使人遺憾的是一來項目未開源,二來還必須依賴阿里雲的分佈式數據庫。畢竟,吃飯的傢伙可不能輕易示人嘛。算法
雖然如此,但《世界難題...》一文中對事務仍是概括的仍是蠻到位的:「一個看似簡單的功能,內部可能須要調用多個「服務」並操做多個數據庫或分片來實現,單一技術手段和解決方案已沒法知足這些複雜應用場景。所以,分佈式系統架構中分佈式事務是一個繞不過去的挑戰。數據庫
什麼是分佈式事務?簡單的說,就是一次大操做由不一樣小操做組成,這些小操做分佈在不一樣服務器上,分佈式事務須要保證這些小操做要麼所有成功,要麼所有失敗。」segmentfault
舉個栗子:緩存
你上Taobao買東西,須要先扣錢,而後商品庫存-1吧。但扣款和庫存分別屬於兩個服務,這兩個服務中間要通過網絡、網關、主機等一系列中間層,萬一任何一個地方出了問題,好比網絡抖動、突發異常等待,都會致使不一致,好比扣款成功了,可是庫存沒-1,就會出現超賣的現象,而這就是分佈式事務須要解決的問題服務器
2階段提交是分佈式事務傳統解決方案,先進爲止還普遍存在。當一個事務跨越多個節點時,爲了保持事務ACID
特性,須要引入一個做爲協調者來統一掌控全部節點(稱做參與者)的操做結果並最終指示這些節點是否要把操做結果進行真正的提交(好比將更新後的數據寫入磁盤等等)。所以,二階段提交的算法思路能夠歸納爲:參與者將操做成敗通知協調者,再由協調者根據全部參與者的反饋情報決定各參與者是否要提交操做仍是停止操做。
以開會爲例
甲乙丙丁四人要組織一個會議,須要肯定會議時間,不妨設甲是協調者,乙丙丁是參與者。
投票階段
提交階段
不只要鎖住參與者的全部資源,並且要鎖住協調者資源,開銷大。一句話總結就是:2PC效率很低,對高併發很不友好。
引用《世界性難題...》
一文原話 "國外具備幾十年歷史和技術沉澱的基於XA模型的商用分佈式事務產品,在相同軟硬件條件下,開啓分佈式事務後吞吐常常有數量級的降低。"
此外還有三階段提交
你們有興趣的不妨研究下
所謂柔性事務是相對強制鎖表的剛性事務而言。流程入下:服務器A的事務若是執行順利,那麼事務A就先行提交,若是事務B也執行順利,則事務B也提交,整個事務就算完成。可是若是事務B執行失敗,事務B自己回滾,這時事務A已經被提交,因此須要執行一個補償操做,將已經提交的事務A執行的操做做反操做,恢復到未執行前事務A的狀態。
缺點是業務侵入性太強,還要補償操做,缺少廣泛性,無法大規模推廣。
目前基於消息隊列的解決方案有阿里的RocketMQ
,它實現了半消息
的解決方案,有點相似於Paxos算法,具體流程以下
[關於分佈式事務,工程領域主要討論的是強一致性和最終一致性的解決方案。典型方案包括: 兩階段提交(2PC, Two-phase Commit)方案 eBay 事件隊列方案 TCC 補償模式 緩存
第一階段:上游應用執行業務併發送MQ消息
可靠消息系統修改消息狀態爲發送狀態並將消息投遞到 MQ 中間件
第二階段:下游應用監聽 MQ 消息並執行業務
下游應用監聽 MQ 消息並執行業務,而且將消息的消費結果通知可靠消息服務。
RocketMQ
貌似是一種先進的實現方案了,但問題是缺少文檔
,不管是在Apache項目主頁,仍是在阿里的頁面上,最多隻告訴你如何用,而原理性或者指導性的東西很是缺少。
固然,若是你在阿里雲上專門購買了RocketMQ
服務,想必是另當別論了。但若是你試圖在本身的服務環境中部署和使用,想必要歷經至關大的學習曲線。畢竟是人家吃飯的傢伙嘛
RabbitMQ
遵循了AMQP規範
,用消息確認機制來保證:只要消息發送,就能確保被消費者消費來作到了消息最終一致性。並且開源,文檔還異常豐富,貌似是實現分佈式事務的良好載體
rabbitmq的整個發送過程以下
rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> { if (!ack) { //try to resend msg } else { //delete msg in db } });
final Consumer consumer = new DefaultConsumer(channel) { @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { String message = new String(body, "UTF-8"); System.out.println(" [x] Received '" + message + "'"); try { doWork(message); } finally { //確認收到消息 channel.basicAck(envelope.getDeliveryTag(), false); } } };
咱們來看看可能發送異常的四種
網絡斷了,拋出異常,業務直接回滾便可。若是出現connection closed
錯誤,直接增長 connection
數便可
connectionFactory.setChannelCacheSize(100);
rabbitmq
提供了確認ack機制,能夠用來確認消息是否有返回。所以咱們能夠在發送前在db中(內存或關係型數據庫)先存一下消息,若是ack異常則進行重發
/**confirmcallback用來確認消息是否有送達消息隊列*/ rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> { if (!ack) { //try to resend msg } else { //delete msg in db } }); /**若消息找不到對應的Exchange會先觸發returncallback */ rabbitTemplate.setReturnCallback((message, replyCode, replyText, tmpExchange, tmpRoutingKey) -> { try { Thread.sleep(Constants.ONE_SECOND); } catch (InterruptedException e) { e.printStackTrace(); } log.info("send message failed: " + replyCode + " " + replyText); rabbitTemplate.send(message); });
若是設置了消息持久化,那麼ack= true
是在消息持久化完成後,就是存到硬盤上以後再發送的,確保消息已經存在硬盤上,萬一消息服務掛了,消息服務恢復是可以再重發消息
消息服務收到消息後,消息會處於"UNACK"的狀態,直到客戶端確認消息
channel.basicQos(1); // accept only one unack-ed message at a time (see below) final Consumer consumer = new DefaultConsumer(channel) { @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { String message = new String(body, "UTF-8"); System.out.println(" [x] Received '" + message + "'"); try { doWork(message); } finally { //確認收到消息 channel.basicAck(envelope.getDeliveryTag(), false); } } }; boolean autoAck = false; channel.basicConsume(TASK_QUEUE_NAME, autoAck, consumer);
消息返回時假設確認消息丟失了,那麼消息服務會重發消息。注意,若是你設置了autoAck= false
,但又沒應答channel.baskAck
也沒有應答channel.baskNack
,那麼會致使很是嚴重的錯誤:消息隊列會被堵塞住,因此,不管如何都必須應答
消息監聽接受消息並處理,假設拋異常了,第一階段事物已經完成,若是要配置回滾則過於麻煩,即便作事務補償也可能事務補償失效的狀況,因此這裏能夠作一個重複執行,好比guava
的retry
,設置一個指數時間來循環執行,若是n次後依然失敗,發郵件、短信,用人肉來兜底。
《世界性難題...》
一文中對分佈式事務的幾種實現方式進行了形象概括
你天天上班,要通過一條10千米的只有兩條車道的馬路到達公司。這條路很堵,常常須要兩三個小時,上班時間沒有保證,這是2PC的問題-慢。
選擇一條很繞,長30千米但不多堵車的路,這是選b。上班時間有保證,可是必須早起,付出足夠的時間和汽油。這是柔性事務的問題,必須用具體業務來回滾,很難模塊化
選擇一條有點繞,長20千米的山路,路不平,只有suv能夠走,這是事務消息最終一致性問題。引入了新的消息中間件,須要額外的開發成本。但我司開發的CoolMQ已經對組件進行了封裝,只須要發送,接受,就能知足事務的要求。目前還有該方案的專題講座,你們能夠根據本身的須要選用。
最後是GTS
,GTS
修了一條擁有4條車道的高架橋,沒有繞路,仍是10千米。不堵車,對事務來講是高性能;不繞路,對事務來講是簡單易用,對業務無侵入,不用爲事務而重構;沒有車型限制,對事務來講是沒有功能限制,提供強一致事務。在沒有高架橋的時代,高架橋出現對交通來講就是一個顛覆性創新,不少之前看來無解的問題就迎刃而解了,一樣的,GTS但願經過創新改變數據一致性處理的行業現狀。但遺憾的是並未開源
,並且須要結合阿里雲服務
來使用。