Spring Cloud 整合分佈式事務Seata客戶端啓動TM源碼分析

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();
    }

其他的內容後面再補充

發佈了25 篇原創文章 · 獲贊 0 · 訪問量 7808
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章