javascript
全球数据一致性,事务,微服务和Spring Boot / Tomcat / Jetty
我們通常會構建需要一起執行以下幾項操作的應用程序:調用后端(微)服務,寫入數據庫,發送JMS消息等。但是,如果在調用其中之一時出錯,會發生什么情況?遠程資源,例如,在調用Web服務后,如果數據庫插入失敗? 如果遠程服務調用寫入數據,則由于服務已提交其數據,但尚未提交對數據庫的調用,您可能會陷入全局不一致的狀態。 在這種情況下,您將需要補償錯誤,通常,補償的管理是復雜且需要手寫的。
紅帽公司的Arun Gupta在
DZone微服務Refcard入門 。 實際上,這些模式中的大多數都顯示了調用多個其他微服務的微服務。 在所有這些情況下,全局數據一致性變得至關重要,即確保補償對微服務的后者調用中的一個失敗,或者重新嘗試執行該調用,直到所有微服務中的所有數據再次保持一致為止。 在其他有關微服務的文章中,通常很少或根本沒有提到跨遠程邊界的數據一致性,例如,標題為“微服務不是免費的午餐 ”的好文章, 當事情必須發生時 ,作者只是用陳述來觸及問題。 ……在交易上……事情變得復雜,我們需要管理……分布式交易以將各種行動聯系在一起 ”。 確實,我們這樣做了,但是在此類文章中卻從未提及如何做到這一點。
在分布式環境中管理一致性的傳統方法是利用分布式事務。 部署了事務管理器來監督全局系統是否保持一致。 已經開發了諸如兩階段提交的協議來標準化過程。 JTA,JDBC和JMS是使應用程序開發人員能夠保持多個數據庫和消息服務器一致的規范。 JCA是允許開發人員圍繞企業信息系統(EIS)編寫包裝的規范。 在最近的一篇文章中,我寫了關于如何構建通用JCA連接器的信息,該連接器使您可以將對微服務的調用等綁定到這些全局分布式事務中,從而使您不必編寫自己的框架代碼來處理期間的故障。分布式交易。 連接器負責確保您的數據最終保持一致 。
但是,您將無法始終訪問支持JCA的完整Java EE應用服務器,尤其是在微服務環境中。因此,我現在擴展了該庫,使其在以下環境中包括對提交/回滾/恢復的自動處理:
- Spring靴
- Spring+ Tomcat /碼頭
- Servlet + Tomcat /碼頭
- Spring批
- 獨立的Java應用程序
為了做到這一點,應用程序需要使用與JTA兼容的事務管理器,即Atomikos或Bitronix之一 。
以下描述基于您已經閱讀了較早的博客文章這一事實。
設置遠程調用以使其參與事務的過程類似于使用先前博客文章中提供的JCA適配器時的過程。 有兩個步驟:1)在傳遞給從BasicTransactionAssistanceFactory類檢索的TransactionAssistant對象的回調中調用遠程服務,以及2)設置中央提交/回滾處理程序。
第一步,即屬于執行階段的代碼(請參見前面的博客文章),如下所示(使用Spring時):
@Service @Transactional public class SomeService {@Autowired @Qualifier("xa/bookingService")BasicTransactionAssistanceFactory bookingServiceFactory;public String doSomethingInAGlobalTransactionWithARemoteService(String username) throws Exception {//write to say a local database...//call a remote serviceString msResponse = null;try(TransactionAssistant transactionAssistant = bookingServiceFactory.getTransactionAssistant()){msResponse = transactionAssistant.executeInActiveTransaction(txid->{BookingSystem service = new BookingSystemWebServiceService().getBookingSystemPort();return service.reserveTickets(txid, username);});}return msResponse;} }清單1:在事務內調用Web服務
第5-6行提供了第13行用于獲取TransactionAssistant的工廠實例。 注意,您必須確保此處使用的名稱與下面清單3中的安裝過程中使用的名稱相同。 這是因為當事務被提交或回滾時,事務管理器需要找到用于提交或補償第16行進行的調用的相關回調。很有可能在應用程序中將有多個這樣的遠程調用,對于集成的每個遠程服務,必須編寫清單1中所示的代碼。請注意,此代碼與使用JDBC調用數據庫沒有什么不同。 對于登記到事務中的每個數據庫,您需要:
- 注入數據源(類似于第5-6行)
- 從數據源獲得連接(第13行)
- 創建一條語句(第14行)
- 執行語句(第15-16行)
- 關閉連接(第13行,當try塊調用自動關閉資源的close方法時)。 在使用完交易助手之后,在交易完成之前關閉它非常重要 。
為了創建BasicTransactionAssistanceFactory的實例(清單1中的第5-6行),我們使用Spring @Configuration :
@Configuration public class Config {@Bean(name="xa/bookingService")public BasicTransactionAssistanceFactory bookingSystemFactory() throws NamingException {Context ctx = new BitronixContext();BasicTransactionAssistanceFactory microserviceFactory = (BasicTransactionAssistanceFactory) ctx.lookup("xa/bookingService");return microserviceFactory;} ...清單2:Spring的
清單2的第4行使用的名稱與清單1的第5行的@Qualifier相同。清單2的第5行的方法通過在JNDI中查找工廠來創建工廠,在本示例中使用Bitronix。 使用Atomikos時,代碼看起來略有不同-有關詳細信息,請參見demo/genericconnector-demo-springboot-atomikos項目。
上面提到的第二步是設置提交/回滾回調。 當提交或回滾清單1第8-20行的事務時,事務管理器將使用此方法。 請注意,由于清單1第2行上有@Transactional批注,因此存在事務。清單3中顯示了此設置:
CommitRollbackCallback bookingCommitRollbackCallback = new CommitRollbackCallback() {private static final long serialVersionUID = 1L;@Overridepublic void rollback(String txid) throws Exception {new BookingSystemWebServiceService().getBookingSystemPort().cancelTickets(txid);}@Overridepublic void commit(String txid) throws Exception {new BookingSystemWebServiceService().getBookingSystemPort().bookTickets(txid);} }; TransactionConfigurator.setup("xa/bookingService", bookingCommitRollbackCallback);清單3:設置一個提交/回滾處理程序
第12行將回調與配置清單1和2中使用的唯一名稱一起傳遞給配置器。
如果要集成的服務僅提供執行方法和該執行的補償方法,則第9行的提交很可能為空。 此提交回調來自兩個階段的提交,其目的是將分布式系統不一致的時間保持在絕對最小限度。 請參閱本文末尾的討論。
第5和9行實例化了一個新的Web服務客戶端。 注意,回調處理程序應該是無狀態的 ! 它是可序列化的,因為在某些平臺(例如Atomikos)上,它將與事務信息一起序列化,以便在必要時可以在恢復期間進行調用。 我想只要它可以序列化就可以使它有狀態,但是我建議使其保持無狀態。
在此示例中,傳遞給第4行和第8行的回調的事務ID(名為txid的字符串)被傳遞給Web服務。 在一個更實際的示例中,您將使用該ID查找在執行階段保存的上下文信息(請參見清單1的第15和16行)。 然后,您將使用該上下文信息,例如來自對Web服務的較早調用的參考號,來進行提交或回滾清單1中的Web服務調用的調用。
這些清單的獨立變體(例如在Spring環境之外使用此庫)幾乎相同,不同之處在于您需要手動管理事務。 有關某些受支持環境中的代碼示例,請參見Github上的demo文件夾。
請注意,在通用連接器的JCA版本中,您可以配置通用連接器是否在內部處理恢復。 如果沒有,則必須提供事務管理器可以調用的回調,以查找您認為尚未完成的事務。 在本文討論的非JCA實現中,這始終由通用連接器在內部進行處理。 通用連接器會將上下文信息寫入目錄,并在恢復期間使用該信息來告訴事務管理器需要清除哪些內容。 嚴格來說,這不是很正確,因為如果您的硬盤發生故障,所有有關不完整事務的信息都將丟失。 在嚴格的兩階段提交中,這就是為什么允許事務管理器調用資源以獲取需要恢復的不完整事務的列表的原因。 在當今的RAID控制器世界中,沒有理由使生產機器由于硬盤故障而丟失數據,因此,目前沒有選擇提供對通用連接器的回調,該回調可以告訴它正在進行的事務。需要恢復的狀態。 如果節點發生災難性的硬件故障(無法啟動該節點并再次運行),則需要將通用連接器寫入的所有文件從舊硬盤上物理復制到第二個硬盤上。節點。 然后,在第二個節點上運行的事務管理器和通用連接器將通過提交或回滾這些崩潰的事務中的任何一個來協調工作,以完成所有掛起的事務。 此過程與災難恢復期間復制事務管理器日志沒有什么不同,具體取決于您使用的是哪個事務管理器。 您執行此操作的機會非常小-在我的職業生涯中,我從未聽說過我所從事的項目/產品中的生產機器以這種方式失敗。
您可以使用清單4所示的第二個參數來配置此上下文信息的寫入位置:
MicroserviceXAResource.configure(30000L, new File("."));顯示的值也是默認值。
清單4設置了與恢復相關的最小事務壽命。 在這種情況下,只有在30秒鐘以上時,該事務才被認為與通過恢復進行清理相關。 您可能需要根據執行業務流程所需的時間來調整此值,并且可能取決于為您調用的每個后端服務配置的超時時間之和。 低值和高值之間需要權衡:值越低,失敗后恢復期間在事務管理器中運行的后臺任務清理所需的時間越少。 這意味著值越小,不一致窗口越小。 但是請注意,如果該值太低,恢復任務將嘗試回滾實際上仍處于活動狀態的事務。 通常,您可以配置事務管理器的超時期限,清單4中設置的值應大于等于事務管理器的超時期限。 此外,清單4中將存儲上下文數據的目錄配置為本地目錄。 您可以指定任何目錄,但是請確保該目錄存在,因為通用連接器不會嘗試創建該目錄。
如果在Tomcat環境中使用Bitronix,則可能會發現關于如何配置環境的信息不多。 在Bitronix從codehaus.org移至Github之前,它的記錄非常好。 我為Bitronix創建了一個問題來改進文檔。 demo/genericconnector-demo-tomcat-bitronix文件夾中的源代碼和自述文件包含提示和鏈接。
使用通用連接器要注意的最后一件事是提交和回滾的工作方式。 連接器所做的全部工作都是在JTA事務之上進行搭載,以便在某些情況需要回滾時,它通過回調獲得通知。 然后,通用連接器將此信息傳遞到清單3中注冊的回調中的代碼中。在后端中實際回滾數據并不是通用連接器所做的事情–它只是調用回調,以便您可以告訴后端系統回滾數據。 通常,您不會像這樣進行回滾,而是通常使用狀態來標記已寫入的數據不再有效。 正確回滾執行階段已寫入的所有數據痕跡可能非常困難。 在嚴格的兩階段提交協議設置中(例如,使用兩個數據庫),在執行和提交/回滾之間,寫入每個資源中的數據將保持鎖定狀態,第三方事務不可觸摸。 實際上,這是兩階段提交的缺點之一,因為鎖定資源會降低可伸縮性。 通常,您集成的后端系統不會在執行階段和提交階段之間鎖定數據,并且確實,提交回調將保持為空,因為它無關緊要–數據通常在第16行時已在后端中提交清單1中的代碼在執行階段返回。 但是,如果您想構建一個更嚴格的系統,并且可以影響要集成的后端的實現,那么通??梢酝ㄟ^使用狀態將后端系統中的數據“鎖定”在執行和提交階段之間,例如,執行后為“預訂機票”,提交后為“預訂機票”。 第三方交易將不允許在“保留”狀態下訪問資源/票證。
- 通用連接器和許多演示項目可從https://github.com/maxant/genericconnector/獲得 ,二進制文件和源代碼可從Maven獲得 。
翻譯自: https://www.javacodegeeks.com/2015/10/global-data-consistency-transactions-microservices-and-spring-boot-tomcat-jetty.html
總結
以上是生活随笔為你收集整理的全球数据一致性,事务,微服务和Spring Boot / Tomcat / Jetty的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: spring作业_Spring和石英:多
- 下一篇: Spring综合课程总结