• 从零搭建开发脚手架 本地事务和远程调用等操作的剥离


    背景

    在项目编码中会遇到下面的场景,在事务方法中,夹杂着远程调用消息发送缓存更新文件操作等IO操作。

    // 注册送积分
    @Transactional(rollbackFor = Exception.class)
    public void userRegister(..) {
    	userService.register(..); // 本地DB
        remoteCall.addIntegration(...); // 远程调用
    	...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    由于网络存在以下特性:

    • 网络是不可靠的
    • 网络存在延迟的
    • 带宽不是无限的
    • 网络不是安全的

    当网络存在波动或者大量call可能会导致remoteCall.addIntegration方法耗时过长,导致长事务发生,数据库连接得不到释放,引发无法获取数据库连接,则系统操作就会block住。

    所以我们应该把本地事务和其他IO操作剥离开来,不要影响数据库性能。

    Spring提供了很好事务管理机制,主要分为编程式事务声明式事务两种。我们分表用两种方式来实现。

    方案

    编程式事务

    基于底层的API,如PlatformTransactionManagerTransactionDefinitionTransactionTemplate 等核心接口,开发者完全可以通过编程的方式来进行事务管理。

    编程式事务方式需要是开发者在代码中手动的管理事务的开启、提交、回滚等操作。

    TransactionManager

    @Autowired
    TransactionManager  transactionManager;
    // 或
    private TransactionManager  transactionManager;
    private DefaultTransactionDefinition  definition;
    public UserService(PlatformTransactionManager transactionManager) { 
      this.transactionManager = transactionManager ; 
      definition = new DefaultTransactionDefinition() ; 
      definition.setName("laker") ; 
      definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED) ; 
      definition.setTimeout(10);
    } 
    
    public void userRegister() {
    	DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
    	definition.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ);
        // 事务超时时间单位 秒
    	definition.setTimeout(30);
        TransactionStatus status = transactionManager.getTransaction(definition);
           try {
             // 事务操作
             userService.register(..);
             // 事务提交
             transactionManager.commit(status);
          } catch (DataAccessException e) {
             // 事务回滚
             transactionManager.rollback(status);
             throw e;
          }
        // 远程调用
        remoteCall.addIntegration(..)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    TransactionTemplate

    @Autowired
    TransactionTemplate transactionTemplate;
    // 或
    private TransactionTemplate transactionTemplate ;      
    public UserService(PlatformTransactionManager transactionManager) { 
      this.transactionTemplate = new TransactionTemplate(transactionManager) ; 
      this.transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED); 
      this.transactionTemplate.setTimeout(30); //seconds 
    } 
    
    public void userRegister() { 
         // 带有返回值
         transactionTemplate.execute(new TransactionCallback() {
                @Override
                public Object doInTransaction(TransactionStatus transactionStatus) {
                     // 事务操作
                     userService.register(..);
                     // 发生异常会自动回滚。也可以通过在 TransactionStatus上调用setRollbackOnly() 来手动触发回滚:
                     // transactionStatus.setRollbackOnly();
                    return null;
                };
            });
           // 远程调用
           remoteCall.addIntegration(..)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    无返回值的调用方式

    // 执行无返回值的事务管理
    transactionTemplate.execute(new TransactionCallbackWithoutResult() {
    	@Override
    	protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
        // 事务操作
         userService.register(..);
         // 发生异常会自动回滚。也可以通过在 TransactionStatus上调用setRollbackOnly() 来手动触发回滚:
         // transactionStatus.setRollbackOnly();
        }
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    自定义TransactionTemplate

    transactionTemplate = new TransactionTemplate(transactionManager);
    // 设置事务隔离级别
    transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ);
    // 事务传播行为
    transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
    // 事务超时时间单位秒
    transactionTemplate.setTimeout(1000);
    // 设置只读事务
    transactionTemplate.setReadOnly(true);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    声明式事务

    spring事务提供扩展可以允许在事务提交后(还有其他阶段),做一些事情

    查看TransactionPhase,有哪些阶段

    public enum TransactionPhase { 
      BEFORE_COMMIT, // 事务提交前触发 
      AFTER_COMMIT, // 事务提交后触发 
      AFTER_ROLLBACK, // 事务回滚触发 
      AFTER_COMPLETION // 事务完成后 触发 
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    TransactionSynchronizationManager.registerSynchronization

    @Transactional(rollbackFor = Exception.class)
    public void userRegister(..) {
    	userService.register(..);
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
                @Override
                public void afterCommit() {
                 	//做你想做的业务
                    remoteCall.addIntegration(..)
                }
        });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    通过TransactionalEventListener注解+ApplicationEventPublisher

    TransactionalEventListener注解只对声明式事务起作用,对编程式事务无效。仅适用于由PlatformTransactionManager管理的线程绑定事务

    @Transactional(rollbackFor = Exception.class)
    public void userRegister(..) {
    	userService.register(..);
        applicationEventPublisher.publishEvent(..);
    }
    
    
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void addIntegration(..){
    	remoteCall.addIntegration(..)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
  • 相关阅读:
    第11章_数据库的设计规范
    有限元仿真分析误差来源之边界条件,约束和point mass
    docker实战学习2022版本(七)之docker网络学习
    C++核心编程--类篇
    这家公司因Log4j漏洞惨遭黑客攻击并勒索500万美元
    java开发手册-01编程规约
    102.二叉树的层序遍历
    部署vue项目到阿里云服务器
    为啥要用C艹不用C?
    【QT+QGIS跨平台编译】056:【pdal_lepcc+Qt跨平台编译】(一套代码、一套框架,跨平台编译)
  • 原文地址:https://blog.csdn.net/abu935009066/article/details/126832065