全球数据一致性,事务,微服务和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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/357340.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端导入导出文件

一、导出 1、返回值是url:创建个a标签即可 printRecord(params).then((response) > {if (response.success) {let a document.createElement(a);//创建a标签//从新页面打开,下载的话不需要这个,打开一个图片需要从新页面打开a.setAttrib…

选取文档元素的API

除了现在常用的选取API 1 document.getElementById() 2 document.getElementsByName() 3 document.getElementsByTagName() 4 ... 新增的API 主要是  document.querySelector(div>ul)  document.querySelectorAll(div>ul>li) 这两个API的强大之处在于能像CSS选…

vue进阶

1、vue-cli 使用 在开发中,需要打包的东西不止是js、css、html。还有更多的东西要处理,这些插件和加载器如果我们一一去添加就会比较麻烦,vue官方提供了一个快速搭建vue项目的脚手架,使用它能快速的构建一个web工程模板。 官网&…

每天一个linux命令(1):ln 命令

每天一个linux命令(35):ln 命令 ln 是linux中又一个非常重要命令,它的功能是为某一个文件在另外一个位置建立一个同步的链接.当我们需要在不同的目录,用到相同的文件时,我们不需要在 每一个需要的目录下都放…

ehcache 程序_将Ehcache添加到Openxava应用程序

ehcache 程序介绍 本文介绍如何在Openxava应用程序上快速启用Ehcache,从而提高性能。 查看实体及其图形时,将加载关系。 添加第二级缓存可加快关联元素的检索速度,因为已加载的元素是从缓存而不是数据库中检索的。 最终,该页面解…

Diff 算法核心原理

什么是虚拟DOM 讲Diff算法前,我先给大家讲一讲什么是虚拟DOM吧。这有利于后面大家对Diff算法的理解加深。 虚拟DOM是一个对象,一个什么样的对象呢?一个用来表示真实DOM的对象,要记住这句话。我举个例子,请看以下真实…

比较Java 8中的命令式和功能性算法

Mario Fusco的流行推文令人印象深刻,显示了类似算法的命令性和功能性方法之间的主要区别实际上是: 势在必行–功能分离pic.twitter.com/G2cC6iBkDJ — Mario Fusco(mariofusco) 2015年3月1日 两种算法都做同样的事情&#xff0…

mvc的视图中显示DataTable的方法

mvc的视图中显示DataTable的方法&#xff1a; 不断的循环画出table {ViewBag.Title "ShowDataTable"; } using System.Data; model Models.ConModel{var table Model.ExcelTable as DataTable; }<script src"~/Scripts/My97DatePicker/WdatePicker.js"…

rem,em,px,rpx等

1、任意浏览器的默认字体高都是16px。谷歌浏览器显示的最小字体大小是12px。 exp&#xff1a;突破谷歌浏览器显示12px限制。 &#xff08;1&#xff09;、<div>文本</div> 文本嵌套块标签&#xff0c;这是因为缩放只对有宽高的标签有效&#xff0c;缩放的时候也是…

python 列表 字典 读写文件:pickle模块的基本使用

建议大家使用cPickle&#xff0c;速度更快&#xff01;&#xff01;&#xff01; python数据持久存储&#xff1a;pickle模块的基本使用&#xff08;转载&#xff09; 作者: pzxbc出处: http://pzxbc.cnblogs.com/本文版权归作者和博客园共有&#xff0c;欢迎转载&#xff0c;但…

first-child、first-of-type等属性的差别

1、xxx:first-child 伪类 xxx作为第一个子元素 //p元素作为第一个子元素的都会变色 p:first-child { background:yellow; } //p作为父元素的 第一个子元素。不要求第一个元素的类型 p>:first-child {background:yellow; }//p作为父元素的 第一个子元素。要求第一个元素的类…

js 手机端触发事事件、javascript手机端/移动端触发事件

处理Touch事件能让你跟踪用户的每一根手指的位置。你可以绑定以下四种Touch事件: touchstart: // 手指放到屏幕上的时候触发 touchmove: // 手指在屏幕上移动的时候触发 touchend: // 手指从屏幕上拿起的时候触发 touchcancel: // 系统取消touch事件的时候触发。至于系统…

java 8 lambda_Java 8的烹调方式– Lambda项目

java 8 lambda什么是project lambda &#xff1a;Project lambda是用于以Java语言语法启用lambda表达式的项目。 Lambda表达式是功能编程语言&#xff08;如lisp&#xff09;中的主要语法。 Groovy将是支持lambda表达式&#xff08;也称为闭包&#xff09;的java的最接近亲戚。…

我的Serverless实战——引领云计算的下一个十年

前言&#xff1a;如今&#xff0c;越来越多的大厂企业开始大规模使用Serverless&#xff0c;处于变革中的开发者&#xff0c;大多已从观望状态转向尝试阶段&#xff0c;越来越多Serverless落地场景被解锁。作为基础研发底座&#xff0c;越来越多企业开始接受Serverless&#xf…

各种边距clientWidth、offsetWidth、scrollWidth、clientLeft、getBoundingClientRect详解

1、clientWidth、offsetWidth、scrollWidth <!DOCTYPE html> <html><head><meta charset"utf-8" /><style>#box1 {padding: 50px;position: static;}#box {border: 1px solid red;overflow: scroll;height: 200px;width: 500px;}#con…

encodeURIComponent的使用

URL 元字符&#xff1a;分号&#xff08;;&#xff09;&#xff0c;逗号&#xff08;,&#xff09;&#xff0c;斜杠&#xff08;/&#xff09;&#xff0c;问号&#xff08;?&#xff09;&#xff0c;冒号&#xff08;:&#xff09;&#xff0c;at&#xff08;&#xff09;&a…

如何以及何时使用枚举和注释

本文是我们名为“ 高级Java ”的学院课程的一部分。 本课程旨在帮助您最有效地使用Java。 它讨论了高级主题&#xff0c;包括对象创建&#xff0c;并发&#xff0c;序列化&#xff0c;反射等。 它将指导您完成Java掌握的过程&#xff01; 在这里查看 &#xff01; 目录 1.简…

花了两天时间用html+css+js做了一个网页版坦克大战游戏

大家好&#xff0c;我是孙叫兽&#xff0c;本期内容给大家分享如何用htmlcssjavaScript去做一个简易网页版坦克游戏。 目录 坦克游戏玩法及介绍 项目结构 源码地址&#xff1a; 坦克游戏玩法及介绍 我们先来看一下首页。 打开这个首页很简单&#xff0c;基本是上面这个样子&…

软件工程第一次冲刺进度条(1-10天)

第一天&#xff08;4.20&#xff09;昨天做了什么今天做了什么遇到的问题配置安装所需要的环境和相关软件 查询android关于界面编程与视图的相关资料并且初步编写代码 配置虚拟机的时候电脑上总是失败第二天&#xff08;4.21&#xff09;昨天做了什么今天做了什么遇到的问题查询…

css基础过渡与动画与应用于vue、react

一、css属性过渡transition 1、解释&#xff1a; 使用该属性后变化不会在一瞬间完成&#xff0c;会有一个连续的变化效果。第一个参数设置哪些属性变化时需要有连续的效果。 不论用什么方式使属性的值发生变化&#xff0c;transition都会生效。 2、语法&#xff1a; trans…