我们知道seata分布式组件中支持AT/XA/TCC/Saga模式,在AT模式下有三个角色:
- TC (Transaction Coordinator) 维护全局和分支事务的状态,驱动全局事务提交或回滚。
- TM (Transaction Manager)-定义全局事务的范围:开始全局事务、提交或回滚全局事务
- RM (Resource Manager)-管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚
本文对TM客户端启动源码进行分析,分为3点
1.seata-spring集成
2.启动netty服务
3.创建代理类
一 、seata-spring集成
这里是大家所熟悉的spring.factories即spring SPI自动装载SeataAutoConfiguration这个配置类
org.springframework.boot.autoconfigure.EnableAutoConfiguration=io.seata.spring.boot.autoconfigure.SeataAutoConfiguration
了解spring-bean生命周期的同学对下面的内容应该不会陌生,通过@Bean注解将GlobalTransactionScanner注册到spring容器的beanDefinition中,并且在容器启动时初始化该bean
@Bean
@DependsOn({BEAN_NAME_SPRING_APPLICATION_CONTEXT_PROVIDER, BEAN_NAME_FAILURE_HANDLER})
@ConditionalOnMissingBean(GlobalTransactionScanner.class)
public GlobalTransactionScanner globalTransactionScanner(SeataProperties seataProperties, FailureHandler failureHandler) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Automatically configure Seata");
}
return new GlobalTransactionScanner(seataProperties.getApplicationId(), seataProperties.getTxServiceGroup(), failureHandler);
}
GlobalTransactionScanner继承了AbstractAutoProxyCreator(对符合条件的bean进行代理)以及实现了InitializingBean接口
public class GlobalTransactionScanner extends AbstractAutoProxyCreator implements InitializingBean, ApplicationContextAware,
DisposableBean
二、启动netty服务
我们知道实现了InitializingBean接口的bean需要重写afterPropertiesSet,在初始化bean时afterPropertiesSet方法会被调用
@Override
public void afterPropertiesSet() {
if (disableGlobalTransaction) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Global transaction is disabled.");
}
return;
}
initClient(); // @1
}
代码@1: initClient()方法完成netty-client的启动并以及channel的注册,分为TMClient以及RMClient的初始化
private void initClient() {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Initializing Global Transaction Clients ... ");
}
if (StringUtils.isNullOrEmpty(applicationId) || StringUtils.isNullOrEmpty(txServiceGroup)) {
throw new IllegalArgumentException(String.format("applicationId: %s, txServiceGroup: %s", applicationId, txServiceGroup));
}
TMClient.init(applicationId, txServiceGroup); // @1
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Transaction Manager Client is initialized. applicationId[{}] txServiceGroup[{}]", applicationId, txServiceGroup);
}
//init RM
RMClient.init(applicationId, txServiceGroup); // @2
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Resource Manager is initialized. applicationId[{}] txServiceGroup[{}]", applicationId, txServiceGroup);
}
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Global Transaction Clients are initialized. ");
}
registerSpringShutdownHook();
}
代码@1: 启动TMClient
代码@2: 启动RMClient
io.seata.core.rpc.netty.AbstractNettyRemotingClient#init定时任务链接TC服务端
@Override
public void init() {
timerExecutor.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
clientChannelManager.reconnect(getTransactionServiceGroup()); // @1
}
}, SCHEDULE_DELAY_MILLS, SCHEDULE_INTERVAL_MILLS, TimeUnit.MILLISECONDS);
if (NettyClientConfig.isEnableClientBatchSendRequest()) {
mergeSendExecutorService = new ThreadPoolExecutor(MAX_MERGE_SEND_THREAD,
MAX_MERGE_SEND_THREAD,
KEEP_ALIVE_TIME, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(),
new NamedThreadFactory(getThreadPrefix(), MAX_MERGE_SEND_THREAD));
//
mergeSendExecutorService.submit(new MergedSendRunnable()); // @2
}
super.init();
clientBootstrap.start();
}
代码@1 :获取所有seata-server列表,建立链接
代码@2: 建立一个启动批量发送的线程MergedSendRunnable,具体在后面文章再分析seata的rpc消息发送
Channel acquireChannel(String serverAddress) {
Channel channelToServer = channels.get(serverAddress);
if (channelToServer != null) {
channelToServer = getExistAliveChannel(channelToServer, serverAddress);
if (channelToServer != null) {
return channelToServer;
}
}
if (LOGGER.isInfoEnabled()) {
LOGGER.info("will connect to " + serverAddress);
}
channelLocks.putIfAbsent(serverAddress, new Object()); //创建一把锁
synchronized (channelLocks.get(serverAddress)) {
return doConnect(serverAddress); // 建立链接
}
}
三、创建代理类
作为AbstractAutoProxyCreator的实现类并且重写wrapIfNecessary方法,由@GlobalTransactional修饰的bean, 在spring初始化时,BeanPostProcessor方法postProcessAfterInitialization被执行创建AOP代理类,@GlobalTransactional修饰的方法执行实际由GlobalTransactionalInterceptor.invoke执行。
@Override
public Object invoke(final MethodInvocation methodInvocation) throws Throwable {
Class<?> targetClass =
methodInvocation.getThis() != null ? AopUtils.getTargetClass(methodInvocation.getThis()) : null;
Method specificMethod = ClassUtils.getMostSpecificMethod(methodInvocation.getMethod(), targetClass);
if (specificMethod != null && !specificMethod.getDeclaringClass().equals(Object.class)) {
final Method method = BridgeMethodResolver.findBridgedMethod(specificMethod);
final GlobalTransactional globalTransactionalAnnotation =
getAnnotation(method, targetClass, GlobalTransactional.class);
final GlobalLock globalLockAnnotation = getAnnotation(method, targetClass, GlobalLock.class);
boolean localDisable = disable || (degradeCheck && degradeNum >= degradeCheckAllowTimes);
if (!localDisable) {
if (globalTransactionalAnnotation != null) {
return handleGlobalTransaction(methodInvocation, globalTransactionalAnnotation); // @1
} else if (globalLockAnnotation != null) {
return handleGlobalLock(methodInvocation); // @2
}
}
}
return methodInvocation.proceed();
}
代码@1: @GlobalTransactional事务注解修饰的方法,会调用handleGlobalTransaction处理事务请求
代码@2: @GlobalLock注解修饰的方法,不用交给事务管理器处理,保证对数据的修改能够加入到seata机制当中;