seata 客户端启动

Seata Client 启动

Seata Server 启动了解了, 现在来看一下Seata Client的启动, 需要对SpringBoot 有一些了解

因为Seata Server 解析的是1.5.1, 那么Seata Client 也解析1.5.1

引入Seata

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>

因为引入了seata 的starter, 所以会自动引入一些Jar包如下

因为SpringBoot 自动装配的时候会加载Jar 下的spring.factories 中实现EnableAutoConfiguration的类, 这也是我们分析一些框架源码的入口

spring-cloud-starter-alibaba-seata-2.2.8.RELEASE.jar

spring-cloud-starter-alibaba-seata 中的spring.factories总共有4个类实现了EnableAutoConfiguration 接口: restTemplate,handlerInteceptor,feignClient,Hystrix相关的

SeataRestTemplateAutoConfiguration

从上述的代码中可以看出来,在SeataRestTemplateAutoConfiguration 生成Bean 的时候会调用init方法,在init方法中,会将一个interceptor增加到restTemplate中, restTemplate 发起请求的时候,会优先调用这个interceptor, 而SeataRestTemplateInterceptor 是在一个配置类中生成的,可以注入到SeataRestTemplateAutoConfiguration

SeataRestTemplateInterceptor

可以看到在SeataRestTemplateInterceptor并没有进行很复杂的操作,而是从seata的全局上下文中获取到当前事务的全局事务Id, 然后添加到Header中,这样就可以让全局事务ID 在各个微服务之间进行流转

SeataHandlerInterceptorConfiguration

SeataHandlerInterceptorConfiguration 也是添加了一个拦截器, 不过是对SpringMVC进行拦截,拦截器是SeataHandlerInterceptor

SeataHandlerInterceptor

SeataFeignClientAutoConfiguration

SeataFeignClientAutoConfiguration 是利用构造器模式生成SeataFeignClient,同时支持hystrix, sentinel和普通模式

SeataFeignClient

我们来看一下execute方法,在方法中会从上下文中获取到全局事务Xid,然后将xid放到请求头中

SeataHystrixAutoConfiguration

Hystrix 会将请求包装成Command,然后将Command由线程交给请求框架执行

seata-spring-autoconfigure-client-1.5.1.jar

下面来看一下seata-spring-autoconfigure-client-1.5.1.jarspring.factories

client自动装配存在两个类,一个是SeataTCCFenceAutoConfiguration,一个是SeataClientEnvironmentPostProcessorSeataClientEnvironmentPostProcessor 主要是装配seataClient 的一些配置, SeataTCCFenceAutoConfiguration主要是装配TCC模式的一些配置

SeataTCCFenceAutoConfiguration

SeataClientEnvironmentPostProcessor

seata-spring-autoconfigure-core-1.5.1.jar

下面来看一下seata-spring-autoconfigure-core-1.5.1.jarspring.factories

seata 核心类中也存在两个自动装配类

SeataCoreAutoConfiguration

SpringApplicationContextProvider 实现了ApplicationContextAware, 如果对spring 生命周期有些了解的话,会知道ApplicationContextAware主要是用于方便获取到springApplicationContext上下文

SeataCoreEnvironmentPostProcessor

SeataCoreEnvironmentPostProcessorinit初始化的时候会将seata的配置,注册等信息全部保存到一个Map中,为了保证线程安全,这里用CAS的模式进行添加

seata-spring-boot-starter-1.5.1.jar

seata-spring-boot-starterspring.factory中的自动装配类就比较多了,包括seata数据库代理,seata, http请求seataSaga模式这4中自动装配

SeataDataSourceAutoConfiguration

SeataDataSourceAutoConfiguration中比较简单就是创建一个SeataAutoDataSourceProxyCreatorbean,SeataAutoDataSourceProxyCreatorAbstractAutoProxyCreator的子类,主要是实现SeataAT模式数据库代理的切面

SeataAutoDataSourceProxyCreator
public class SeataAutoDataSourceProxyCreator extends AbstractAutoProxyCreator {

    private static final Logger LOGGER = LoggerFactory.getLogger(SeataAutoDataSourceProxyCreator.class);

    private final Set<String> excludes;

    private final String dataSourceProxyMode;

    private final Object[] advisors;

    public SeataAutoDataSourceProxyCreator(boolean useJdkProxy, String[] excludes, String dataSourceProxyMode) {
        setProxyTargetClass(!useJdkProxy);
        this.excludes = new HashSet<>(Arrays.asList(excludes));
        this.dataSourceProxyMode = dataSourceProxyMode;
        this.advisors = buildAdvisors(dataSourceProxyMode);
    }

    // 将需要代理的对象方法注解构建Advice调用链,如果执行到方法有被指定注解修饰,那么最终会调用advice 链条中的invoke 方法
    private Object[] buildAdvisors(String dataSourceProxyMode) {
        Advice advice = new SeataAutoDataSourceProxyAdvice(dataSourceProxyMode);
        return new Object[]{new DefaultIntroductionAdvisor(advice)};
    }

    @Override
    protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource customTargetSource) {
        return advisors;
    }

    @Override
    protected boolean shouldSkip(Class<?> beanClass, String beanName) {
        if (excludes.contains(beanClass.getName())) {
            return true;
        }
        return SeataProxy.class.isAssignableFrom(beanClass);
    }

    @Override
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        // we only care DataSource bean
        if (!(bean instanceof DataSource)) {
            return bean;
        }

        // when this bean is just a simple DataSource, not SeataDataSourceProxy
        if (!(bean instanceof SeataDataSourceProxy)) {
            Object enhancer = super.wrapIfNecessary(bean, beanName, cacheKey);
            if (bean == enhancer) {
                return bean;
            }
            // 构建数据库代理,将代理对象储存在map中
            DataSource origin = (DataSource) bean;
            SeataDataSourceProxy proxy = buildProxy(origin, dataSourceProxyMode);
            DataSourceProxyHolder.put(origin, proxy);
            return enhancer;
        }

        LOGGER.warn("Manually register SeataDataSourceProxy(or its subclass) bean is discouraged! bean name: {}", beanName);
        SeataDataSourceProxy proxy = (SeataDataSourceProxy) bean;
        DataSource origin = proxy.getTargetDataSource();
        Object originEnhancer = super.wrapIfNecessary(origin, beanName, cacheKey);
        // this mean origin is either excluded by user or had been proxy before
        if (origin == originEnhancer) {
            return origin;
        }
        // else, put <origin, proxy> to holder and return originEnhancer
        DataSourceProxyHolder.put(origin, proxy);
        return originEnhancer;
    }

    SeataDataSourceProxy buildProxy(DataSource origin, String proxyMode) {
        // AT模式
        if (BranchType.AT.name().equalsIgnoreCase(proxyMode)) {
            return new DataSourceProxy(origin);
        }
        // XA 模式
        if (BranchType.XA.name().equalsIgnoreCase(proxyMode)) {
            return new DataSourceProxyXA(origin);
        }
        throw new IllegalArgumentException("Unknown dataSourceProxyMode: " + proxyMode);
    }
}

SeataAutoConfiguration

SeataAutoConfiguration主要是生成了2个bean, 一个是FailureHandler,这个是事务处理的类, 一个是GlobalTransactionScanner,这个类是一个相当重要的类,TMClient, RMClient 对象都在GlobalTransactionScanner 进行初始化

FailureHandler

DefaultFailureHandlerImplFailureHandler的默认实现类,主要包括commit失败,rollback失败和重试策略,DefaultFailureHandlerImpl 这里的处理的逻辑比较简单,就是单纯打印日志,一般需要重新实现FailureHandler实现一些自定义的逻辑

GlobalTransactionScanner
public class GlobalTransactionScanner extends AbstractAutoProxyCreator
        implements ConfigurationChangeListener, InitializingBean, ApplicationContextAware, DisposableBean {

我们来看一下GlobalTransactionScanner 实现了InitializingBean, 那么在加入到Spring中的时候会调用afterPropertiesSet 方法,然后在方法中调用initClient会实例化TmClient,RmClient

public void afterPropertiesSet() {
    if (disableGlobalTransaction) {
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Global transaction is disabled.");
        }
        ConfigurationCache.addConfigListener(ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION,
                                             (ConfigurationChangeListener)this);
        return;
    }
    // 初始化seata client, 为了线程安全使用了CAS
    if (initialized.compareAndSet(false, true)) {
        initClient();
    }
}


private void initClient() {
    if (LOGGER.isInfoEnabled()) {
        LOGGER.info("Initializing Global Transaction Clients ... ");
    }
    if (DEFAULT_TX_GROUP_OLD.equals(txServiceGroup)) {
        ...
    }
    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();

}

TMClient.init

init方法中会生成一个netty的远程连接对象

TmNettyRemotingClient#init

AbstractNettyRemotingClient#init

clientBootstrap.start启动netty client

RMClient#init

RMClient 同样使用NettyClient进行连接

RmNettyRemotingClient#init

RmNettyRemotingClientTmNettyRemotingClient 都是AbstractNettyRemotingClient的子类,super.init() 方法是相同的,都是利用启动netty客户端

HttpAutoConfiguration

HttpAutoConfiguration就是在springMVC注入一个拦截器,这个拦截器会往事务全局上下文添加Xid 和清楚Xid

TransactionPropagationInterceptor

SeataSagaAutoConfiguration

SeataSagaAutoConfiguration主要是实现Saga模式,因为平常基本使用AT,TCC模式居多,这里暂时不做分析

posted @ 2023-08-15 16:53  苜蓿椒盐  阅读(99)  评论(0编辑  收藏  举报