Seata客户端的启动过程
1.自动装配4个配置类
将在SpringBoot启动时往容器中添加4个类
1. 自动配置类 SeataAutoConfiguration
SeataAutoConfiguration将会往容器中添加两个bean
- failureHandler 事务处理失败执行器
- globalTransactionScanner
failureHandler
failureHandler 实现了 FailureHandler 接口,将在事务处理出现异常时,执行相应阶段失败时的方法,我们可以自定类实现此接口,
在失败时执行对应的方法,比如发送邮件通知。但是要注意的是,自定义的事务失败处理器的beanName必须叫failureHandler,否则GlobalTransactionScanner将不会添加到容器中。
public interface FailureHandler {
void onBeginFailure(GlobalTransaction tx, Throwable cause);
void onCommitFailure(GlobalTransaction tx, Throwable cause);
void onRollbackFailure(GlobalTransaction tx, Throwable originalException);
void onRollbackRetrying(GlobalTransaction tx, Throwable originalException);
void onTimeoutRollback(GlobalTransaction tx, Throwable originalException);
}
failureHandler 并且会启动周期任务,去检查事务状态是不是正常完成,否则将继续打印警告信息
public void onBeginFailure(GlobalTransaction tx, Throwable cause) {
LOGGER.warn("Failed to begin transaction. ", cause);
}
public void onCommitFailure(GlobalTransaction tx, Throwable cause) {
LOGGER.warn("Failed to commit transaction[" + tx.getXid() + "]", cause);
this.timer.newTimeout(new CheckTimerTask(tx, GlobalStatus.Committed), 10L, TimeUnit.SECONDS);
}
public void onRollbackFailure(GlobalTransaction tx, Throwable originalException) {
LOGGER.warn("Failed to rollback transaction[" + tx.getXid() + "]", originalException);
this.timer.newTimeout(new CheckTimerTask(tx, GlobalStatus.Rollbacked), 10L, TimeUnit.SECONDS);
}
public void onRollbackRetrying(GlobalTransaction tx, Throwable originalException) {
StackTraceLogger.warn(LOGGER, originalException, "Retrying to rollback transaction[{}]", new String[]{tx.getXid()});
this.timer.newTimeout(new CheckTimerTask(tx, GlobalStatus.RollbackRetrying), 10L, TimeUnit.SECONDS);
}
public void onTimeoutRollback(GlobalTransaction tx, Throwable originalException) {
StackTraceLogger.warn(LOGGER, originalException, "Transaction timeout rollback[{}]", new String[]{tx.getXid()});
}
globalTransactionScanner
实现了4个接口,继承了一个父类
- AbstractAutoProxyCreator 创建代理类
- ConfigurationChangeListener 监听配置文件变更
- initializingBean 初始化bean执行回调方法
- ApplicationContextAware 获取spring容器
- DisposableBean 可丢弃bean
首先看initializingBean 的afterPropertiesSet()回调方法
disableGlobalTransaction 默认是false,走 initClient方法
private void initClient() {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Initializing Global Transaction Clients ... ");
}
if (DEFAULT_TX_GROUP_OLD.equals(txServiceGroup)) {
LOGGER.warn("the default value of seata.tx-service-group: {} has already changed to {} since Seata 1.5, " +
"please change your default configuration as soon as possible " +
"and we don't recommend you to use default tx-service-group's value provided by seata",
DEFAULT_TX_GROUP_OLD, DEFAULT_TX_GROUP);
}
if (StringUtils.isNullOrEmpty(applicationId) || StringUtils.isNullOrEmpty(txServiceGroup)) {
throw new IllegalArgumentException(String.format("applicationId: %s, txServiceGroup: %s", applicationId, txServiceGroup));
}
//init TM
TMClient.init(applicationId, txServiceGroup, accessKey, secretKey);
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Transaction Manager Client is initialized. applicationId[{}] txServiceGroup[{}]", applicationId, txServiceGroup);
}
//init RM
RMClient.init(applicationId, txServiceGroup);
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Resource Manager is initialized. applicationId[{}] txServiceGroup[{}]", applicationId, txServiceGroup);
}
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Global Transaction Clients are initialized. ");
}
registerSpringShutdownHook();
}
其中完成了TM与RM的初始化
首先看TM的初始化
创建了TmNettyRemotingClient实列,而TmNettyRemotingClient 的成员变量 instance 则是一个TmNettyRemotingClient 对象,用于客户端与服务端通信的netty客户端,消息处理器为io.seata.core.rpc.netty.AbstractNettyRemotingClient.ClientHandler,同样也是一个双向消息处理器
@Sharable
class ClientHandler extends ChannelDuplexHandler {
@Override
public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
if (!(msg instanceof RpcMessage)) {
return;
}
processMessage(ctx, (RpcMessage) msg);
}
@Override
public void channelWritabilityChanged(ChannelHandlerContext ctx) {
synchronized (lock) {
if (ctx.channel().isWritable()) {
lock.notifyAll();
}
}
ctx.fireChannelWritabilityChanged();
}
......
}
调用了TmNettyRemotingClient.init方法
@Override
public void init() {
// registry processor
registerProcessor();
if (initialized.compareAndSet(false, true)) {
super.init();
if (io.seata.common.util.StringUtils.isNotBlank(transactionServiceGroup)) {
getClientChannelManager().reconnect(transactionServiceGroup);
}
}
}
registerProcessor() 注册消息处理器,同样封装成Pair放入map中
super.registerProcessor(MessageType.TYPE_SEATA_MERGE_RESULT, onResponseProcessor, null);
super.registerProcessor(MessageType.TYPE_GLOBAL_BEGIN_RESULT, onResponseProcessor, null);
super.registerProcessor(MessageType.TYPE_GLOBAL_COMMIT_RESULT, onResponseProcessor, null);
super.registerProcessor(MessageType.TYPE_GLOBAL_REPORT_RESULT, onResponseProcessor, null);
super.registerProcessor(MessageType.TYPE_GLOBAL_ROLLBACK_RESULT, onResponseProcessor, null);
super.registerProcessor(MessageType.TYPE_GLOBAL_STATUS_RESULT, onResponseProcessor, null);
super.registerProcessor(MessageType.TYPE_REG_CLT_RESULT, onResponseProcessor, null);
super.registerProcessor(MessageType.TYPE_BATCH_RESULT_MSG, onResponseProcessor, null);
getClientChannelManager().reconnect(transactionServiceGroup)与服务端建立连接
void reconnect(String transactionServiceGroup) {
List<String> availList = null;
try {
availList = getAvailServerList(transactionServiceGroup);
} catch (Exception e) {
LOGGER.error("Failed to get available servers: {}", e.getMessage(), e);
return;
}
if (CollectionUtils.isEmpty(availList)) {
RegistryService registryService = RegistryFactory.getInstance();
String clusterName = registryService.getServiceGroup(transactionServiceGroup);
if (StringUtils.isBlank(clusterName)) {
LOGGER.error("can not get cluster name in registry config '{}{}', please make sure registry config correct",
ConfigurationKeys.SERVICE_GROUP_MAPPING_PREFIX,
transactionServiceGroup);
return;
}
if (!(registryService instanceof FileRegistryServiceImpl)) {
LOGGER.error("no available service found in cluster '{}', please make sure registry config correct and keep your seata server running", clusterName);
}
return;
}
Set<String> channelAddress = new HashSet<>(availList.size());
try {
for (String serverAddress : availList) {
try {
acquireChannel(serverAddress);
channelAddress.add(serverAddress);
} catch (Exception e) {
LOGGER.error("{} can not connect to {} cause:{}", FrameworkErrorCode.NetConnect.getErrCode(),
serverAddress, e.getMessage(), e);
}
}
} finally {
if (CollectionUtils.isNotEmpty(channelAddress)) {
List<InetSocketAddress> aliveAddress = new ArrayList<>(channelAddress.size());
for (String address : channelAddress) {
String[] array = address.split(":");
aliveAddress.add(new InetSocketAddress(array[0], Integer.parseInt(array[1])));
}
RegistryFactory.getInstance().refreshAliveLookup(transactionServiceGroup, aliveAddress);
} else {
RegistryFactory.getInstance().refreshAliveLookup(transactionServiceGroup, Collections.emptyList());
}
}
}
seata服务收到TM注册消息后,会调用io.seata.core.rpc.processor.server.RegTmProcessor#onRegTmMessage,并给客户端返回注册成功的消息
private void onRegTmMessage(ChannelHandlerContext ctx, RpcMessage rpcMessage) {
RegisterTMRequest message = (RegisterTMRequest) rpcMessage.getBody();
String ipAndPort = NetUtil.toStringAddress(ctx.channel().remoteAddress());
Version.putChannelVersion(ctx.channel(), message.getVersion());
boolean isSuccess = false;
String errorInfo = StringUtils.EMPTY;
try {
if (null == checkAuthHandler || checkAuthHandler.regTransactionManagerCheckAuth(message)) {
ChannelManager.registerTMChannel(message, ctx.channel());
Version.putChannelVersion(ctx.channel(), message.getVersion());
isSuccess = true;
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("TM checkAuth for client:{},vgroup:{},applicationId:{} is OK",
ipAndPort, message.getTransactionServiceGroup(), message.getApplicationId());
}
} else {
if (LOGGER.isWarnEnabled()) {
LOGGER.warn("TM checkAuth for client:{},vgroup:{},applicationId:{} is FAIL",
ipAndPort, message.getTransactionServiceGroup(), message.getApplicationId());
}
}
} catch (Exception exx) {
isSuccess = false;
errorInfo = exx.getMessage();
LOGGER.error("TM register fail, error message:{}", errorInfo);
}
RegisterTMResponse response = new RegisterTMResponse(isSuccess);
if (StringUtils.isNotEmpty(errorInfo)) {
response.setMsg(errorInfo);
}
remotingServer.sendAsyncResponse(rpcMessage, ctx.channel(), response);
if (isSuccess && LOGGER.isInfoEnabled()) {
LOGGER.info("TM register success,message:{},channel:{},client version:{}", message, ctx.channel(),
message.getVersion());
}
}
RM的初始化
public static void init(String applicationId, String transactionServiceGroup) {
//实例化RM的netty客户端
RmNettyRemotingClient rmNettyRemotingClient = RmNettyRemotingClient.getInstance(applicationId, transactionServiceGroup);
//设置RM资源管理
rmNettyRemotingClient.setResourceManager(DefaultResourceManager.get());
//设置RM事务管理器
rmNettyRemotingClient.setTransactionMessageHandler(DefaultRMHandler.get());
rmNettyRemotingClient.init();
}
ResourceManager 用于管理数据库资源
TransactionMessageHandler用来处理RM事务消息
rmNettyRemotingClient.init()方法中注册消息处理器,需要注意的是,此时并不会与seata服务器建立连接,因为此时还比没有管理数据库资源
public void init() {
// registry processor
registerProcessor();
if (initialized.compareAndSet(false, true)) {
super.init();
// Found one or more resources that were registered before initialization
if (resourceManager != null
&& !resourceManager.getManagedResources().isEmpty()
&& StringUtils.isNotBlank(transactionServiceGroup)) {
getClientChannelManager().reconnect(transactionServiceGroup);
}
}
}
遍历四种模式下的资源管理器,获取其所管理的资源(此时全为空),因此并不会与seata服务端在此时建立连接。 RM与TC建立连接可以看后面创建代理数据源的过程。RM是用来管理代理数据源,当没有数据源的时候,也没必要建立连接,真正向TC注册RM的时机在初始化代理数据源后。
seata服务端io.seata.core.rpc.processor.server.RegRmProcessor#onRegRmMessage 返回RM注册成功消息
private void onRegRmMessage(ChannelHandlerContext ctx, RpcMessage rpcMessage) {
RegisterRMRequest message = (RegisterRMRequest) rpcMessage.getBody();
String ipAndPort = NetUtil.toStringAddress(ctx.channel().remoteAddress());
boolean isSuccess = false;
String errorInfo = StringUtils.EMPTY;
try {
if (null == checkAuthHandler || checkAuthHandler.regResourceManagerCheckAuth(message)) {
ChannelManager.registerRMChannel(message, ctx.channel());
Version.putChannelVersion(ctx.channel(), message.getVersion());
isSuccess = true;
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("RM checkAuth for client:{},vgroup:{},applicationId:{} is OK", ipAndPort, message.getTransactionServiceGroup(), message.getApplicationId());
}
} else {
if (LOGGER.isWarnEnabled()) {
LOGGER.warn("RM checkAuth for client:{},vgroup:{},applicationId:{} is FAIL", ipAndPort, message.getTransactionServiceGroup(), message.getApplicationId());
}
}
} catch (Exception exx) {
isSuccess = false;
errorInfo = exx.getMessage();
LOGGER.error("RM register fail, error message:{}", errorInfo);
}
RegisterRMResponse response = new RegisterRMResponse(isSuccess);
if (StringUtils.isNotEmpty(errorInfo)) {
response.setMsg(errorInfo);
}
remotingServer.sendAsyncResponse(rpcMessage, ctx.channel(), response);
if (isSuccess && LOGGER.isInfoEnabled()) {
LOGGER.info("RM register success,message:{},channel:{},client version:{}", message, ctx.channel(),
message.getVersion());
}
}
2.自动配置类 SeataDataSourceAutoConfiguration
此自动配置类只会往容器中添加一个Bean :SeataAutoDataSourceProxyCreator,来自动创建数据源代理对象
@Bean({"seataAutoDataSourceProxyCreator"})
@ConditionalOnMissingBean({SeataAutoDataSourceProxyCreator.class})
public static SeataAutoDataSourceProxyCreator seataAutoDataSourceProxyCreator(SeataProperties seataProperties) {
return new SeataAutoDataSourceProxyCreator(seataProperties.isUseJdkProxy(), seataProperties.getExcludesForAutoProxying(), seataProperties.getDataSourceProxyMode());
}
、
SeataDataSourceAutoConfiguration继承org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator 抽象类,并且重写了父类的wrapIfNecessary方法
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
//判断当前bean是否是数据源类,不是不需要代理,直接返回
if (!(bean instanceof DataSource)) {
return bean;
} else {
DataSource origin;
//判断是否是数据源代理类,如果是,则不需要er
if (!(bean instanceof SeataDataSourceProxy)) {
//获取缓存代理
Object enhancer = super.wrapIfNecessary(bean, beanName, cacheKey);
//如果当前bean就是缓存代理,直接返回
if (bean == enhancer) {
return bean;
} else {
//创建代理数据源
origin = (DataSource)bean;
SeataDataSourceProxy proxy = this.buildProxy(origin, this.dataSourceProxyMode);
DataSourceProxyHolder.put(origin, proxy);
return enhancer;
}
} else {
LOGGER.warn("Manually register SeataDataSourceProxy(or its subclass) bean is discouraged! bean name: {}", beanName);
SeataDataSourceProxy proxy = (SeataDataSourceProxy)bean;
origin = proxy.getTargetDataSource();
Object originEnhancer = super.wrapIfNecessary(origin, beanName, cacheKey);
if (origin == originEnhancer) {
return origin;
} else {
DataSourceProxyHolder.put(origin, proxy);
return originEnhancer;
}
}
}
}
调用buildProxy方法将当前数据源封装到DataSourceProxy对象中
SeataDataSourceProxy buildProxy(DataSource origin, String proxyMode) {
if (BranchType.AT.name().equalsIgnoreCase(proxyMode)) {
return new DataSourceProxy(origin);
} else if (BranchType.XA.name().equalsIgnoreCase(proxyMode)) {
return new DataSourceProxyXA(origin);
} else {
throw new IllegalArgumentException("Unknown dataSourceProxyMode: " + proxyMode);
}
}
并且跟进 构造,找到init方法
private void init(DataSource dataSource, String resourceGroupId) {
this.resourceGroupId = resourceGroupId;
try {
Connection connection = dataSource.getConnection();
Throwable var4 = null;
try {
this.jdbcUrl = connection.getMetaData().getURL();
this.dbType = JdbcUtils.getDbType(this.jdbcUrl);
if ("oracle".equals(this.dbType)) {
this.userName = connection.getMetaData().getUserName();
} else if ("mariadb".equals(this.dbType)) {
this.dbType = "mysql";
}
this.version = this.selectDbVersion(connection);
} catch (Throwable var14) {
var4 = var14;
throw var14;
} finally {
if (connection != null) {
if (var4 != null) {
try {
connection.close();
} catch (Throwable var13) {
var4.addSuppressed(var13);
}
} else {
connection.close();
}
}
}
} catch (SQLException var16) {
throw new IllegalStateException("can not init dataSource", var16);
}
this.initResourceId();
//向TC注册RM
DefaultResourceManager.get().registerResource(this);
if (ENABLE_TABLE_META_CHECKER_ENABLE) {
this.tableMetaExecutor.scheduleAtFixedRate(() -> {
try {
Connection connection = dataSource.getConnection();
Throwable var3 = null;
try {
TableMetaCacheFactory.getTableMetaCache(this.getDbType()).refresh(connection, this.getResourceId());
} catch (Throwable var13) {
var3 = var13;
throw var13;
} finally {
if (connection != null) {
if (var3 != null) {
try {
connection.close();
} catch (Throwable var12) {
var3.addSuppressed(var12);
}
} else {
connection.close();
}
}
}
} catch (Exception var15) {
}
}, 0L, TABLE_META_CHECKER_INTERVAL, TimeUnit.MILLISECONDS);
}
RootContext.setDefaultBranchType(this.getBranchType());
}
可以看到此时完成了RM向TC的注册
3.自动配置类 SeataHttpAutoConfiguration
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnWebApplication
@ConditionalOnMissingBean({SeataWebMvcConfigurer.class})
@ConditionalOnProperty(
prefix = "seata.client.http",
name = {"interceptor-enabled"},
havingValue = "true",
matchIfMissing = true
)
@AutoConfigureOrder(Integer.MAX_VALUE)
public class SeataHttpAutoConfiguration {
public SeataHttpAutoConfiguration() {
}
@Bean
@ConditionalOnClass(
name = {"jakarta.servlet.http.HttpServletRequest"}
)
public JakartaSeataWebMvcConfigurer jakartaSeataWebMvcConfigurer() {
return new JakartaSeataWebMvcConfigurer();
}
@Bean
@ConditionalOnMissingBean({JakartaSeataWebMvcConfigurer.class})
public SeataWebMvcConfigurer seataWebMvcConfigurer() {
return new SeataWebMvcConfigurer();
}
}
public class SeataWebMvcConfigurer implements WebMvcConfigurerAdapter {
public SeataWebMvcConfigurer() {
}
public void addInterceptors(InterceptorRegistry registry) {
//添加拦截器
registry.addInterceptor(new TransactionPropagationInterceptor());
}
}
主要是为了在传递xid
4.自动配置类SeataSagaAutoConfiguration
SeataSagaAutoConfiguration主要是为了出来saga模式下的分布式事务,基本不用跳过。