pack问题汇总反馈 #563
Description
大家好!本人最近做分布式事务服务框架,刚开始想直接将pack拿过来使用,但我们发现pack的问题太多了。逼不得已将pack的核心代码基本全部重写,但保持业务逻辑与pack一致。受制于公司政策,代码不方便公开。现将我们遇到的问题进行反馈:
-
服务发现机制不健全,如果新加入alpha,客户端无法感知;
-
SQL代码复杂。这是由于数据库设计表不合理造成的。saga只有一个表,tcc有三个表,其实两张表最合理,一张表存事务的整体状态,一张表存事务的事件。这样可以使SQL写的很简单。
-
回滚逻辑复杂,回滚速度慢,如果出现要回滚1000条,立即卡死。建议可以引入状态机来描述分布式事务的状态,回滚逻辑就不需要那么复杂了,代码只需一百多行。下面是我们设计的SAGA状态机,TCC状态机会更复杂一些:
-
gRPC性能较低:gRPC同步模型性能很差,但受制于spring线程模型,只能采用同步模型。
rpc OnTxEvent (GrpcTxEvent) returns (GrpcAck) {}
我们是直接用netty手撸了定制的RPC框架,即使是在客户端阻塞的情况下,qps也比gRPC双工异步快。 -
mysql性能较慢:其实这种中间件对性能要求是很高的,一个RPC请求最好在5ms内返回,内网环境下网络需消耗2ms,请求处理要在3ms内返回。使用mysql在高负载下经常超时,目前我们的处理方式是异步批量入库,但这种情况下客户端需将事件发到固定的alpha,以防止事件顺序不对,同时要处理关闭server时清空入库队列;
-
jpa有些不好用:我们在做批量入库时,使用了saveAll方法,但会丢数据,查询时缓存也有影响,查这个事务的数据能出来一条别的事务的数据。我们团队没人会jpa,果断换成mybatis;
-
RPC次数较多:每个分支有两次请求,加上开启与提交,如果有n个分支,SAGA RPC次数是2n+2,TCC是3n+2。我们觉得实在太多了,可以砍掉一部分。我们假定大部分情况下分布式事务是成功的,如果成功率太低,这个接口没有意义。所以,对于分支事务开始会发RPC请求,但结束时不会,除非发生异常会进行RPC请求。这样一个事务正常情况下,SAGA变为n+2,TCC变为2n+2;
经过开发分布式事务,我觉得这玩意就是鸡肋,能不用就不要用!我还是更推荐AP+最终一致性。使用这种框架增加了整个提供的复杂度、耦合性,如果alpha宕机会导致整个系统不可用,而且接口都要求幂等,对普通开发人员来说很容易生产bug。
最后,感谢各位开发者贡献这么好的框架,使本人学会了怎么处理这类问题。非常感谢!
Activity
coolbeevip commentedon Sep 24, 2019
Pack 0.5.0 版本已经基于 Akka Actor 重构了 Saga 部分
https://github.com/apache/servicecomb-pack/blob/master/docs/fsm/fsm_manual_zh.md
WillemJiang commentedon Sep 24, 2019
很高兴能看到你的反馈,欢迎加入Pack的开发队伍,这样可以把你的优化经验向社区分享。
不知道你之前是基于那个版本的pack来重写的,Omega自动进行Alpha服务发现的机制我们是在0.3.0的时候就提供了, 不知道你提到的第一问题具体是如何解决的。
在0.5.0里面,我们引入了Actor来作为来简化SQL以及回滚逻辑,同时执行事件存储过程中也会采用批处理的方式来进一步提升系统的执行效率。
感谢你提供了有关性能优化的方法,我们再后续的版本中会参考这部分的思路进行优化。
WillemJiang commentedon Sep 24, 2019
@loveoobaby 可否将你的联系方式 发送到我的邮箱 willem.jiang AT gmail 中,希望能跟你有深入的探讨。
loveoobaby commentedon Sep 24, 2019
我们基于0.4,6月份做完的,没有关注最近代码
loveoobaby commentedon Sep 24, 2019
第一个问题我们的解决方式比较简单。我们起了一个定时任务,我们只用Eureka,从其DiscoveryClient缓存中可找到所有alpha服务,然后Omega保存也保存了所有alpha,两者进行对比,如果是新加的实例会创建新的连接。如果服务下线或网络异常,Omega会报错尝试重连,超过一定的时间剔除该服务。最终,Omega与alpha可以自由的启停。
coolbeevip commentedon Sep 24, 2019
无论如何,这个想法很独特(如果是让我选择只能发送一个消息,那我可能会选择只发送子事务的结束消息),但是子事务只发送一个消息会存在一些缺陷
关于 RPC 效率问题,0.5.0版本使用状态机,Alpha 收到 RPC 消息后不会操作数据库,所以吞吐率会提高很多。
loveoobaby commentedon Sep 24, 2019
第一个问题:其实我们假设子事务是串行执行的。
第二个问题:依靠补偿接口有处理无效补偿事件的能力。执行分支事务与RPC发送结束事件本来就无法保证原子性。我们遇到了这样的场景:分支事务执行完成,数据库已经提交,这时宕机,导致alpha没有收到结束事件,原先的逻辑是不会回滚当前分支的,重启业务服务也不会回滚。我们改成回滚当前分支,但如果分支没有完成,已经自动回滚了,这时要求补偿接口不要产生副作用。
用这种框架写业务要求太多,怎么做都有坑。怎么样想办法降低门槛才行,Seata可自动补偿,提供了部分隔离性,可参考一下。
loveoobaby commentedon Sep 24, 2019
Seata的自动补偿建立在解析SQL上,也是非常棒的想法
coolbeevip commentedon Sep 24, 2019
是的,第一原则就是避免分布式事务,可是我的场景面临较多的第三方服务的整合以及一些非关系数据库的业务系统,所以 Seata 的 SQL 模式不太适合我。
WillemJiang commentedon Sep 24, 2019
我觉得咱们可以把这部分的内容在新的版本中实现, 我刚刚建了一个JIRA SCB-1496来跟这个问题, 欢迎提供PR。
zhfeng commentedon Sep 25, 2019
非常高兴能看到您的反馈。Pack做为一个通用的分布式事务解决框架和方案,还有很多地方可以提高和改进。欢迎加入我们的社区!
zhfeng commentedon Sep 25, 2019
如果不发送结束请求的话,如何判断事务是正常结束还是已经超时?在超时的情况下,现在的框架是要进行回滚的。
loveoobaby commentedon Sep 25, 2019
7 remaining items