浅谈,seata在使用feign-url通过域名调用时分布式事务不生效的问题及解决
阅读原文时间:2021年06月07日阅读:1

浅谈,seata在使用feign-url通过域名调用时分布式事务不生效的问题及解决

​ 在前几个月时,我们项目出现了分布式事务的问题,那么什么是分布式事务问题呢,简单的说,我们有俩服务A和B,它们对应的数据源分别是a_db和b_db,A服务收到请求在执行到某个操作时,需要调用B服务,在B服务里继续执行,B服务里面的执行牵扯到了对b_db的增删改操作,等B服务执行完后,A又继续执行,结果此刻,A发生异常了,由于B在另一个服务,有自己的数据源,它和A也不属于一个事务,导致a_db自己回滚了,b_db却没有回滚,这不就出问题了么,这也就是我们系统分布式事务问题的来源。

​ 后来呢,我们引入了springcloud中解决分布式事务的组件seata,关于分布式事务seata的介绍安装各个模式我就不详细的一一啰嗦的解释了,当然为了方便后面大家对问题发生和解决的更好理解,我还是会说一下这个seata解决分布式事务的流程。

关于seata解决分布式事务流程的介绍

seata分为三部分,TC事务协调者,TM事务管理器,RM资源管理器。其中TC是一个独立的seata-sever服务,用来协调整个分布式事务的,在git_hub上可以自行下载;TM是全局事务的发起者,管理整个全局事务,相当于项目中的调用者服务,在我们项目中就相当于那个A;RM资源管理器可以有多个,在项目中属于被调用者,在我们项目中相当于那个B。

它们的执行流程如下:

  • 1.TM向TC发起一个请求,说明自己要开启一个全局事务。
  • 2.TC收到了来自于TM的请求,生成了一个XID作为这个全局事务的唯一标识,返给了TM。
  • 3.TM开始去调用其他的RM,并将XID一并的传给了那些RM。
  • 4.RM会接收到XID,知道自己的事务属于这个全局事务,它会将自己的本地事务注册到TC作为这个XID下面的一个分支事务,并把自己的事务执行结果也告诉TC。
  • 5.各个微服务执行完之后,TC就知道这个XID下的各个分支事务的执行结果,当然TM也知道了。
  • 6.TM发现各个分支事务都成功了,就向TC发起请求进行提交,否则就向TC发起请求进行回滚。
  • 7.TC收到请求后,就向XID下的所有的分支事务发起相应的请求。
  • 8.各个微服务收到TC的请求后,执行相应的命令,并把执行结果上报给TC。

下面为了帮助大家更好的理解,放了一张图进行演示:

引入seata后的后遗症

​ 我们项目是个不太标准的微服务,分为服务A和B,但却没有注册中心,使用了springcloud的组件feign进行服务通信调用,但是没有注册中心不能通过服务名发现服务,只能使用了feign的url模式进行互通 ,当然这也是为问题的爆发埋下了种子。

​ seata其实使用的模式有很多,比如AT了,TCC了,XA了,还有Saga了,当然我们选用的是AT模式,这种是无侵入业务式的模式,官方也比较推荐。至于注册配置方式也有很多,有file 、nacos 、eureka、redis、zk、consul、etcd3、sofa等,最终由于我们项目没有注册中心的特殊性,我们就只好选用了file单机的注册配置方式。

​ 于是经过下载seata-server并启动,修改file.conf文件的seata-server地址,并将file.conf和registory.conf配置文件放入到resources下,修改数据源为代理数据源,在a_db和b_db添加undo_log数据库表,变A调用端的@Transactional为@GlobalTransactional,这些操作完成后,一个seata分布式事务处理框架的引入算是正式完成,于是在本地电脑上启动了A,B项目,进行模拟单元测试,分布式事务问题完美处理,于是代码上测试服务器,过了一段时间上了预生产服务器。

​ 那时候由于工作事多,只在本地模拟了下,服务器上也只能测试中遇到了再看,没再去关心这件事,毕竟本地已经完美契合了,结果一个月后上了预生产环境,某个业务代码由于数据原因发生了异常,那个方法还牵扯到了分布式事务问题,在观察数据库数据时,离奇的发现,分布式事务没生效,B服务没有回滚!!

seata问题的一步步剖析及解决

​ 问题发生了后,抓紧在本地模拟了下,发现在自己电脑上分布式事务仍然是可以生效的,代码都一样也没改过,为什么在预生产环境就不行了,真实见了鬼了。

​ 后来就在思考预生产环境和自己在本地上测试有什么区别么,本地测试时,两个服务之间的调用feign里面的url填写的是ip:port,直接就去访问了指定的服务,但是预生产环境中使用的域名,也就是说请求会根据域名通过nginx转发到对应服务,假设第一次请求的B1服务,第二次转发请求的是B2服务,不在一个服务上。有同事就提出了,会不会就和这个B服务有集群有关。但是还是感觉那里不对劲,因为听说我们的预生产环境B虽然做了集群,但是这几个B连接的数据源依旧是同一个数据源,不过看起来说的也好像很有道理的样子。

​ 后来啊,就打算在测试环境的服务器试一下,因为毕竟测试服务器没有什么集群,也是一个A和一个B,他们之间的调用是通过域名而并非ip:port,本以为会成功的,结果却很悲催,在B服务没有做集群的情况下,分布式事务仍然是不生效的,那看起来好像和集群不集群没啥关系啊,毕竟测试环境的服务就是一对一,每次也只会访问那一个B,仍然还不生效,到底发生了啥呢。

​ 一时间没有辙了,只有把那个feign的url统一改成了ip:port方式以解决燃眉之急,但是这样并不好,毕竟这意味着上了生产环境A就固定的去调用其中一个B了,那集群就没有任何意义了。

​ 大约过了一段时间,有同事说把nginx的负载均衡策略改成ip_hash方式就没问题了,但这种方式没有人去验证,我也没这个权限去改服务器的nginx.conf去验证,因为在测试环境1对1的情况下都没办法保证让分布式事务生效,那即使改成ip_hash又能如何呢?

​ 后来大佬推荐我看一篇文章,说问题可能出在nginx上,这是文章的网址:nginx做转发时,带'_'的header内容丢失…..大体上将的就是nginx对带下划线的头请求进行限制,会把它过滤到,这也就是造成nginx转发时带“_”的请求会丢失的情况的产生。后来感觉发现了新大陆,seata分布式事务的事务id的格式是TX_XID,那么在TM拿到XID去调用RM时,会不会发生XID的丢失,导致RM没有办法将自己的分支事务注册到那个XID所代表的全局事务的下面,因此也不会被管理呢,自然不生效了。所以只要将nginx的那个对下划线的请求过滤处理掉就好了。

​ 想到后,激动不已,想在服务器上模拟问题发生及解决,但是项目的nginx.conf岂容我这种小开发随意改动!没事,反正自己暂时无事,于是在自己电脑上写了几个服务demo,转发用的是我自己虚拟机里面的nginx,自己也整了个seata-server运行起来,把各项配置都配好,开始模拟,果然,服务器上的问题被我模拟出来了,接下来就是将nginx里面的对下划线的过滤去除,去除方式就是在nginx.conf的http部分添加一行: underscores_in_headers on;再次测试,成功,url写成域名也没有关系了。

以下是我的模拟情况记录表:

模拟方式

分布式事务是否生效

url通过ip:port,nginx未改动

生效

url通过填写域名,nginx未改动

不生效

url通过填写域名,nginx仅仅将负载均衡策略改为ip_hash

不生效

url通过填写域名,nginx仅仅加入underscores_in_headers on

生效

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章