# Seata ## 部署安装 - 集成nacos配置中心 下载 config ` https://github.com/seata/seata/blob/develop/script/config-center/config.txt` 下载 sh脚本 `https://github.com/seata/seata/blob/develop/script/config-center/nacos/nacos-config.sh` 本地修改config.txt 相关参数,修改nacos-config.sh 相关配置,并执行脚本,将配置推送到nacos - Docker部署(需要指定当前宿主机IP) ```sh docker run --name seata-server \ -p 8091:8091 \ -e SEATA_IP=10.113.206.85 \ -e SEATA_PORT=8091 \ seataio/seata-server ``` 进入容器中,修改registry.conf 配置Nacos相关参数 ```sh registry { # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa type = "nacos" loadBalance = "RandomLoadBalance" loadBalanceVirtualNodes = 10 nacos { application = "serverAddr" serverAddr = "10.113.206.34:8848" group = "DEFAULT_GROUP" namespace = "" cluster = "default" username = "" password = "" } } config { # file、nacos 、apollo、zk、consul、etcd3 type = "nacos" nacos { serverAddr = "10.113.206.34:8848" namespace = "" group = "SEATA_GROUP" username = "" password = "" # dataId = "seataServer.properties" } } ``` ## 注入原理 - 添加注解 @EnableAutoDataSourceProxy 配置自动代理数据源 ```java @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Import({AutoDataSourceProxyRegistrar.class}) @Documented public @interface EnableAutoDataSourceProxy { boolean useJdkProxy() default false; String[] excludes() default {}; //设置工作模式 默认AT模式,可以选择TCC、XA、SAGA String dataSourceProxyMode() default "AT"; } ``` @Import 引入配置类 AutoDataSourceProxyRegistrar.class 配置了代理数据源对象 ```java ``` - 由于maven依赖了 ```java io.seata seata-spring-boot-starter 1.4.1 ``` 1:由spring.factories 配置加载配置类 加载bean GlobalTransactionScanner ```java @Configuration @EnableConfigurationProperties({SeataProperties.class}) public class SeataAutoConfiguration { . . . @Bean @DependsOn({"springApplicationContextProvider", "failureHandler"}) @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 是个spring bean处理器,主要继承以及实现如下 ```java public class GlobalTransactionScanner extends AbstractAutoProxyCreator implements ConfigurationChangeListener, InitializingBean, ApplicationContextAware, DisposableBean { } public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware { } ``` 由这部分判断是否有注解@GlobalTransactional 并创建代理对象,增强方法主要在有该注解的方法前,生成XID,在TC注册该全局事务; ```java public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (this.earlyProxyReferences.remove(cacheKey) != bean) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; } ``` GlobalTransactionalInterceptor 判断 是否有@GlobalTransactional注解 ```java public Object invoke(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)) { Method method = BridgeMethodResolver.findBridgedMethod(specificMethod); GlobalTransactional globalTransactionalAnnotation = (GlobalTransactional)this.getAnnotation(method, targetClass, GlobalTransactional.class); GlobalLock globalLockAnnotation = (GlobalLock)this.getAnnotation(method, targetClass, GlobalLock.class); boolean localDisable = this.disable || degradeCheck && degradeNum >= degradeCheckAllowTimes; if (!localDisable) { if (globalTransactionalAnnotation != null) { return this.handleGlobalTransaction(methodInvocation, globalTransactionalAnnotation); } if (globalLockAnnotation != null) { return this.handleGlobalLock(methodInvocation, globalLockAnnotation); } } } return methodInvocation.proceed(); } ``` AT的增强类:SeataAutoDataSourceProxyAdvice ```java public class SeataAutoDataSourceProxyAdvice implements MethodInterceptor, IntroductionInfo { public Object invoke(MethodInvocation invocation) throws Throwable { if (!RootContext.requireGlobalLock() && this.dataSourceProxyMode != RootContext.getBranchType()) { return invocation.proceed(); } else { Method method = invocation.getMethod(); Object[] args = invocation.getArguments(); Method m = BeanUtils.findDeclaredMethod(this.dataSourceProxyClazz, method.getName(), method.getParameterTypes()); if (m != null) { SeataDataSourceProxy dataSourceProxy = DataSourceProxyHolder.get().putDataSource((DataSource)invocation.getThis(), this.dataSourceProxyMode); return m.invoke(dataSourceProxy, args); } else { return invocation.proceed(); } } } } ``` 其中 RootContext.requireGlobalLock() 先判断了当前线程的该ThreadLocal中是否有 TX_LOCK ,如果有,执行代理方法 ``` public static boolean requireGlobalLock() { return CONTEXT_HOLDER.get("TX_LOCK") != null; } ``` 2:该spring.factories 配置也同时加载配置类 加载bean HttpAutoConfiguration ```java @Configuration @ConditionalOnWebApplication public class HttpAutoConfiguration extends WebMvcConfigurerAdapter { public HttpAutoConfiguration() { } public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new TransactionPropagationInterceptor()); } public void extendHandlerExceptionResolvers(List exceptionResolvers) { exceptionResolvers.add(new HttpHandlerExceptionResolver()); } } ``` 新增了拦截器,用于拦截请求,构造上面的 线程ThreadLocal 变量,为之后数据源代理对象,判断是否是分布式事务 ```java public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String xid = RootContext.getXID(); String rpcXid = request.getHeader("TX_XID"); if (LOGGER.isDebugEnabled()) { LOGGER.debug("xid in RootContext[{}] xid in HttpContext[{}]", xid, rpcXid); } if (xid == null && rpcXid != null) { RootContext.bind(rpcXid); if (LOGGER.isDebugEnabled()) { LOGGER.debug("bind[{}] to RootContext", rpcXid); } } return true; } public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { if (RootContext.inGlobalTransaction()) { XidResource.cleanXid(request.getHeader("TX_XID")); } } ``` - 未发现用feign拦截器进行request插入参数 XID的配置;对于请求的XID如何传递,暂时未看到源码。