tx-lcn
tx-lcn copied to clipboard
LCN模式下使用mysql主从模式导致事物不提交
我们的场景是 在参与者中使用了多数据源
场景
我们自定义了读写分离的DataSource,继承了org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource,
问题
AbstractRoutingDataSource这个类实现了javax.sql.DataSource,而他的getConnection()的实现方法中调用了连接池的DataSource中的getConnection()方法,如下
package org.springframework.jdbc.datasource.lookup;
public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {
@Override
public Connection getConnection() throws SQLException {
return determineTargetDataSource().getConnection();
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return determineTargetDataSource().getConnection(username, password);
}
}
而连接池的DataSource的getConnection()方法会一起被DataSourceAspect这个环绕嵌套触发两次,继而在LcnTransactionResourceProxy中代理Connection的时候发生被两次代理
public class LcnTransactionResourceProxy implements TransactionResourceProxy {
// ...
@Override
public Connection proxyConnection(ConnectionCallback connectionCallback) throws Throwable {
String groupId = DTXLocalContext.cur().getGroupId();
try {
return globalContext.getLcnConnection(groupId);
} catch (TCGlobalContextException e) {
// 这里为什么会触发两次我也没有搞清楚
LcnConnectionProxy lcnConnectionProxy = new LcnConnectionProxy(connectionCallback.call());
globalContext.setLcnConnection(groupId, lcnConnectionProxy);
lcnConnectionProxy.setAutoCommit(false);
return lcnConnectionProxy;
}
}
}
最后我们拿到的Connection变成了new LcnConnectionProxy(new LcnConnectionProxy(originConnection)),而正确的应该是new LcnConnectionProxy(originConnection),这也就是为什么业务中回出现链接无法Commit和Close的原因了。
解决
我们尝试性的做了一个简单的容错,虽然不是根本的解决问题,只是可以解决当前场景下的问题,希望可以帮助到问题的定位
我们的处理方式如下
package com.codingapi.txlcn.tc.core.transaction.lcn.resource;
//......
@Service(value = "transaction_lcn")
@Slf4j
public class LcnTransactionResourceProxy implements TransactionResourceProxy {
//...
@Override
public Connection proxyConnection(ConnectionCallback connectionCallback) throws Throwable {
String groupId = DTXLocalContext.cur().getGroupId();
try {
return globalContext.getLcnConnection(groupId);
} catch (TCGlobalContextException e) {
Connection currentConnection = connectionCallback.call();
//这里是我们的容错
if(currentConnection instanceof LcnConnectionProxy){
LcnConnectionProxy lcnConnectionProxy = (LcnConnectionProxy)currentConnection;
globalContext.setLcnConnection(groupId, lcnConnectionProxy );
lcnConnectionProxy.setAutoCommit(false);
return lcnConnectionProxy;
}
LcnConnectionProxy lcnConnectionProxy = new LcnConnectionProxy(connectionCallback.call());
globalContext.setLcnConnection(groupId, lcnConnectionProxy);
lcnConnectionProxy.setAutoCommit(false);
return lcnConnectionProxy;
}
}
}