浅谈,seata在使用feign-url通过域名调用时分布式事务不生效的问题及解决
浅谈,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 | 生效 |
浅谈,seata在使用feign-url通过域名调用时分布式事务不生效的问题及解决的更多相关文章
- 浅谈WebService开发二(同步与异步调用)转
上文 <http://www.dotnetgeek.cn/xuexiwebservice1.html>已经跟大家说了,如果创建一个webservice和简单的调用,本文将注重webserv ...
- 浅谈Js对象的概念、创建、调用、删除、修改!
一.我们经常困惑,对象究竟是什么,其实这是一种思维,一种意识上的东西,就像我们都说 世界是有物质组成的道理一样,理解了下面的几句话!对象也不是那么抽象! 1.javascript中的所有事 ...
- 浅谈WebService开发三(动态调用WebService)转
在前两讲里,我已经向大家演示了如何使用WebService.同步, 异步调用WebService,而在实际开发过程中,可能会有多个WebService接口供你选择,而在程序执行过程中才决定使用哪一个 ...
- URL跳转与webview安全浅谈
URL跳转与webview安全浅谈 我博客的两篇文章拼接在一起所以可能看起来有些乱 起因 在一次测试中我用burpsuite搜索了关键词url找到了某处url我测试了一下发现waf拦截了指向外域的请求 ...
- 浅谈angular2+ionic2
浅谈angular2+ionic2 前言: 不要用angular的语法去写angular2,有人说二者就像Java和JavaScript的区别. 1. 项目所用:angular2+ionic2 ...
- 浅谈Hybrid技术的设计与实现第三弹——落地篇
前言 接上文:(阅读本文前,建议阅读前两篇文章先) 浅谈Hybrid技术的设计与实现 浅谈Hybrid技术的设计与实现第二弹 根据之前的介绍,大家对前端与Native的交互应该有一些简单的认识了,很多 ...
- 浅谈Hybrid技术的设计与实现第二弹
前言 浅谈Hybrid技术的设计与实现 浅谈Hybrid技术的设计与实现第二弹 浅谈Hybrid技术的设计与实现第三弹——落地篇 接上文:浅谈Hybrid技术的设计与实现(阅读本文前,建议阅读这个先) ...
- 浅谈Hybrid技术的设计与实现
前言 浅谈Hybrid技术的设计与实现 浅谈Hybrid技术的设计与实现第二弹 浅谈Hybrid技术的设计与实现第三弹——落地篇 随着移动浪潮的兴起,各种APP层出不穷,极速的业务扩展提升了团队对开发 ...
- 浅谈Vue.js
作为一名Vue.js的忠实用户,我想有必要写点文章来歌颂这一门美好的语言了,我给它的总体评价是“简单却不失优雅,小巧而不乏大匠”,下面将围绕这句话给大家介绍Vue.js,希望能够激发你对Vue.js的 ...
随机推荐
- CPF 入门教程 - 设计器和模板库的使用(五)
CPF netcore跨平台UI框架 系列教程 CPF 入门教程(一) CPF 入门教程 - 数据绑定和命令绑定(二) CPF 入门教程 - 样式和动画(三) CPF 入门教程 - 绘图(四) CPF ...
- Oralce注入 bypass waf出数据
发存货: 探测banner 版本号: ' and (SELECT banner FROM v$version where rownum=1) like 'O%' and '1'like'1 rownu ...
- 4.Linux命令基本格式与简单使用
Linux命令名组成: 在Linux/Unix系统下输入命令,就会进行相应的操作,那么这个命令有如下组成: 命令名 [选项] [参数] 注:[]的内容代表可选 命令实例: ls #显示当前文件夹下的所 ...
- UVA10943简单递推
题意: 给你两个数字n,k,意思是用k个不大于n的数字组合(相加和)为n一共有多少种方法? 思路: 比较简单的递推题目,d[i][j]表示用了i个数字的和为j一共有多少种情况,则 ...
- Python 爬虫 BeautifulSoup4 库的使用
BeautifulSoup4库 和 lxml 一样,Beautiful Soup 也是一个HTML/XML的解析器,主要的功能也是如何解析和提取 HTML/XML 数据.lxml 只会局部遍历,而Be ...
- 【maven】maven创建项目问题
这个问题困扰了很多,几个月在做大数据课设的时候,本想创建maven项目,但是创建项目失败了.这次又碰到maven创建项目失败,终于解决了.下面按碰到问题的时间来描述,所以需要从头认真看 前提须知 Ma ...
- C# 泛型Generic
泛型(Generic),是将不确定的类型预先定义下来的一种C#高级语法,我们在使用一个类,接口或者方法前,不知道用户将来传什么类型,或者我们写的类,接口或方法相同的代码可以服务不同的类型,就可以定义为 ...
- Pytorch系列:(五)CNN
卷积 Conv2d 2D卷积函数和参数如下 nn.Conv2d( in_channels, out_channels, kernel_size, stride=1, padding=0, dilati ...
- chardet模块
import chardet chardet.detect(f.read())检测哪种编码
- 技能Get·Windows10将任何格式文件固定到开始屏幕
阅文时长 | 0.6分钟 字数统计 | 960.8字符 主要内容 | 1.引言&背景 2.将文件加入到应用列表中 3.修改注册表法 4.声明与参考资料 『技能Get·Windows10将任何格 ...