Spring cloud 整合分布式事务Seata客户端可以参考之前文章
下面是分析客户端的TM的初始化过程
下面是Spring cloud Seata客户端启动过程日志
2019-12-30 10:39:16.898 INFO 2588 --- [ main] com.cloud.tcc.OrderServerApplication : No active profile set, falling back to default profiles: default
2019-12-30 10:39:17.576 INFO 2588 --- [ main] o.s.cloud.context.scope.GenericScope : BeanFactory id=fc323f92-062a-3709-a80b-f9f296632043
2019-12-30 10:39:17.634 INFO 2588 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'spring.cloud.alibaba.seata-com.alibaba.cloud.seata.SeataProperties' of type [com.alibaba.cloud.seata.SeataProperties] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2019-12-30 10:39:17.635 INFO 2588 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'com.alibaba.cloud.seata.GlobalTransactionAutoConfiguration' of type [com.alibaba.cloud.seata.GlobalTransactionAutoConfiguration$$EnhancerBySpringCGLIB$$67c0a1f6] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2019-12-30 10:39:17.678 INFO 2588 --- [ main] io.seata.config.FileConfiguration : The file name of the operation is registry.conf
2019-12-30 10:39:17.737 WARN 2588 --- [ main] io.seata.config.ConfigurationFactory : failed to load extConfiguration:not found service provider for : io.seata.config.ExtConfigurationProvider[null] and classloader : sun.misc.Launcher$AppClassLoader@18b4aac2
io.seata.common.loader.EnhancedServiceNotFoundException: not found service provider for : io.seata.config.ExtConfigurationProvider[null] and classloader : sun.misc.Launcher$AppClassLoader@18b4aac2
2019-12-30 10:39:17.741 INFO 2588 --- [ main] io.seata.config.FileConfiguration : The file name of the operation is file.conf
2019-12-30 10:39:17.753 WARN 2588 --- [ main] io.seata.config.ConfigurationFactory : failed to load extConfiguration:not found service provider for : io.seata.config.ExtConfigurationProvider[null] and classloader : sun.misc.Launcher$AppClassLoader@18b4aac2
io.seata.common.loader.EnhancedServiceNotFoundException: not found service provider for : io.seata.config.ExtConfigurationProvider[null] and classloader : sun.misc.Launcher$AppClassLoader@18b4aac2
2019-12-30 10:39:17.756 INFO 2588 --- [ main] i.s.s.a.GlobalTransactionScanner : Initializing Global Transaction Clients ...
2019-12-30 10:39:17.867 INFO 2588 --- [ main] i.s.c.r.netty.AbstractRpcRemotingClient : RpcClientBootstrap has started
2019-12-30 10:39:17.869 INFO 2588 --- [ main] i.s.s.a.GlobalTransactionScanner : Transaction Manager Client is initialized. applicationId[order-server] txServiceGroup[fsp_tx_group]
2019-12-30 10:39:17.879 INFO 2588 --- [ main] io.seata.rm.datasource.AsyncWorker : Async Commit Buffer Limit: 10000
2019-12-30 10:39:17.884 INFO 2588 --- [ main] i.s.c.r.netty.AbstractRpcRemotingClient : RpcClientBootstrap has started
2019-12-30 10:39:17.885 INFO 2588 --- [ main] i.s.s.a.GlobalTransactionScanner : Resource Manager is initialized. applicationId[order-server] txServiceGroup[fsp_tx_group]
2019-12-30 10:39:17.885 INFO 2588 --- [ main] i.s.s.a.GlobalTransactionScanner : Global Transaction Clients are initialized.
2019-12-30 10:39:17.916 INFO 2588 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration' of type [org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2019-12-30 10:39:17.937 INFO 2588 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'com.alibaba.cloud.seata.feign.SeataFeignClientAutoConfiguration$FeignBeanPostProcessorConfiguration' of type [com.alibaba.cloud.seata.feign.SeataFeignClientAutoConfiguration$FeignBeanPostProcessorConfiguration$$EnhancerBySpringCGLIB$$c7a62141] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2019-12-30 10:39:17.941 INFO 2588 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'seataFeignObjectWrapper' of type [com.alibaba.cloud.seata.feign.SeataFeignObjectWrapper] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2019-12-30 10:39:17.942 INFO 2588 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration' of type [org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration$$EnhancerBySpringCGLIB$$f6ae9b78] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2019-12-30 10:39:18.155 INFO 2588 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8180 (http)
2019-12-30 10:39:18.164 INFO 2588 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2019-12-30 10:39:18.164 INFO 2588 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.27]
2019-12-30 10:39:18.333 INFO 2588 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2019-12-30 10:39:18.333 INFO 2588 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1414 ms
从上面的Spring cloud Seata的启动日志可以看出先加载相关的ConfigurationFactory和FileConfiguration加载出相关的配置文件,主要是读取file.conf和register.conf文件,这二个文件是在客户的resource目录下,在整合的时候要放到类路径下,ConfigurationFactory和FileConfiguration的相关的源码如下:
**
* The type Configuration factory.
* 配置文件工厂对象
* @author slievrly
* @author Geng Zhang
*/
public final class ConfigurationFactory {
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationFactory.class);
private static final String REGISTRY_CONF_PREFIX = "registry";
private static final String REGISTRY_CONF_SUFFIX = ".conf";
private static final String ENV_SYSTEM_KEY = "SEATA_ENV";
public static final String ENV_PROPERTY_KEY = "seataEnv";
private static final String SYSTEM_PROPERTY_SEATA_CONFIG_NAME = "seata.config.name";
private static final String ENV_SEATA_CONFIG_NAME = "SEATA_CONFIG_NAME";
public static final Configuration CURRENT_FILE_INSTANCE;
static {
String seataConfigName = System.getProperty(SYSTEM_PROPERTY_SEATA_CONFIG_NAME);
if (null == seataConfigName) {
seataConfigName = System.getenv(ENV_SEATA_CONFIG_NAME);
}
if (null == seataConfigName) {
seataConfigName = REGISTRY_CONF_PREFIX;
}
String envValue = System.getProperty(ENV_PROPERTY_KEY);
if (null == envValue) {
envValue = System.getenv(ENV_SYSTEM_KEY);
}
Configuration configuration = (null == envValue) ? new FileConfiguration(seataConfigName + REGISTRY_CONF_SUFFIX,
false) : new FileConfiguration(seataConfigName + "-" + envValue + REGISTRY_CONF_SUFFIX, false);
Configuration extConfiguration = null;
try {
extConfiguration = EnhancedServiceLoader.load(ExtConfigurationProvider.class).provide(configuration);
if (LOGGER.isInfoEnabled()) {
LOGGER.info("load extConfiguration:{}",
extConfiguration == null ? null : extConfiguration.getClass().getSimpleName());
}
} catch (Exception e) {
LOGGER.warn("failed to load extConfiguration:{}", e.getMessage(), e);
}
CURRENT_FILE_INSTANCE = null == extConfiguration ? configuration : extConfiguration;
}
private static final String NAME_KEY = "name";
private static final String FILE_TYPE = "file";
private static volatile Configuration instance = null;
/**
* Gets instance.
*
* @return the instance
*/
public static Configuration getInstance() {
if (instance == null) {
synchronized (Configuration.class) {
if (instance == null) {
instance = buildConfiguration();
}
}
}
return instance;
}
private static Configuration buildConfiguration() {
ConfigType configType = null;
String configTypeName = null;
try {
configTypeName = CURRENT_FILE_INSTANCE.getConfig(
ConfigurationKeys.FILE_ROOT_CONFIG + ConfigurationKeys.FILE_CONFIG_SPLIT_CHAR
+ ConfigurationKeys.FILE_ROOT_TYPE);
configType = ConfigType.getType(configTypeName);
} catch (Exception e) {
throw new NotSupportYetException("not support register type: " + configTypeName, e);
}
if (ConfigType.File == configType) {
String pathDataId = ConfigurationKeys.FILE_ROOT_CONFIG + ConfigurationKeys.FILE_CONFIG_SPLIT_CHAR
+ FILE_TYPE + ConfigurationKeys.FILE_CONFIG_SPLIT_CHAR + NAME_KEY;
String name = CURRENT_FILE_INSTANCE.getConfig(pathDataId);
//创建出对象
Configuration configuration = new FileConfiguration(name);
Configuration extConfiguration = null;
try {
//使用类加载器加载相关的配置ExtConfigurationProvider对象,如果是Spring boot stater则有相关的ExtConfigurationProvider
//的实现类,Spring cloud starter则没有相关的ExtConfigurationProvider的实现类
extConfiguration = EnhancedServiceLoader.load(ExtConfigurationProvider.class).provide(configuration);
if (LOGGER.isInfoEnabled()) {
LOGGER.info("load extConfiguration:{}",
extConfiguration == null ? null : extConfiguration.getClass().getSimpleName());
}
} catch (Exception e) {
LOGGER.warn("failed to load extConfiguration:{}", e.getMessage(), e);
}
return null == extConfiguration ? configuration : extConfiguration;
} else {
//进去加载相关的配置
return EnhancedServiceLoader.load(ConfigurationProvider.class, Objects.requireNonNull(configType).name())
.provide();
}
}
}
FileConfiguration的源码如下:
/**
* The type FileConfiguration.
* 文件配置类信息,主要是进行解析registry.conf和file.conf文件,去取出这二个文件的内容
*/
public class FileConfiguration extends AbstractConfiguration {
private static final Logger LOGGER = LoggerFactory.getLogger(FileConfiguration.class);
private Config fileConfig;
private ExecutorService configOperateExecutor;
private static final int CORE_CONFIG_OPERATE_THREAD = 1;
private static final int MAX_CONFIG_OPERATE_THREAD = 2;
private static final long LISTENER_CONFIG_INTERNAL = 1 * 1000;
private static final String REGISTRY_TYPE = "file";
private static final String SYS_FILE_RESOURCE_PREFIX = "file:";
private final ConcurrentMap<String, Set<ConfigurationChangeListener>> configListenersMap = new ConcurrentHashMap<>(
8);
private final Map<String, String> listenedConfigMap = new HashMap<>(8);
private final String targetFilePath;
private volatile long targetFileLastModified;
private final String name;
private final boolean allowDynamicRefresh;
/**
* Note that:this constructor is only used to create proxy with CGLIB
* see io.seata.spring.boot.autoconfigure.provider.SpringBootConfigurationProvider#provide
*/
public FileConfiguration() {
this.name = null;
this.targetFilePath = null;
this.allowDynamicRefresh = false;
}
/**
* Instantiates a new File configuration.
*
* @param name the name
*/
public FileConfiguration(String name) {
this(name, true);
}
/**
* Instantiates a new File configuration.
* 实例化一个文件配置,Spring cloud 应用启动的时候会自动创建这个对象,进行初始化相关的操作
* @param name the name 默认的registry.conf和file.conf
* @param allowDynamicRefresh the allow dynamic refresh
*/
public FileConfiguration(String name, boolean allowDynamicRefresh) {
//INFO 8504 --- [ main] io.seata.config.FileConfiguration : The file name of the operation is registry.conf
LOGGER.info("The file name of the operation is {}", name);
if (null == name) {
throw new IllegalArgumentException("name can't be null");
} else if (name.startsWith(SYS_FILE_RESOURCE_PREFIX)) {
File targetFile = new File(name.substring(SYS_FILE_RESOURCE_PREFIX.length()));
if (targetFile.exists()) {
targetFilePath = targetFile.getPath();
Config appConfig = ConfigFactory.parseFileAnySyntax(targetFile);
fileConfig = ConfigFactory.load(appConfig);
} else {
targetFilePath = null;
}
} else {
//应用程序执行到这里来
URL resource = this.getClass().getClassLoader().getResource(name);
if (null != resource) {
targetFilePath = resource.getPath();
//进行解析配置文件的内容到fileConfig对象当中
fileConfig = ConfigFactory.load(name);
} else {
targetFilePath = null;
}
}
/**
* 对于seata-server,这个配置一定要存在
* 对于应该程序,用seata-spring-boot-starter 的时候配置文件可以不存在
*/
if (null == targetFilePath) {
fileConfig = ConfigFactory.load();
this.allowDynamicRefresh = false;
} else {
targetFileLastModified = new File(targetFilePath).lastModified();
this.allowDynamicRefresh = allowDynamicRefresh;
}
this.name = name;
//新开一个线程去定时检查配置文件
configOperateExecutor = new ThreadPoolExecutor(CORE_CONFIG_OPERATE_THREAD, MAX_CONFIG_OPERATE_THREAD,
Integer.MAX_VALUE, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(),
new NamedThreadFactory("configOperate", MAX_CONFIG_OPERATE_THREAD));
}
@Override
public String getConfig(String dataId, String defaultValue, long timeoutMills) {
String value;
if ((value = getConfigFromSysPro(dataId)) != null) {
return value;
}
ConfigFuture configFuture = new ConfigFuture(dataId, defaultValue, ConfigOperation.GET, timeoutMills);
configOperateExecutor.submit(new ConfigOperateRunnable(configFuture));
return (String)configFuture.get();
}
@Override
public boolean putConfig(String dataId, String content, long timeoutMills) {
ConfigFuture configFuture = new ConfigFuture(dataId, content, ConfigOperation.PUT, timeoutMills);
configOperateExecutor.submit(new ConfigOperateRunnable(configFuture));
return (Boolean)configFuture.get();
}
@Override
public boolean putConfigIfAbsent(String dataId, String content, long timeoutMills) {
ConfigFuture configFuture = new ConfigFuture(dataId, content, ConfigOperation.PUTIFABSENT, timeoutMills);
configOperateExecutor.submit(new ConfigOperateRunnable(configFuture));
return (Boolean)configFuture.get();
}
@Override
public boolean removeConfig(String dataId, long timeoutMills) {
ConfigFuture configFuture = new ConfigFuture(dataId, null, ConfigOperation.REMOVE, timeoutMills);
configOperateExecutor.submit(new ConfigOperateRunnable(configFuture));
return (Boolean)configFuture.get();
}
@Override
public void addConfigListener(String dataId, ConfigurationChangeListener listener) {
if (null == dataId || null == listener) {
return;
}
configListenersMap.putIfAbsent(dataId, new ConcurrentSet<>());
configListenersMap.get(dataId).add(listener);
listenedConfigMap.put(dataId, getConfig(dataId));
FileListener fileListener = new FileListener(dataId, listener);
fileListener.onProcessEvent(new ConfigurationChangeEvent());
}
@Override
public void removeConfigListener(String dataId, ConfigurationChangeListener listener) {
Set<ConfigurationChangeListener> configChangeListeners = getConfigListeners(dataId);
if (dataId == null || configChangeListeners == null) {
return;
}
if (configListenersMap.containsKey(dataId)) {
configListenersMap.get(dataId).remove(listener);
if (configListenersMap.get(dataId).isEmpty()) {
configListenersMap.remove(dataId);
listenedConfigMap.remove(dataId);
}
}
listener.onShutDown();
}
@Override
public Set<ConfigurationChangeListener> getConfigListeners(String dataId) {
return configListenersMap.get(dataId);
}
@Override
public String getTypeName() {
return REGISTRY_TYPE;
}
/**
* The type Config operate runnable.
* 定时检查配置更新线程
*
*/
class ConfigOperateRunnable implements Runnable {
private ConfigFuture configFuture;
/**
* Instantiates a new Config operate runnable.
*
* @param configFuture the config future
*/
public ConfigOperateRunnable(ConfigFuture configFuture) {
this.configFuture = configFuture;
}
@Override
public void run() {
if (null != configFuture) {
if (configFuture.isTimeout()) {
setFailResult(configFuture);
return;
}
try {
if (allowDynamicRefresh) {
long tempLastModified = new File(targetFilePath).lastModified();
if (tempLastModified > targetFileLastModified) {
Config tempConfig;
if (name.startsWith(SYS_FILE_RESOURCE_PREFIX)) {
Config appConfig = ConfigFactory.parseFileAnySyntax(new File(targetFilePath));
tempConfig = ConfigFactory.load(appConfig);
} else {
tempConfig = ConfigFactory.load(name);
}
if (null != tempConfig) {
fileConfig = tempConfig;
targetFileLastModified = tempLastModified;
}
}
}
if (configFuture.getOperation() == ConfigOperation.GET) {
String result = fileConfig.getString(configFuture.getDataId());
configFuture.setResult(result);
} else if (configFuture.getOperation() == ConfigOperation.PUT) {
//todo
configFuture.setResult(Boolean.TRUE);
} else if (configFuture.getOperation() == ConfigOperation.PUTIFABSENT) {
//todo
configFuture.setResult(Boolean.TRUE);
} else if (configFuture.getOperation() == ConfigOperation.REMOVE) {
//todo
configFuture.setResult(Boolean.TRUE);
}
} catch (Exception e) {
setFailResult(configFuture);
LOGGER.warn("Could not found property {}, try to use default value instead.",
configFuture.getDataId());
}
}
}
private void setFailResult(ConfigFuture configFuture) {
if (configFuture.getOperation() == ConfigOperation.GET) {
String result = configFuture.getContent();
configFuture.setResult(result);
} else {
configFuture.setResult(Boolean.FALSE);
}
}
}
/**
* The type FileListener.
*
* 配置文件监听器
*/
class FileListener implements ConfigurationChangeListener {
private final String dataId;
private final ConfigurationChangeListener listener;
private final ExecutorService executor = new ThreadPoolExecutor(CORE_LISTENER_THREAD, MAX_LISTENER_THREAD, 0L,
TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(),
new NamedThreadFactory("fileListener", MAX_LISTENER_THREAD));
/**
* Instantiates a new FileListener.
*
* @param dataId the data id
* @param listener the listener
*/
public FileListener(String dataId, ConfigurationChangeListener listener) {
this.dataId = dataId;
this.listener = listener;
}
@Override
public void onChangeEvent(ConfigurationChangeEvent event) {
while (true) {
try {
String currentConfig = getConfig(dataId);
String oldConfig = listenedConfigMap.get(dataId);
if (ObjectUtils.notEqual(currentConfig, oldConfig)) {
listenedConfigMap.put(dataId, currentConfig);
event.setDataId(dataId).setNewValue(currentConfig).setOldValue(oldConfig);
listener.onChangeEvent(event);
}
Thread.sleep(LISTENER_CONFIG_INTERNAL);
} catch (Exception exx) {
LOGGER.error("fileListener execute error:{}", exx.getMessage(), exx);
}
}
}
@Override
public ExecutorService getExecutorService() {
return executor;
}
}
}
因为项目当中引入了spring-cloud-alibaba-seata 的pom文件,图片如下
他本身是一个标准的Spring boot starter,从spring.factoris文件当中可以看到他默认启动的配置类有5个,现在主要是看全局事务的主要配置类GlobalTransactionAutoConfiguration,这个类当中主要是配置了全局事务的扫描器GlobalTransactionScanner
@Configuration
@EnableConfigurationProperties(SeataProperties.class)
public class GlobalTransactionAutoConfiguration {
private final ApplicationContext applicationContext;
private final SeataProperties seataProperties;
public GlobalTransactionAutoConfiguration(ApplicationContext applicationContext,
SeataProperties seataProperties) {
this.applicationContext = applicationContext;
this.seataProperties = seataProperties;
}
@Bean
public GlobalTransactionScanner globalTransactionScanner() {
String applicationName = applicationContext.getEnvironment()
.getProperty("spring.application.name");
//seataProperties封装了application.yml配置文件当中的内容一个
String txServiceGroup = seataProperties.getTxServiceGroup();
//如果在application.yml文件没有配置这个,那么则就默认用应用名称,加-fescar-service-group
if (StringUtils.isEmpty(txServiceGroup)) {
txServiceGroup = applicationName + "-fescar-service-group";
seataProperties.setTxServiceGroup(txServiceGroup);
}
//创建一个全局事务扫描器
return new GlobalTransactionScanner(applicationName, txServiceGroup);
}
}
GlobalTransactionScanner类是主要是初始化TM和
**
* TM 类
* The type Global transaction scanner.
* 不管是和Spring Boot整合,还是和Spring Cloud整合,都是要把这个类给Spring 容器注入进去
*
* GlobalTransactionScanner类实现了InitializingBean和ApplicationContextAware
* ApplicationContextAware这个类可以让GlobalTransactionScanner获取到Spring容器的上下文容器对象
* InitializingBean可以在创建完这个GlobalTransactionScanner对象之后会自动执行到afterPropertiesSet这个方法
*
* initClient()就是在afterPropertiesSet()方法当中调用的,也就是在GlobalTransactionScanner创建完成之后就会调用这个
* initClient()方法
*
* @author slievrly
*/
public class GlobalTransactionScanner extends AbstractAutoProxyCreator
implements InitializingBean, ApplicationContextAware,
DisposableBean {
/**
*
*/
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = LoggerFactory.getLogger(GlobalTransactionScanner.class);
private static final int AT_MODE = 1;
private static final int MT_MODE = 2;
private static final int ORDER_NUM = 1024;
private static final int DEFAULT_MODE = AT_MODE + MT_MODE;
private static final Set<String> PROXYED_SET = new HashSet<>();
private static final FailureHandler DEFAULT_FAIL_HANDLER = new DefaultFailureHandlerImpl();
private MethodInterceptor interceptor;
private final String applicationId;
private final String txServiceGroup;
private final int mode;
private final boolean disableGlobalTransaction = ConfigurationFactory.getInstance().getBoolean(
ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION, false);
private final FailureHandler failureHandlerHook;
private ApplicationContext applicationContext;
/**
* Instantiates a new Global transaction scanner.
*
* @param txServiceGroup the tx service group
*/
public GlobalTransactionScanner(String txServiceGroup) {
this(txServiceGroup, txServiceGroup, DEFAULT_MODE);
}
/**
* Instantiates a new Global transaction scanner.
*
* @param txServiceGroup the tx service group
* @param mode the mode
*/
public GlobalTransactionScanner(String txServiceGroup, int mode) {
this(txServiceGroup, txServiceGroup, mode);
}
/**
* Instantiates a new Global transaction scanner.
*
* @param applicationId the application id
* @param txServiceGroup the default server group
*/
public GlobalTransactionScanner(String applicationId, String txServiceGroup) {
this(applicationId, txServiceGroup, DEFAULT_MODE);
}
/**
* Instantiates a new Global transaction scanner.
*
* @param applicationId the application id
* @param txServiceGroup the tx service group
* @param mode the mode
*/
public GlobalTransactionScanner(String applicationId, String txServiceGroup, int mode) {
this(applicationId, txServiceGroup, mode, DEFAULT_FAIL_HANDLER);
}
/**
* Instantiates a new Global transaction scanner.
*
* @param applicationId the application id
* @param txServiceGroup the tx service group
* @param failureHandlerHook the failure handler hook
*/
public GlobalTransactionScanner(String applicationId, String txServiceGroup, FailureHandler failureHandlerHook) {
this(applicationId, txServiceGroup, DEFAULT_MODE, failureHandlerHook);
}
/**
* Instantiates a new Global transaction scanner.
*
* @param applicationId the application id
* @param txServiceGroup the tx service group
* @param mode the mode
* @param failureHandlerHook the failure handler hook
*/
public GlobalTransactionScanner(String applicationId, String txServiceGroup, int mode,
FailureHandler failureHandlerHook) {
setOrder(ORDER_NUM);
setProxyTargetClass(true);
this.applicationId = applicationId;
this.txServiceGroup = txServiceGroup;
this.mode = mode;
this.failureHandlerHook = failureHandlerHook;
}
@Override
public void destroy() {
ShutdownHook.getInstance().destroyAll();
}
/**
*
* TM的核心初始化操作
* tm和netty的客户端的启动操作都在这里实现
*/
private void initClient() {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Initializing Global Transaction Clients ... ");
}
if (StringUtils.isNullOrEmpty(applicationId) || StringUtils.isNullOrEmpty(txServiceGroup)) {
throw new IllegalArgumentException(
"applicationId: " + applicationId + ", txServiceGroup: " + txServiceGroup);
}
//TM的初始化,启动Netty客户端,向Seata服务端发送注册请求
TMClient.init(applicationId, txServiceGroup);
if (LOGGER.isInfoEnabled()) {
LOGGER.info(
"Transaction Manager Client is initialized. applicationId[" + applicationId + "] txServiceGroup["
+ txServiceGroup + "]");
}
//RM的初始化方法,向Seata服务端发送注册消息
RMClient.init(applicationId, txServiceGroup);
if (LOGGER.isInfoEnabled()) {
LOGGER.info(
"Resource Manager is initialized. applicationId[" + applicationId + "] txServiceGroup[" + txServiceGroup
+ "]");
}
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Global Transaction Clients are initialized. ");
}
//注册Spring boot 关闭的回调方法
registerSpringShutdownHook();
}
private void registerSpringShutdownHook() {
if (applicationContext instanceof ConfigurableApplicationContext) {
((ConfigurableApplicationContext) applicationContext).registerShutdownHook();
ShutdownHook.removeRuntimeShutdownHook();
}
ShutdownHook.getInstance().addDisposable(TmRpcClient.getInstance(applicationId, txServiceGroup));
ShutdownHook.getInstance().addDisposable(RmRpcClient.getInstance(applicationId, txServiceGroup));
}
@Override
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (disableGlobalTransaction) {
return bean;
}
try {
synchronized (PROXYED_SET) {
if (PROXYED_SET.contains(beanName)) {
return bean;
}
interceptor = null;
//check TCC proxy
if (TCCBeanParserUtils.isTccAutoProxy(bean, beanName, applicationContext)) {
//TCC interceptor, proxy bean of sofa:reference/dubbo:reference, and LocalTCC
interceptor = new TccActionInterceptor(TCCBeanParserUtils.getRemotingDesc(beanName));
} else {
Class<?> serviceInterface = SpringProxyUtils.findTargetClass(bean);
Class<?>[] interfacesIfJdk = SpringProxyUtils.findInterfaces(bean);
if (!existsAnnotation(new Class[]{serviceInterface})
&& !existsAnnotation(interfacesIfJdk)) {
return bean;
}
if (interceptor == null) {
interceptor = new GlobalTransactionalInterceptor(failureHandlerHook);
ConfigurationFactory.getInstance().addConfigListener(ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION,(ConfigurationChangeListener)interceptor);
}
}
LOGGER.info(
"Bean[" + bean.getClass().getName() + "] with name [" + beanName + "] would use interceptor ["
+ interceptor.getClass().getName() + "]");
if (!AopUtils.isAopProxy(bean)) {
bean = super.wrapIfNecessary(bean, beanName, cacheKey);
} else {
AdvisedSupport advised = SpringProxyUtils.getAdvisedSupport(bean);
Advisor[] advisor = buildAdvisors(beanName, getAdvicesAndAdvisorsForBean(null, null, null));
for (Advisor avr : advisor) {
advised.addAdvisor(0, avr);
}
}
PROXYED_SET.add(beanName);
return bean;
}
} catch (Exception exx) {
throw new RuntimeException(exx);
}
}
private boolean existsAnnotation(Class<?>[] classes) {
if (classes != null && classes.length > 0) {
for (Class clazz : classes) {
if (clazz == null) {
continue;
}
Method[] methods = clazz.getMethods();
for (Method method : methods) {
GlobalTransactional trxAnno = method.getAnnotation(GlobalTransactional.class);
if (trxAnno != null) {
return true;
}
GlobalLock lockAnno = method.getAnnotation(GlobalLock.class);
if (lockAnno != null) {
return true;
}
}
}
}
return false;
}
private MethodDesc makeMethodDesc(GlobalTransactional anno, Method method) {
return new MethodDesc(anno, method);
}
@Override
protected Object[] getAdvicesAndAdvisorsForBean(Class beanClass, String beanName, TargetSource customTargetSource)
throws BeansException {
return new Object[]{interceptor};
}
@Override
public void afterPropertiesSet() {
if (disableGlobalTransaction) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Global transaction is disabled.");
}
return;
}
initClient();
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
this.setBeanFactory(applicationContext);
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
if (bean instanceof DataSourceProxy && ConfigurationFactory.getInstance().getBoolean(DATASOURCE_AUTOPROXY, false)) {
throw new ShouldNeverHappenException("Auto proxy of DataSource can't be enabled as you've created a DataSourceProxy bean." +
"Please consider removing DataSourceProxy bean or disabling auto proxy of DataSource.");
}
return super.postProcessBeforeInitialization(bean, beanName);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof DataSource && !(bean instanceof DataSourceProxy) && ConfigurationFactory.getInstance().getBoolean(DATASOURCE_AUTOPROXY, false)) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Auto proxy of [{}]", beanName);
}
DataSourceProxy dataSourceProxy = DataSourceProxyHolder.get().putDataSource((DataSource) bean);
Class<?>[] interfaces = SpringProxyUtils.getAllInterfaces(bean);
return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), interfaces, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Method m = BeanUtils.findDeclaredMethod(DataSourceProxy.class, method.getName(), method.getParameterTypes());
if (null != m) {
return m.invoke(dataSourceProxy, args);
} else {
boolean oldAccessible = method.isAccessible();
try {
method.setAccessible(true);
return method.invoke(bean, args);
} finally {
//recover the original accessible for security reason
method.setAccessible(oldAccessible);
}
}
}
});
}
return super.postProcessAfterInitialization(bean, beanName);
}
}
TM的客户端源码如下:
/**
* The type Tm client.
* TM的客户端,也可以理解为全局事务管理器
*/
public class TMClient {
/**
* Init.
*
* @param applicationId the application id
* @param transactionServiceGroup the transaction service group
*/
public static void init(String applicationId, String transactionServiceGroup) {
//获取TM实例
TmRpcClient tmRpcClient = TmRpcClient.getInstance(applicationId, transactionServiceGroup);
//初始化tm
tmRpcClient.init();
}
}
启动代码
/**
* 客户端启动和具体的信息绑定请求
*/
@Override
public void init() {
//启动Netty客户端,里面是标准的Netty的客户端启动代码
clientBootstrap.start();
//定时重新A
timerExecutor.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
clientChannelManager.reconnect(getTransactionServiceGroup());
}
}, SCHEDULE_INTERVAL_MILLS, SCHEDULE_INTERVAL_MILLS, TimeUnit.SECONDS);
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());
}
super.init();
}
其他的内容后面再补充