JTA XAResource 多个数据库:如果准备后两阶段提交失败怎么办
JTA XAResource multiple databases: what if two phase commit fails after prepare
我需要做一个 POC,它在核心 java 应用程序(非 Web)中使用 JTA XA 资源和 MySQL,而不使用任何框架。所有文章和示例都显示使用单个数据源提交。虽然我已经用两个数据库完成了分布式事务,但是我已经 doubts/queries in 'Two Phase Commit' 使用 XA Resource。我的代码是:
public static void main(String[] args) {
DataSource dataSourceRemote = ConnectionManager.getDatasourceRemote();
DataSource dataSourceLocal = ConnectionManager.getDatasourceLocal();
XAResource xaRes;
XAResource xaRes2;
XID xid, xid2;
try {
XADataSource xaDataSourceLocal;
XAConnection xaCon;
//XID xid;
Connection con;
Statement stmt;
int ret;
XADataSource xaDataSourceRemote;
XAConnection xaCon2;
//XID xid2;
Connection con2;
Statement stmt2;
int ret2;xaDataSourceLocal = (XADataSource) dataSourceLocal;
xaCon = xaDataSourceLocal.getXAConnection("root", "root");
con = xaCon.getConnection();
stmt = con.createStatement();
xaRes = xaCon.getXAResource();
xaDataSourceRemote = (XADataSource) dataSourceRemote;
xaCon2 = xaDataSourceRemote.getXAConnection("root", "root");
con2 = xaCon2.getConnection();
stmt2 = con2.createStatement();
xaRes2 = xaCon2.getXAResource();
con.setAutoCommit(false);
con2.setAutoCommit(false);
xid = new XID(100, new byte[] {0x01}, new byte[] {0x02});
xid2 = new XID(101, new byte[] {0x02}, new byte[] {0x03});
xaRes.start(xid, XAResource.TMNOFLAGS);
String query = "insert into emp values (12, \"Sanjay\", \"12345\", 100000)";
System.out.println(query);
stmt.executeUpdate(query);
xaRes2.start(xid2, XAResource.TMNOFLAGS);
stmt2.executeUpdate("insert into emp values (11, \"Nikhil\", \"12345\", 100000)");
xaRes2.end(xid2, XAResource.TMSUCCESS);
xaRes.end(xid, XAResource.TMSUCCESS);ret = xaRes.prepare(xid);
ret2 = xaRes2.prepare(xid2);
if(ret == XAResource.XA_OK && ret2 == XAResource.XA_OK){
xaRes2.commit(xid2, false);
xaRes.commit(xid, false);
}
con.close();
con2.close();
xaCon.close();
xaCon2.close();
con2.close();
} catch (XAException e) {
e.printStackTrace();
}
catch (SQLException e) {
e.printStackTrace();
}
}
现在我的查询是:
1) 是否需要定义两个Xid 就像我用Xid 替换Xid2,代码运行正常
2) 我在行进行了调试:if(ret == XAResource.XA_OK && ret2 == XAResource.XA_OK), when prepare returns XAResource.XA_OK and condition对于两个 xresources 都是正确的。现在,如果我停止 MySQL 服务到我的本地机器,xaRes2.commit(xid2, false);将成功运行并且 xaRes.commit(xid, false);会抛出 XA 异常。在这种情况下 'two phase commit' 失败,因为一个数据源处于不一致状态。
我的问题是,我做得对还是有其他方法?
3) recover 的实际用途是什么,因为在这种情况下,一个事务已提交,而其他事务在准备成功返回后失败
1) 我觉得你搞糊涂了。 XA 事务由 XID identified/referenced。因此,如果您使用两个 xID,您将有两个 XA 事务。
您可以在两个不同的 jam 中使用相同的 XID,它们都可以属于同一个 XA 事务。
2) 通常,您有一个事务管理器来处理此问题并在出现错误时进行清理。在这种情况下,您基本上需要实现它并且您需要处理所有错误情况,这不是微不足道的。为确保恢复部分正常工作,您需要测试所有不同的错误场景。例如,Oracle 允许您在提交代码路径中的不同点使提交失败以进行测试。我不确定 mysql 是否具有相同的能力。
3) 例如,如果您的 XA 事务的一个分支失败,或者如果您提交但您的资源管理器之一无法提交,则需要恢复。
一般来说,如果您在您的案例中使用像 Mysql 这样的数据库,您需要该数据库实现 xa 协议,否则恢复很可能无法正常工作。当然,您需要告诉数据库这是一个 xa 事务。
我需要做一个 POC,它在核心 java 应用程序(非 Web)中使用 JTA XA 资源和 MySQL,而不使用任何框架。所有文章和示例都显示使用单个数据源提交。虽然我已经用两个数据库完成了分布式事务,但是我已经 doubts/queries in 'Two Phase Commit' 使用 XA Resource。我的代码是:
public static void main(String[] args) {
DataSource dataSourceRemote = ConnectionManager.getDatasourceRemote();
DataSource dataSourceLocal = ConnectionManager.getDatasourceLocal();
XAResource xaRes;
XAResource xaRes2;
XID xid, xid2;
try {
XADataSource xaDataSourceLocal;
XAConnection xaCon;
//XID xid;
Connection con;
Statement stmt;
int ret;
XADataSource xaDataSourceRemote;
XAConnection xaCon2;
//XID xid2;
Connection con2;
Statement stmt2;
int ret2;xaDataSourceLocal = (XADataSource) dataSourceLocal;
xaCon = xaDataSourceLocal.getXAConnection("root", "root");
con = xaCon.getConnection();
stmt = con.createStatement();
xaRes = xaCon.getXAResource();
xaDataSourceRemote = (XADataSource) dataSourceRemote;
xaCon2 = xaDataSourceRemote.getXAConnection("root", "root");
con2 = xaCon2.getConnection();
stmt2 = con2.createStatement();
xaRes2 = xaCon2.getXAResource();
con.setAutoCommit(false);
con2.setAutoCommit(false);
xid = new XID(100, new byte[] {0x01}, new byte[] {0x02});
xid2 = new XID(101, new byte[] {0x02}, new byte[] {0x03});
xaRes.start(xid, XAResource.TMNOFLAGS);
String query = "insert into emp values (12, \"Sanjay\", \"12345\", 100000)";
System.out.println(query);
stmt.executeUpdate(query);
xaRes2.start(xid2, XAResource.TMNOFLAGS);
stmt2.executeUpdate("insert into emp values (11, \"Nikhil\", \"12345\", 100000)");
xaRes2.end(xid2, XAResource.TMSUCCESS);
xaRes.end(xid, XAResource.TMSUCCESS);ret = xaRes.prepare(xid);
ret2 = xaRes2.prepare(xid2);
if(ret == XAResource.XA_OK && ret2 == XAResource.XA_OK){
xaRes2.commit(xid2, false);
xaRes.commit(xid, false);
}
con.close();
con2.close();
xaCon.close();
xaCon2.close();
con2.close();
} catch (XAException e) {
e.printStackTrace();
}
catch (SQLException e) {
e.printStackTrace();
}
}
现在我的查询是:
1) 是否需要定义两个Xid 就像我用Xid 替换Xid2,代码运行正常
2) 我在行进行了调试:if(ret == XAResource.XA_OK && ret2 == XAResource.XA_OK), when prepare returns XAResource.XA_OK and condition对于两个 xresources 都是正确的。现在,如果我停止 MySQL 服务到我的本地机器,xaRes2.commit(xid2, false);将成功运行并且 xaRes.commit(xid, false);会抛出 XA 异常。在这种情况下 'two phase commit' 失败,因为一个数据源处于不一致状态。 我的问题是,我做得对还是有其他方法?
3) recover 的实际用途是什么,因为在这种情况下,一个事务已提交,而其他事务在准备成功返回后失败
1) 我觉得你搞糊涂了。 XA 事务由 XID identified/referenced。因此,如果您使用两个 xID,您将有两个 XA 事务。
您可以在两个不同的 jam 中使用相同的 XID,它们都可以属于同一个 XA 事务。
2) 通常,您有一个事务管理器来处理此问题并在出现错误时进行清理。在这种情况下,您基本上需要实现它并且您需要处理所有错误情况,这不是微不足道的。为确保恢复部分正常工作,您需要测试所有不同的错误场景。例如,Oracle 允许您在提交代码路径中的不同点使提交失败以进行测试。我不确定 mysql 是否具有相同的能力。
3) 例如,如果您的 XA 事务的一个分支失败,或者如果您提交但您的资源管理器之一无法提交,则需要恢复。
一般来说,如果您在您的案例中使用像 Mysql 这样的数据库,您需要该数据库实现 xa 协议,否则恢复很可能无法正常工作。当然,您需要告诉数据库这是一个 xa 事务。