美文网首页
软件工程中的事务管理(未允禁转) 2023-10-24

软件工程中的事务管理(未允禁转) 2023-10-24

作者: 9_SooHyun | 来源:发表于2023-10-23 16:00 被阅读0次

软件工程中的事务管理

软件工程中不可避免遇到事务管理的问题。有2种主流的事务管理模式,编程式事务声明式事务

  • 编程式事务:通过编程代码在业务逻辑时需要时自行实现,粒度更小。
    编程式事务需要在代码中显式调用beginTransaction()、commit()、rollback()等事务管理相关的方法,事务的开始、传递和关闭都显式嵌入到函数的业务逻辑里面

  • 声明式事务:通过注解、装饰模式或者XML配置实现。声明式事务是一种AOP式事务管理,可以统一地在函数执行前后进行事务开启和关闭,而不侵入函数的业务逻辑

编程式事务

例子如下:

...
func get_user_by_id(id: int) -> User:
    with db.session.begin():
        user = db.session.query(User).filter(User.id == id).first()
    return user

显式控制事务,事务代码与业务逻辑代码耦合,但是显式也更清晰、控制粒度更细

编程式事务是最自然、最朴素的事务管理模式,任何语言都可以自然地进行编程式事务

声明式事务

java spring 的事务注解是典型的声明式事务(AOP事务)实践

python 声明式事务的实现

python flask中可以借助装饰模式模拟java spring的事务注解

from enum import Enum
from app.flask_extension.plugin import db  # db is an instance of class: `flask_sqlalchemy.SQLAlchemy`


class TransactionPropagation(Enum):
    """
    事务传播方式
    """
    REQUIRED = "REQUIRED"  # 如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。
    MANDATORY = "MANDATORY"  # 如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。

def transactional(propagation: TransactionPropagation = TransactionPropagation.REQUIRED):
    """
    模仿Java Spring的事务注解功能. 默认propagation为REQUIRED

    usage example:
    `python
    @transactional()
    def method_A():
        # 在这里执行A方法的操作
        pass

    @transactional()
    def method_B():
        # 在这里执行B方法的操作
        pass

    @transactional()
    def method_C():
        # 在这里执行C方法的操作
        pass

    @transactional()
    def method_D_1():
        # aop 模式:利用transactional()装饰器 管理 method_D 的事务
        # method_A method_B method_C 在一个事务内执行
        method_A()
        method_B()
        method_C()

    def method_D_2():
        # 编程式事务模式 + aop模式:显式调用beginTransaction()、commit()、rollback()等事务管理相关的方法来管理 method_D 的事务
        # method_A method_B method_C 在一个事务内执行
        with db.session.begin():
            method_A()
            method_B()
            method_C()
    `
    在 `method_D_1()` 和 `method_D_2()`中,我们按顺序调用这些方法,它们会在一个事务中执行
    """
    def _transactional(f):

        @wraps(f)
        def decorated_function(*args, **kwargs):
            supported_propagation = [cur_propagation for cur_propagation in TransactionPropagation]
            if propagation not in supported_propagation:
                raise ValueError(f"{propagation} not in supported_propagation: {supported_propagation}")

            print(f"{f.__name__}使用的propagation是{propagation}")

            if propagation == TransactionPropagation.REQUIRED:
                # 如果当前存在事务,则加入该事务
                if db.session().in_transaction():
                    print(f"执行{f.__name__}前存在已有事务,加入该事务")
                    result = f(*args, **kwargs)
                    print(f"{f.__name__}执行完毕")
                    return result

                # 如果当前不存在事务,则创建一个新的事务
                try:
                    db.session.begin()
                    print(f"{f.__name__}执行前,开启一个新事务")

                    result = f(*args, **kwargs)
                    print(f"{f.__name__}执行完毕")

                    db.session.commit()
                    print(f"{f.__name__}执行完毕后,事务提交完毕")
                    return result
                except Exception as e:
                    db.session.rollback()
                    print(f"{f.__name__}执行出现Exception{e},事务回滚")
                    raise e
                finally:
                    pass

            elif propagation == TransactionPropagation.MANDATORY:
                # 如果当前存在事务,则加入该事务
                if db.session().in_transaction():
                    print(f"执行{f.__name__}前存在已有事务,加入该事务")
                    result = f(*args, **kwargs)
                    print(f"{f.__name__}执行完毕")
                    return result
                else:
                    msg = f"{f.__name__}'s propagation type is {propagation}, but no transaction provided"
                    print(msg)
                    raise ValueError(msg)

        return decorated_function
    return _transactional

这里,通过装饰模式实现声明式事务,解决 scoped_session 对象上的事务开关问题。这是根本问题,不需要每个方法都写事务判断,按需声明propagation即可

golang 声明式事务的实现

因为python是动态语言,所以python的装饰器实现起来很丝滑,不管什么签名的函数都可以统一被装饰

但是golang是强类型语言,一般地,golang中搞装饰模式,只能装饰那些特定签名的函数。在软件工程中,业务逻辑函数的签名有成百上千种,怎么像python那样实现统一的装饰呢?🤔️

  • 要装饰不同签名的函数,装饰函数就需要接受不同签名的函数,于是只能interface{}承载
  • interface{}承载,那么就需要reflect获取函数真实的类型

下面给出一个通用Print装饰器的实现

// You can edit this code!
// Click here and start typing.
package main

import (
    "fmt"
    "reflect"
)

// PrintDecorate 通用的打印装饰器
func PrintDecorate(decoratedFuncPtr, f interface{}) {
    ori_func := reflect.ValueOf(f)

    wrapFunc := func(in []reflect.Value) []reflect.Value {
        // before do something...
        fmt.Println("before do something...")

        ret := ori_func.Call(in)

        // after do something...
        fmt.Println("after do something...")

        return ret
    }
    decoratedFuncValue := reflect.MakeFunc(ori_func.Type(), wrapFunc)

    decoratedFunc := reflect.ValueOf(decoratedFuncPtr).Elem()
    decoratedFunc.Set(decoratedFuncValue)
    return
}

func Add(a, b int) int {
    fmt.Println("do adding")
    return a + b
}

func main() {
    // 函数字面量在 Go 中被视为第一类值(First-Class Value),这意味着你可以将它们分配给变量、作为参数传递给其他函数,或作为其他函数的返回值。然而,你不能直接获取一个函数字面量的地址,因为函数本身并不是一个可寻址的存储位置。
    add := Add // 函数字面量Add必须先赋值给一个变量。

  // PrintDecorate去装饰Add,得到的新函数赋值给add
    PrintDecorate(&add, Add)

    res := add(1, 2)
    fmt.Printf("----result is %+v-----\n", res)
}

参考:https://juejin.cn/post/7115343063119036453

这样,我们就可以实现Transactional装饰器,然后Transactional(&decoratedFunc, OriginalFunc),如下:

// You can edit this code!
// Click here and start typing.
package main

import (
    "fmt"
    "reflect"
)

// Transactional 通用的事务装饰器
func Transactional(decoratedFuncPtr, f interface{}) {
    ori_func := reflect.ValueOf(f)

    wrapFunc := func(in []reflect.Value) []reflect.Value {
        // before do something...
        fmt.Println("open new transaction, or reentrant existed transaction")

        ret := ori_func.Call(in)

        // after do something...
        fmt.Println("commit/rollback transaction, or do nothing")

        return ret
    }
    decoratedFuncValue := reflect.MakeFunc(ori_func.Type(), wrapFunc)

    decoratedFunc := reflect.ValueOf(decoratedFuncPtr).Elem()
    decoratedFunc.Set(decoratedFuncValue)
    return
}

func Add(a, b int) int {
    fmt.Println("do adding")
    return a + b
}

func Subtract(a, b int) int {
    fmt.Println("do subtracting")
    return a - b
}

// TransactionalAdd 加入声明式事务的Add方法
func TransactionalAdd(a, b int) int {
    add := Add
    Transactional(&add, Add)
    return add(a, b)
}

// TransactionalSubtract 加入声明式事务的Subtract方法
func TransactionalSubtract(a, b int) int {
    subtract := Subtract
    Transactional(&subtract, Subtract)
    return subtract(a, b)
}

func main() {
    // 调用TransactionalAdd和TransactionalSubtract即可

    res_add := TransactionalAdd(1, 2)
    fmt.Printf("result is %+v\n", res_add)

    res_sub := TransactionalSubtract(1, 2)
    fmt.Printf("result is %+v\n", res_sub)
}

相关文章

网友评论

      本文标题:软件工程中的事务管理(未允禁转) 2023-10-24

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