大型项目改造实践经验篇----分布式事务之本地事务表

分布式系统中,分布式事务是避不开的问题。

理论知识不再过多赘述,CAP,BASE理论,这些都是面试避不开的话题,下面说一下自己的一些经验。

其实无论是单机事务,还是分布式事务,核心都是一个词,事务。在java语言的落地过程中,事务封装在Connection对象中,正常提交是commit(),异常回滚是rollback()。

最好的解决方案就是去掉分布式事务,接口粗化,避免分布式事务,数据一致性要求不高,甚至可以不用考虑,等应用场景多了之后,可以针对这些数据库应用做一个数据清洗程序,定时清洗即可。

但是问题还是存在,解决问题的思路,就是统一操作connection对象,统一执行或者回滚,这样很容易想到二阶段提交的解决方案。A项目执行execute,同时向B项目发起请求,B项目执行execute后返回执行结果,A再commit,B也跟着commit。

这种数据强一致性解决方案,适用于toB或者金融领域,但是有一个前提,应用要有足够强的容错能力,应用之间完全隔离。否则,一旦出现网络波动,事务无法正常提交,将导致应用的线程大量堵塞,宕机丢失重要数据。

下面讲讲我的解决方案,我们的场景是,数据在web页面上提供查询功能,和钱不打交道,查询频率较高,但是数据实时性准确性要求都不高,容错较强,所以我们的选择是其它事务方保证接口幂等性,本地事务表的方式解决这个问题。

这么做的优点是,第一,不会像两阶段事务,出现网络波动阻塞应用,影响核心业务。第二,三阶段提交方案看起来完善,但是落地后一个需求对应多个接口开发,我们流程比较规范,每个接口都要完善的接口文档,测试用例脚本编写,压测,微服务注册,申请新服务等流程,开发,测试运维人远代价过大。所以采用本地事务表方式解决,开发难度低,便于管理,容错性较高。

我的思路是定义DistributedTranscation注解,然后切面拦截,失败后立即重试一次,继续失败的话记录入库,然后定时任务从库中随机获取失败的任务执行,达到最大重试次数后,不执行。同时需要一个定时任务扫描失败达到最大次数的对象,达到标准后通知运维,开发人员及时排查。

下面截取几个关键类图

切面类
 

  @Around(value = "@annotation(com.summary.net.transcation.config.DistributedTranscation)")
  public Object invoke(ProceedingJoinPoint point) throws Throwable {
    try {
      //如果是恢复线程,退出切面
      return point.proceed(point.getArgs());
    } catch (Throwable throwable) {
      //如果是事务恢复线程,退出切面
      if (ThreadLocalUtils.isResume()) {
        throw throwable;
      }
      try {
        //立即重试一次
        return point.proceed(point.getArgs());
      } catch (Throwable e) {
        TranscationDTO transcationDTO = TranscationDTO.builder()
            .id(generateIDSequenceService.generateSequenceByRedis("transcation"))
            .className(getClassName(point))
            .methodName(getMethodName(point))
            .paramClass(getParamTypeList(point))
            .paramContents(getParamContentList(point))
            .failReason(START.append(getFailReason(e)).toString())
            .retryCount(1)
            .insertTimeForHis(new Date())
            .validStatus("0")
            .threadId(Thread.currentThread().getId())
            .beanName(getBeanName(point)).build();
        log.error("方法异常, {}", e);

        for (TranscationIntercetor transcationIntercetor : pluginChain.getAll()) {
          transcationDTO =   transcationIntercetor.preSave(transcationDTO);
        }

        resposity.save(transcationDTO);
        //重试失败后,保存失败信息
        //保存异常信息
        throw new RuntimeException(throwable);
      }
    }
  }

定时任务类

  //每10秒随机取10条执行
  @Scheduled(cron = "0/10 * * * * ? ")
  public void configureTask() {
    System.out.println("开始执行.........");
    if (ObjectUtils.isEmpty(tasks) && !tasks.addAll(resposity.queryTranscationDTOList())) {
      return;
    }
    System.out.println("任务执行.........");
    for (int i = 0; i < tasks.size(); i++) {
      service.execute(new DistrubutedTaskService(tasks.get(i), pluginChain, resposity));
      tasks.remove(i);
    }
  }

 

该项目demo版已上传到github。拉下来配置一下数据库即可执行。主服务测试接口为http://localhost:8091/tt,访问othercontroller的t1接口,可以加jvm参数-Dserver.port=8808启动同一个服务,模拟场景.

github地址 https://github.com/836219171/distributed_transcations.git

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值