Seata AT模式启动过程图文示例详解

目录
  • 背景
  • SeataDataSourceAutoConfiguration
  • SeatAutoConfiguration
  • HttpAutoConfiguration
  • 小结

背景

为了了解Seata AT模式的原理,我通过源码解读的方式画出了Seata AT模式启动的图示:

如果是基于Springboot项目的话,项目启动的使用,一般约定会先查看spring.factories文件,配置了哪些类是需要自动装配的。Seata也是利用了这个约定,在项目启动的时候,默认会装配指定的类,以完成Seata相关组件的初始化。

下面我们来一起根据源码解读Seata AT模式启动流程。

由上图可知,Seata AT模式可大概分成以下三部分:

1.与底层数据库打交道的DataSource,这部分功能处理交给了SeataDataSourceAutoConfiguration。

2.处理@GlobalTransactional注解,实现分布式事务管理功能,这部分交给SeataAutoConfiguration处理。

3.分支事务获取、销毁全局事务XID,这部分功能交给HttpAutoConfiguration。

SeataDataSourceAutoConfiguration

首先,我们来看看Seata是如何处理DataSource的。

// 依赖DataSource
@ConditionalOnBean(DataSource.class)
// 三个配置都要为true
@ConditionalOnExpression("${seata.enabled:true} && ${seata.enableAutoDataSourceProxy:true} && ${seata.enable-auto-data-source-proxy:true}")
@AutoConfigureAfter({SeataCoreAutoConfiguration.class})
public class SeataDataSourceAutoConfiguration {
    /**
     * The bean seataAutoDataSourceProxyCreator.
     */
    @Bean(BEAN_NAME_SEATA_AUTO_DATA_SOURCE_PROXY_CREATOR)
    // 可替换
    @ConditionalOnMissingBean(SeataAutoDataSourceProxyCreator.class)
    public SeataAutoDataSourceProxyCreator seataAutoDataSourceProxyCreator(SeataProperties seataProperties) {
        return new SeataAutoDataSourceProxyCreator(seataProperties.isUseJdkProxy(),
            seataProperties.getExcludesForAutoProxying(), seataProperties.getDataSourceProxyMode());
    }
}

1.@ConditionalOnBean(DataSource.class)意味着我们的项目中一定要有DataSource这个Bean。

2.@ConditionalOnExpression里面表示要满足以下三个条件才会创建SeataDataSourceAutoConfiguration:

seata.enabled=true

seata.enableAutoDataSourceProxy=true

seata.enable-auto-data-source-proxy=true

3.@AutoConfigureAfter表示当前Bean创建一定在指定的SeataCoreAutoConfiguration之后。

根据以上分析,我们在引入Seata AT模式的时候,一定要先创建项目的DataSource Bean对象,其次保证相关的配置满足要求,那么才能正确地保证DataSource被Seata代理。

下面继续看SeataAutoDataSourceProxyCreator的创建:

@ConditionalOnMissingBean表示这个Bean的创建其实是可以开发人员自定义的,如果开发人员没有自定义,那么就由Seata自己创建。

SeataAutoDataSourceProxyCreator类中,它继承了AbstractAutoProxyCreator,也就是AOP功能的标准实现。这个时候,我们主要关注wrapIfNecessary方法的实现:

public class SeataAutoDataSourceProxyCreator extends AbstractAutoProxyCreator {
  @Override
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        // 不是DataSource对象不代理
        if (!(bean instanceof DataSource)) {
            return bean;
        }
        // 如果是DataSource对象,但是不是SeataDataSourceProxy对象
        if (!(bean instanceof SeataDataSourceProxy)) {
            // 先调用父类包装一层
            Object enhancer = super.wrapIfNecessary(bean, beanName, cacheKey);
            // 如果代理后的对象和代理前的对象是同一个对象
            // 说明要么这个对象之前已经被代理过
            // 要么SeataDataSourceProxy被开发人员excluded
            if (bean == enhancer) {
                return bean;
            }
            // 如果是正常的DataSource对象的话,那么就会被自动构建成SeataDataSourceProxy,并返回
            DataSource origin = (DataSource) bean;
            SeataDataSourceProxy proxy = buildProxy(origin, dataSourceProxyMode);
            DataSourceProxyHolder.put(origin, proxy);
            return enhancer;
        }
        /*
         * things get dangerous when you try to register SeataDataSourceProxy bean by yourself!
         * if you insist on doing so, you must make sure your method return type is DataSource,
         * because this processor will never return any subclass of SeataDataSourceProxy
         */
        // Seata是不建议用户自己构建SeataDataSourceProxy对象的,即使用户自己构建了SeataDataSourceProxy对象,Seata也会重新处理
        LOGGER.warn("Manually register SeataDataSourceProxy(or its subclass) bean is discouraged! bean name: {}", beanName);
        // 获取用户包装好的代理对象
        SeataDataSourceProxy proxy = (SeataDataSourceProxy) bean;
        // 获取原生DataSource
        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);
        // 返回包装好的代理对象SeataDataSourceProxy
        return originEnhancer;
    }
}

1.通过以上代码解读,有一个点我们需要注意,就是开发人员不需要自己的构建SeataDataSourceProxy对象,使用原生的DataSource即可,Seata会帮助我们构建SeataDataSourceProxy对象。

SeatAutoConfiguration

SeatAutoConfiguration主要功能就是创建GlobalTransactionScanner对象,所以核心功能全部在GlobalTransactionScanner里面。

// 配置seata.enabled=true
@ConditionalOnProperty(prefix = SEATA_PREFIX, name = "enabled", havingValue = "true", matchIfMissing = true)
// 装配顺序
@AutoConfigureAfter({SeataCoreAutoConfiguration.class})
public class SeataAutoConfiguration {
    private static final Logger LOGGER = LoggerFactory.getLogger(SeataAutoConfiguration.class);
    @Bean(BEAN_NAME_FAILURE_HANDLER)
    // 失败处理器,可替换
    @ConditionalOnMissingBean(FailureHandler.class)
    public FailureHandler failureHandler() {
        return new DefaultFailureHandlerImpl();
    }
    @Bean
    @DependsOn({BEAN_NAME_SPRING_APPLICATION_CONTEXT_PROVIDER, BEAN_NAME_FAILURE_HANDLER})
    // 开发人员可自定义GlobalTransactionScanner
    @ConditionalOnMissingBean(GlobalTransactionScanner.class)
    public GlobalTransactionScanner globalTransactionScanner(SeataProperties seataProperties, FailureHandler failureHandler,
            ConfigurableListableBeanFactory beanFactory,
            @Autowired(required = false) List<ScannerChecker> scannerCheckers) {
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Automatically configure Seata");
        }
        // set bean factory
        GlobalTransactionScanner.setBeanFactory(beanFactory);
        // add checkers
        // '/META-INF/services/io.seata.spring.annotation.ScannerChecker'
        GlobalTransactionScanner.addScannerCheckers(EnhancedServiceLoader.loadAll(ScannerChecker.class));
        // spring beans
        GlobalTransactionScanner.addScannerCheckers(scannerCheckers);
        // add scannable packages
        GlobalTransactionScanner.addScannablePackages(seataProperties.getScanPackages());
        // add excludeBeanNames
        GlobalTransactionScanner.addScannerExcludeBeanNames(seataProperties.getExcludesForScanning());
        //set accessKey and secretKey
        GlobalTransactionScanner.setAccessKey(seataProperties.getAccessKey());
        GlobalTransactionScanner.setSecretKey(seataProperties.getSecretKey());
        // create global transaction scanner
        return new GlobalTransactionScanner(seataProperties.getApplicationId(), seataProperties.getTxServiceGroup(), failureHandler);
    }
}

1.装配SeataAutoConfiguration要求配置中seata.enabled=true

2.我们可以自定义FailureHandler;这个失败处理器是专门给TM使用的;

3.同样我们也可以自定义GlobalTransactionScanner,不过基本上不会这么做,除非有特殊需求;

GlobalTransactionScanner里面基本上做两个事情:

  • 代理所有被@GlobalTransactional@GlobalLock注解的方法;
  • 使用Neety初始化TM ClientRM Client,以便实现和TC通信;TC也就是我们的Seata Server
public class GlobalTransactionScanner extends AbstractAutoProxyCreator
        implements ConfigurationChangeListener, InitializingBean, ApplicationContextAware, DisposableBean {

}
  • AbstractAutoProxyCreator:通过wrapIfNecessary方法代理所有被@GlobalTransactional@GlobalLock注解的方法;
  • ConfigurationChangeListener:通过onChangeEvent方法监听配置service.disableGlobalTransaction的变化;
  • InitializingBean:通过afterPropertiesSet方法初始化TM ClientRM Client
  • ApplicationContextAware:通过setApplicationContext方法获取IOC容器;
  • DisposableBean:当GlobalTransactionScanner被销毁时,通过destroy方法来回收资源;

我们重点关注wrapIfNecessaryafterPropertiesSet方法:

@Override
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        // 检查Bean是否符合被代理的要求
        // 1. 不能是配置类,比如以Configuration、Properties、Config结尾的Bean名称
        // 2. Bean所在的包名在扫描范围内
        // 3. 不能被@Scope注解
        if (!doCheckers(bean, beanName)) {
            return bean;
        }
        try {
            synchronized (PROXYED_SET) {
                // 如果已经被代理,就跳过
                if (PROXYED_SET.contains(beanName)) {
                    return bean;
                }
                interceptor = null;
                // 检查是否被TCC注解
                if (TCCBeanParserUtils.isTccAutoProxy(bean, beanName, applicationContext)) {
                    // 初始化TCC Fence Clean Task
                   TCCBeanParserUtils.initTccFenceCleanTask(TCCBeanParserUtils.getRemotingDesc(beanName), applicationContext);
                    // 创建TCC代理类
                    interceptor = new TccActionInterceptor(TCCBeanParserUtils.getRemotingDesc(beanName));
                    ConfigurationCache.addConfigListener(ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION,
                            (ConfigurationChangeListener)interceptor);
                } else {
                    // 如果不是TCC代理,那么先获取当前类和它实现的接口
                    Class<?> serviceInterface = SpringProxyUtils.findTargetClass(bean);
                    Class<?>[] interfacesIfJdk = SpringProxyUtils.findInterfaces(bean);
                    // 判断当前类及相关方法是否被@GlobalTransactional或@GlobalLock注解
                    if (!existsAnnotation(new Class[]{serviceInterface})
                        && !existsAnnotation(interfacesIfJdk)) {
                        // 没有被注解,不代理
                        return bean;
                    }

                    // 准备创建方法拦截器
                    if (globalTransactionalInterceptor == null) {
                        globalTransactionalInterceptor = new GlobalTransactionalInterceptor(failureHandlerHook);
                        ConfigurationCache.addConfigListener(
                                ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION,
                                (ConfigurationChangeListener)globalTransactionalInterceptor);
                    }
                    // 拦截器创建完毕
                    interceptor = globalTransactionalInterceptor;
                }
                LOGGER.info("Bean[{}] with name [{}] would use interceptor [{}]", bean.getClass().getName(), beanName, interceptor.getClass().getName());
                // 如果bean不是代理对象,那么不做方法拦截,直接返回
                if (!AopUtils.isAopProxy(bean)) {
                    bean = super.wrapIfNecessary(bean, beanName, cacheKey);
                } else {
                    // 准备把方法拦截器插入进去
                    AdvisedSupport advised = SpringProxyUtils.getAdvisedSupport(bean);
                    // 获取所有的方法拦截器,包括GlobalTransactionalInterceptor
                    Advisor[] advisor = buildAdvisors(beanName, getAdvicesAndAdvisorsForBean(null, null, null));
                    int pos;
                    // 依次添加进目标对象中
                    for (Advisor avr : advisor) {
                        // Find the position based on the advisor's order, and add to advisors by pos
                        pos = findAddSeataAdvisorPosition(advised, avr);
                        advised.addAdvisor(pos, avr);
                    }
                }
                PROXYED_SET.add(beanName);
                // 返回被代理的bean
                return bean;
            }
        } catch (Exception exx) {
            throw new RuntimeException(exx);
        }
    }

通过上述源码分析可知:Seata是根据类、接口和方法上的@GlobalTransactional@GlobalLock注解来判断是否需要针对目标方法做拦截的。

@Override
    public void afterPropertiesSet() {
        // 如果不允许全局事务
        if (disableGlobalTransaction) {
            if (LOGGER.isInfoEnabled()) {
                LOGGER.info("Global transaction is disabled.");
            }
          // 添加监听器,监听配置的变化
            ConfigurationCache.addConfigListener(ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION,
                    (ConfigurationChangeListener)this);
            return;
        }

        if (initialized.compareAndSet(false, true)) {
          // 准备初始化TM Client、RM Client
            initClient();
        }
    }
private void initClient() {
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Initializing Global Transaction Clients ... ");
        }
        if (DEFAULT_TX_GROUP_OLD.equals(txServiceGroup)) {
            LOGGER.warn("the default value of seata.tx-service-group: {} has already changed to {} since Seata 1.5, " +
                    "please change your default configuration as soon as possible " +
                    "and we don't recommend you to use default tx-service-group's value provided by seata",
                    DEFAULT_TX_GROUP_OLD, DEFAULT_TX_GROUP);
        }
        if (StringUtils.isNullOrEmpty(applicationId) || StringUtils.isNullOrEmpty(txServiceGroup)) {
            throw new IllegalArgumentException(String.format("applicationId: %s, txServiceGroup: %s", applicationId, txServiceGroup));
        }
        //初始化TM Client
        TMClient.init(applicationId, txServiceGroup, accessKey, secretKey);
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Transaction Manager Client is initialized. applicationId[{}] txServiceGroup[{}]", applicationId, txServiceGroup);
        }
        //初始化RM Client
        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();
    }

至此,SeatAutoConfiguration的工作处理完毕;

HttpAutoConfiguration

HttpAutoConfiguration的工作比较简单,我们想象一下,RM如何知道它属于哪一个分布式事务?这就需要一个统一的标识来决定所有的分支事务都属于同一个分布式事务,这个标识在Seata中叫做XID

XID由TM开启分布式事务时生成,通过RPC的方式从一个分支事务传递到另一个分支事务,所以我们在RM端需要一个从RPC中解析获取XID的功能,以及在业务逻辑处理完毕后,销毁当前线程中XID的功能。

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication
public class HttpAutoConfiguration extends WebMvcConfigurerAdapter {
    // 注册拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new TransactionPropagationInterceptor());
    }

    // 添加异常解析处理器
    @Override
    public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
        exceptionResolvers.add(new HttpHandlerExceptionResolver());
    }
}
public class TransactionPropagationInterceptor extends HandlerInterceptorAdapter {
    private static final Logger LOGGER = LoggerFactory.getLogger(TransactionPropagationInterceptor.class);
    // 前置处理逻辑
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        // 获取当前线程XID
        String xid = RootContext.getXID();
        // 从rpc中获取XID
        String rpcXid = request.getHeader(RootContext.KEY_XID);
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("xid in RootContext[{}] xid in HttpContext[{}]", xid, rpcXid);
        }
        // 如果线程中没有XID,并且从请求中拿到了XID,那么把请求中的XID绑定到当前线程
        if (StringUtils.isBlank(xid) && StringUtils.isNotBlank(rpcXid)) {
            RootContext.bind(rpcXid);
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("bind[{}] to RootContext", rpcXid);
            }
        }
        return true;
    }
    // 后置处理逻辑
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 业务逻辑处理完毕,从当前线程中删除XID
        if (RootContext.inGlobalTransaction()) {
            XidResource.cleanXid(request.getHeader(RootContext.KEY_XID));
        }
    }
}
public class HttpHandlerExceptionResolver extends AbstractHandlerExceptionResolver {
    // 发生异常后,删除当前线程中的XID
    @Override
    protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse httpServletResponse, Object o, Exception e) {
        XidResource.cleanXid(request.getHeader(RootContext.KEY_XID));
        return null;
    }
}

小结

通过以上源码分析和图解Seata AT模式,我们可以了解到以下几点:

1.GlobalTransactionInterceptor属于TM侧,它主要负责通过TM Client开启分布式事务、提交分布式事务以及回滚分布式事务;属于大总管。

2.SeataDataSourceProxy属于RM侧,它主要负责分支事务的开启,提交以及回滚,属于真正干活的小兵。

3.TM ClientRM Client纯属于两个通信工具,负责与TC端建立通信。

4.TransactionPropagationInterceptorHttpHandlerExceptionResolver服务于分支事务,负责全局事务XID的获取以及业务逻辑处理完毕的善后事宜。

以上就是Seata AT模式启动过程图文示例详解的详细内容,更多关于Seata AT模式启动过程的资料请关注我们其它相关文章!

(0)

相关推荐

  • springboot 整合 seata的配置过程

    前言: 小编引入的图片和文字描述都是来自于尚硅谷的视频讲解,在此感谢尚硅谷的老师,同时也结合 seata文档官方文档进行整合 项目地址(gitee): https://gitee.com/qinenqi/online springboot整合 seata 整合配置 online-project 这个服务调用 online-coupon这个服务 在 这两个被整合的服务对用的数据库中分别 创建 UNDO_LOG 表 -- 注意此处0.3.0+ 增加唯一索引 ux_undo_log CREATE TA

  • springcloud整合seata的实现代码

    目录 一.背景 二.项目结构 三.实现功能: 四.项目使用到的技术 五.整合步骤 1.引入spring-cloud-starter-alibaba-seata jar包 2.涉及到的业务库操作 1.业务库需要存在 undo_log 表 2.业务表主键 3.页面中自动更新时间戳 3.开启数据源代理 1.自动配置数据源代理 2.手动配置AT模式数据源代理 4.传递 xid 5.事务分组和seata server对应上 6.注册中心和配置中心 7.业务方法加上@GlobalTransactional

  • Java分布式事务管理框架之Seata

    目录 Seata介绍 三大组件 实现原理 四种事务模式 搭建seata服务端 单机版安装 集群安装 Seata介绍 Seata:Simple Extensible Autonomous Transaction Architecture,简易可扩展的自治式分布式事务管理框架,其前身是fescar.是一种简单分布式事务的解决方案.Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务.Seata 将为用户提供了 AT.TCC.SAGA 和 XA 事务模式,为用户打造一

  • Spring Cloud + Nacos + Seata整合过程(分布式事务解决方案)

    目录 一.简介 二.seata-server部署 1.官网下载 2.解压到本地 3.修改配置文件 4.seata数据库初始化 5.业务数据库 6.启动seata-server 三.微服务项目集成Seata 1.引入依赖 2.配置文件 一.简介    Seata 是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务.   2019 年 1 月,阿里巴巴中间件团队发起了开源项目 Fescar(Fast & EaSy Commit And Rollback),和社区

  • springboot整合shardingsphere和seata实现分布式事务的实践

    各个框架版本信息 springboot: 2.1.3 springcloud: Greenwich.RELEASE seata: 1.0.0 shardingsphere:4.0.1 maven 依赖        <dependency>         <!--<groupId>io.shardingsphere</groupId>-->         <groupId>org.apache.shardingsphere</group

  • 使用springCloud+nacos集成seata1.3.0搭建过程

    1.docker安装seata 1.3.0镜像 docker pull seataio/seata-server:1.3.0 2.运行容器获取配置文件 docker run --name seata-server -p 8091:8091 -d seataio/seata-server:1.3.0 3.将容器中的配置拷贝到/usr/local/seata-1.3.0 docker cp seata-server:/seata-server /usr/local/seata-1.3.0 4.停止容

  • Seata AT模式启动过程图文示例详解

    目录 背景 SeataDataSourceAutoConfiguration SeatAutoConfiguration HttpAutoConfiguration 小结 背景 为了了解Seata AT模式的原理,我通过源码解读的方式画出了Seata AT模式启动的图示: 如果是基于Springboot项目的话,项目启动的使用,一般约定会先查看spring.factories文件,配置了哪些类是需要自动装配的.Seata也是利用了这个约定,在项目启动的时候,默认会装配指定的类,以完成Seata相

  • Seata AT模式TM处理流程图文示例详解

    目录 TM的作用 源码分解 小结 TM的作用 我们根据源码解读画出了下图,该图示展现了TM在整个Seata AT模式的分布式事务中所起的作用: 从上图中可以看出,TM主要有两个作用: 开启分布式事务,以拿到XID作为分布式事务开启的标识:一定是从TC拿到XID,不是从调用方传递过来的XID: 根据所有RM的处理结果来决定是提交分布式事务还是回滚分布式事务: 转换成伪代码如下: try{ // 开启分布式事务 String xid = TM.beginGlobalTransaction(); //

  • java设计模式策略模式图文示例详解

    目录 策略模式 意图 问题 解决方案 真实世界类比 策略模式结构 伪代码 策略模式适合应用场景 实现方式 策略模式优缺点 策略模式优缺点 与其他模式的关系 策略模式 亦称:Strategy 意图 策略模式是一种行为设计模式,它能让你定义一系列算法,并将每种算法分别放入独立的类中,以使算法的对象能够相互替换. 问题 一天,你打算为游客们创建一款导游程序.该程序的核心功能是提供美观的地图,以帮助用户在任何城市中快速定位. 用户期待的程序新功能是自动路线规划:他们希望输入地址后就能在地图上看到前往目的

  • Tomcat启动核心流程示例详解

    目录 一.Tomcat的启动核心流程 1.启动的入口 2.init方法 3.load方法 4.start方法 5.核心流程的总结 一.Tomcat的启动核心流程 前面给大家介绍了Tomcat中的生命周期的设计,掌握了这块对于我们分析Tomcat的核心流程是非常有帮助的,也就是我们需要创建相关的核心组件,比如Server,Service肯定都绕不开生命周期的方法. 1.启动的入口 你可以通过脚本来启动Tomcat服务(startup.bat),但如果你看过脚本的命令,你会发现最终调用的还是Boot

  • 通俗易懂的C++前缀和与差分算法图文示例详解

    目录 1.前缀和 2.前缀和算法有什么好处? 3.二维前缀和 4.差分 5.一维差分 6.二维差分 1.前缀和 前缀和是指某序列的前n项和,可以把它理解为数学上的数列的前n项和,而差分可以看成前缀和的逆运算.合理的使用前缀和与差分,可以将某些复杂的问题简单化. 2.前缀和算法有什么好处? 先来了解这样一个问题: 输入一个长度为n的整数序列.接下来再输入m个询问,每个询问输入一对l, r.对于每个询问,输出原序列中从第l个数到第r个数的和. 我们很容易想出暴力解法,遍历区间求和. 代码如下: in

  • Flutter 异步编程之单线程下异步模型图文示例详解

    目录 一. 本专栏图示概念规范 1. 任务概念规范 2. 任务的状态 3. 时刻与时间线 4.同步与异步 二.理解单线程中的异步任务 1. 任务的分配 2.异步任务特点 3. 异步任务完成与回调 三. Dart 语言中的异步 1.编程语言中与异步模型的对应关系 2.Dart 编程中的异步任务 3.当前任务分析 四.异步模型的延伸 1. 单线程异步模型的局限性 2. 多线程与异步的关系 3. Dart 中如何解决单线程异步模型的局限性 一. 本专栏图示概念规范 本专栏是对 异步编程 的系统探索,会

  • 使用策略模式实现报警服务示例详解(短信报警)

    着重说一下策略模式.首先需要定义一个接口,该接口用来统一报警方法,代码如下: 复制代码 代码如下: /// <summary>/// 报警接口,统一各种报警方式发出报警的方法/// </summary>public interface IAlarm{ void Alarm(Message message);} 大家伙看到Message会不会比较疑惑呢,别着急,Message就是我自己定义的一个报警的模型,比如报警标题,收件人(报警报给谁呢),报警方式(邮件.客户端等)等. 定义好接

  • Golang WorkerPool线程池并发模式示例详解

    目录 正文 处理CVS文件记录 获取测试数据 线程池耗时差异 正文 Worker Pools 线程池是一种并发模式.该模式中维护了固定数量的多个工作器,这些工作器等待着管理者分配可并发执行的任务.该模式避免了短时间任务创建和销毁线程的代价. 在 golang 中,我们使用 goroutine 和 channel 来构建这种模式.工作器 worker 由一个 goroutine 定义,该 goroutine 通过 channel 获取数据. 处理CVS文件记录 接下来让我们通过一个例子,来进一步理

  • Android 应用程序的启动流程示例详解

    目录 应用进程的启动流程 1.ActivityStackSupervisor.startSpecificActivity 2.ATMS.startProcessAsync 3.LocalService.startProcess 4.startProcessLocked函数 5.ProcessList.startProcessLocked 6.ProcessList.startProcessLocked重载 7.ProcessList.startProcess 8.ZygoteState.star

  • vue cli4下环境变量和模式示例详解

    本文介绍了vue cli4下环境变量和模式示例详解,分享给大家,具体如下: 官方文档 环境变量 一个环境变量文件只包含环境变量的键值对: NODE_ENV=development VUE_APP_BASE_URL=http://127.0.0.1:8080/ 注意: NODE_ENV - 是 "development"."production" ."test"或者自定义的值.具体的值取决于应用运行的模式 BASE_URL - 会和 vue.con

随机推荐