言尽于此,完结
无论是一个初级的 coder,高级的程序员,还是顶级的系统架构师,应该都有深刻的领会到设计模式的重要性。
- 第一,设计模式能让专业人之间交流方便,如下:
程序员A:这里我用了XXX设计模式
程序员B:那我大致了解你程序的设计思路了
- 第二,易维护
项目经理:今天客户有这样一个需求…
程序员:明白了,这里我使用了XXX设计模式,所以改起来很快
- 第三,设计模式是编程经验的总结
程序员A:B,你怎么想到要这样去构建你的代码
程序员B:在我学习了XXX设计模式之后,好像自然而然就感觉这样写能避免一些问题
- 第四,学习设计模式并不是必须的
程序员A:B,你这段代码使用的是XXX设计模式对吗?
程序员B:不好意思,我没有学习过设计模式,但是我的经验告诉我是这样写的
从设计思想解读开源框架,一步一步到Spring、Spring5、SpringMVC、MyBatis等源码解读,我都已收集整理全套,篇幅有限,这块只是详细的解说了23种设计模式,整理的文件如下图一览无余!
搜集费时费力,能看到此处的都是真爱!
引入 preCommit 阶段后,协调节点会在 commit 之前再次检查各个事务参与者的状态,保证它们的状态是一致的。但是也存在问题,那就是如果第三阶段发出 rollback 请求,有的节点没有收到,那没有收到的节点会在超时之后进行提交,造成数据不一致。
==============================================================================
xa 事务的语法如下:
- 三阶段的第一阶段:开启 xa 事务,这里 xid 为全局事务 id:
XA {START|BEGIN} xid [JOIN|RESUME]
结束 xa 事务:
XA END xid [SUSPEND [FOR MIGRATE]]
- 三阶段的第二阶段,即 prepare:
XA PREPARE xid
- 三阶段的第三阶段,即 commit/rollback:
XA COMMIT xid [ONE PHASE]
XA ROLLBACK xid
- 查看处于 PREPARE 阶段的所有事务:
XA RECOVER XA RECOVER [CONVERT XID]
================================================================================
seata 是阿里推出的一款开源分布式事务解决方案,目前有 AT、TCC、SAGA、XA 四种模式。
seata 的 XA 模式是利用分支事务中数据库对 XA 协议的支持来实现的。我们看一下 seata 官网的介绍:[1]
从上面的图可以看到,seata XA 模式的流程跟其他模式一样:
-
TM 开启全局事务
-
RM 向 TC 注册分支事务
-
RM 向 TC 报告分支事务状态
-
TC 向 RM 发送 commit/rollback 请求
-
TM 结束全局事务
这里介绍一下 RM 客户端初始化关联的 UML 类图:[2]
这个图中有一个类是 AbstractNettyRemotingClient,这个类的内部类 ClientHandler 来处理 TC 发来的请求并委托给父类 AbstractNettyRemoting 的 processMessage 方法来处理。processMessage 方法调用 RmBranchCommitProcessor 类的 process 方法。
需要注意的是,「seata 的 xa 模式对传统的三阶段提交做了优化,改成了两阶段提交」:
-
第一阶段首执行 XA 开启、执行 sql、XA 结束三个步骤,之后直接执行 XA prepare。
-
第二阶段执行 XA commit/rollback。
mysql 目前是支持 seata xa 模式的两阶段优化的。
「但是这个优化对 oracle 不支持,因为 oracle 实现的是标准的 xa 协议,即 xa end 后,协调节点向事务参与者统一发送 prepare,最后再发送 commit/rollback。这也导致了 seata 的 xa 模式对 oracle 支持不太好。」
================================================================================
seata 中的 XA 模式是使用数据源代理来实现的,需要手动配置数据源代理,代码如下:
@Bean
@ConfigurationProperties(prefix = “spring.datasource”)
public DruidDataSource druidDataSource() {
return new DruidDataSource();
}
@Bean(“dataSourceProxy”)
public DataSource dataSource(DruidDataSource druidDataSource) {
return new DataSourceProxyXA(druidDataSource);
}
-
也可以根据普通 DataSource 来创建 XAConnection,但是这种方式有兼容性问题(比如 oracle),所以 seata 使用了开发者自己配置 XADataSource。
-
seata 提供的 XA 数据源代理,要求代码框架中必须使用 druid 连接池。
当 RM 收到 DML 请求后,seata 会使用 ExecuteTemplateXA来执行,执行方法 execute 中有一个地方很关键,就是把 autocommit 属性改为了 false,而 mysql 默认 autocommit 是 true。事务提交之后,还要把 autocommit 改回默认。
下面我们看一下 XA 第一阶段提交的主要代码。
1)开启 XA
上面代码标注[1]处,调用了 ConnectionProxyXA 类的 setAutoCommit 方法,这个方法的源代码中,XA start 主要做了三件事:
-
向 TC 注册分支事务
-
调用数据源的 XA Start
xaResource.start(this.xaBranchXid, XAResource.TMNOFLAGS);
- 把 xaActive 设置为 true
RM 并没有直接使用 TC 返回的 branchId 作为 xa 数据源的 branchId,而是使用全局事务 id(xid) 和 branchId 重新构建了一个。
2)执行 sql
调用 PreparedStatementProxyXA 的 execute 执行 sql。
3)XA end/prepare
public void commit() throws SQLException {
//省略部分源代码
try {
// XA End: Success
xaResource.end(xaBranchXid, XAResource.TMSUCCESS);
// XA Prepare
xaResource.prepare(xaBranchXid);
// Keep the Connection if necessary
keepIfNecessary();
} catch (XAException xe) {
try {
// Branch Report to TC: Failed
DefaultResourceManager.get().branchReport(BranchType.XA, xid, xaBranchXid.getBranchId(),
BranchStatus.PhaseOne_Failed, null);
} catch (TransactionException te) {
//这儿只打印了一个warn级别的日志
}
throw new SQLException(
"Failed to end(TMSUCCESS)/prepare xa branch on " + xid + “-” + xaBranchXid.getBranchId() + " since " + xe
.getMessage(), xe);
} finally {
cleanXABranchContext();
}
}
从这个源码我们看到,commit 主要做了三件事:
-
调用数据源的 XA end
-
调用数据源的 XA prepare
-
向 TC 报告分支事务状态
到这里我们就可以看到,seata 把 xa 协议的前两个阶段合成了一个阶段。
这里的调用关系用一个时序图来表示:
看一下 RmBranchCommitProcessor 类的 process 方法,代码如下:
@Override
public void process(ChannelHandlerContext ctx, RpcMessage rpcMessage) throws Exception {
String remoteAddress = NetUtil.toStringAddress(ctx.channel().remoteAddress());
Object msg = rpcMessage.getBody();
if (LOGGER.isInfoEnabled()) {
LOGGER.info(“rm client handle branch commit process:” + msg);
}
handleBranchCommit(rpcMessage, remoteAddress, (BranchCommitRequest) msg);
}
结局:总结+分享
看完美团、字节、腾讯这三家的一二三面试问题,是不是感觉问的特别多,可能咱们真的又得开启面试造火箭、工作拧螺丝的模式去准备下一次的面试了。
开篇有提及我可是足足背下了Java互联网工程师面试1000题,多少还是有点用的呢,换汤不换药,不管面试官怎么问你,抓住本质即可!能读到此处的都是真爱
- Java互联网工程师面试1000题
而且从上面三家来看,算法与数据结构是必备不可少的呀,因此我建议大家可以去刷刷这本左程云大佬著作的 《程序员代码面试指南 IT名企算法与数据结构题目最优解》,里面近200道真实出现过的经典代码面试题。
- 程序员代码面试指南–IT名企算法与数据结构题目最优解
- 其余像设计模式,建议可以看看下面这4份PDF(已经整理)
- 更多的Java面试学习笔记如下,关于面试这一块,我额外细分出Java基础-中级-高级开发的面试+解析,以及调优笔记等等等。。。
以上所提及的全部Java面试学习的PDF及笔记,如若皆是你所需要的,那么都可发送给你!
797824170)]
- 其余像设计模式,建议可以看看下面这4份PDF(已经整理)
[外链图片转存中…(img-qSMRUl5z-1715797824170)]
- 更多的Java面试学习笔记如下,关于面试这一块,我额外细分出Java基础-中级-高级开发的面试+解析,以及调优笔记等等等。。。
[外链图片转存中…(img-WjJqLfrm-1715797824170)]
以上所提及的全部Java面试学习的PDF及笔记,如若皆是你所需要的,那么都可发送给你!