美文网首页SpringCloudSeata
Spring Cloud Alibaba Dubbo + Sea

Spring Cloud Alibaba Dubbo + Sea

作者: qiyubing | 来源:发表于2019-09-25 16:13 被阅读0次

    前言

    作者使用的开发套件是Spring Cloud Alibaba,RPC框架采用Dubbo Spring Cloud,使用Feign的同学也可以看看,原理相通,大同小异。

    准备阶段

    1. 配置数据库,创建Seata回滚需要的表UNDO_LOG和业务表
    2. 启动Seata Server

    这部分建议阅读 官方文档-README

    核心原理

    Seata 全局事务的传播机制就是指事务上下文的传播,根本上,就是 XID 的应用运行时的传播方式。默认的,RootContext 的实现是基于 ThreadLocal 的,即 XID 绑定在当前线程上下文中。
    而我们在分布式系统中,各系统不在一个线程中,为了使Seata全局事物能够在分布式系统中传播,需要对此进行扩展,Seata内置了Dubbo的支持,引用官方文档中的描述:

    对 Dubbo 的支持,我们利用了 Dubbo 框架的 org.apache.dubbo.rpc.Filter 机制。

    这部分建议阅读 官网文档-微服务框架支持

    开始配置

    通过阅读Feign + Seata的配置教程,可以得知我们需要配置的就是将GlobalTransactionScannerDataSourceProxy加入Spring容器
    GlobalTransactionScanner的配置较为简单,而配置DataSourceProxy网上主流的方式是手动配置,如:

    DruidDataSource dataSource = initDataSource(dataSourceProps.get("url").toString(), dataSourceProps.get("username").toString(), dataSourceProps.get("password").toString());
    DataSourceProxy proxy = new DataSourceProxy(dataSource);
    

    这种方式的确简单,却使很多配置项丢失,不是完美的解决方式。完美的解决方式应为在Spring容器初始化后用new DataSourceProxy(dataSource)替换原有dataSource对象。

    那么应如何替换呢,我们可以通过实现Spring提供的ApplicationContextAware接口来获取ApplicationContext。而ApplicationContext值有getBean(),而不能移除之前的dataSource。我们先看一张继承关系图:

    图片转自 https://www.javadoop.com/post/spring-ioc

    虽然ApplicationContext不能直接向下转型为DefaultListableBeanFactory,但我们可以通过ApplicationContext中持有的AutowireCapableBeanFactory向下转型为DefaultListableBeanFactory,从而通过它来获取、删除和注册Bean。

    说了这么多,最后贴我的配置类

    @ConditionalOnClass(GlobalTransactionScanner.class)
    @AutoConfigureBefore(name = "mybatisPlusAutoConfiguration")
    @AutoConfigureAfter({DataSourceAutoConfiguration.class})
    @Configuration
    public class SeataAutoConfiguration implements ApplicationContextAware {
    
        @Value("${spring.application.name}")
        private String applicationName;
    
        private String groupName = "my_test_tx_group";
    
        @Bean
        public GlobalTransactionScanner globalTransactionScanner() {
            if (applicationName == null) {
                return new GlobalTransactionScanner(groupName);
            } else {
                return new GlobalTransactionScanner(applicationName, groupName);
            }
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            // 获取SpringBoot自动配置的DataSource
            DataSource originDataSource = applicationContext.getBean(DataSource.class);
            log.debug("originDataSource = {}", applicationContext.getBean("dataSource"));
            DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
            // 从Spring容器中删除原dataSource
            defaultListableBeanFactory.removeBeanDefinition("dataSource");
            // 将原数据源包装成DataSourceProxy
            DataSourceProxy dataSourceProxy = new DataSourceProxy(originDataSource);
            // 向Spring容器中注册DataSourceProxy
            BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(DataSource.class, () -> dataSourceProxy);
            defaultListableBeanFactory.registerBeanDefinition("dataSource", beanDefinitionBuilder.getBeanDefinition());
            log.debug("proxyDataSource = {}", applicationContext.getBean("dataSource"));
        }
    }
    

    参考链接:

    Seata Wiki
    Spring Cloud Alibaba Seata示例项目
    Feign + Seata配置教程
    Spring IOC 容器源码分析

    相关文章

      网友评论

        本文标题:Spring Cloud Alibaba Dubbo + Sea

        本文链接:https://www.haomeiwen.com/subject/xltwuctx.html