天天看點

Python裝飾器-服務層級事務控制前言正文疑問

目錄

前言

正文

 服務層級事務控制器

用法

實作的功能和效果

疑問

前言

我之前的一篇博文講述了一個基于Flask架構,Web後端項目重構的内容,最後的服務層級事務控制一部分沒有講,在這裡做一下補充,包括思想的運用和代碼的展示。

出門左轉另一篇博文,了解需求背景:

Flask項目使用MVC思想進行重構

正文

因為之前是用java那一套的,使用springBoot架構,那個架構相對來說比較全面,包含了各種元件,以及友善的事務控制注解元件

但是,重構的項目是Flask搭建的,比較輕量級,沒有那麼全面的功能。

我在重構的時候也發現了這個問題,是以,我就使用Python裝飾器實作了一個類似java中springBoot架構的事務注解的元件

服務層事務控制的前提:

flask進行更修資料等操作,全部使用flush()來寫入緩存

db.session.flush()
           

而不要使用commit()直接送出到資料庫中,這樣在服務出現異常的時候就可以實作事務的復原

db.session.commit()
           

 服務層級事務控制器

"""
事務處理控制
功能:
    服務層統一事務控制
"""
import traceback
from src.const.extensions import db
from src.dto.ResultInfo import ResultInfo


class Transactional:

    """裝飾器實體函數"""

    @staticmethod
    def transRollBack(originalFunction):
        def newFunction(*args, **kwargs):
            result = ResultInfo()
            try:
                result = originalFunction(*args, **kwargs)
            except Exception as e:
                err_info = traceback.format_exc()
                print(err_info)
                print("transactional rollback")
                db.session.rollback()
                return result.fail(msg="服務異常")
            else:
                db.session.commit()
                return result

        return newFunction

    """
    類裝飾器包裝
    查找所有需要事務控制的服務方法
    """

    @staticmethod
    def transactional(Cls):
        class NewCls:
            def __init__(self, *args, **kwargs):
                self.oInstance = Cls(*args, **kwargs)

            # 調用服務的時候會被調用,以實作面向運作時(切面)程式設計
            def __getattribute__(self, name):
                try:
                    # 預設處理方式
                    x = super(NewCls, self).__getattribute__(name)
                except AttributeError as e:
                    pass
                else:
                    return x
                x = self.oInstance.__getattribute__(name)
                if type(x) == type(self.__init__):
                    return Transactional.transRollBack(x)
                else:
                    return x

        return NewCls
           

用法

  1. 導入注解子產品
  2. 在服務類上加上如下注解即可
from src.transactional.Transactional import Transactional

# 地區服務
@Transactional.transactional
class CityService:

    def getAll(self):
        resultInfo = ResultInfo()
        results = CityDao().getAll()
        if len(results):
            resultInfo.success(
                msg="查找記錄成功",
                data=results,
            )
        else:
            resultInfo.fail(msg="未找到記錄",data=[])
        return resultInfo
           

實作的功能和效果

在所有已經加了事務注解的服務類的任何方法被調用的時候,都會進入事務的控制範圍,當一個服務中的任何一步持久化操作出現異常,都會出發事務的復原,進而實作服務層級的事務控制。(隻有在服務被調用,才會針對被調用的服務進行事務控制,相當于是在運作時來控制程式的進行,類似于切面程式設計的思想)

疑問

可能看到這裡,有些人會有些疑問?

問:為什麼要實作服務層級的事務控制?

答:首先看這樣一個例子:線上轉賬服務,使用者甲,将自己的5k轉賬給乙,對資料庫的操作分為兩個步驟,1.甲的賬戶-5k,2.乙的賬戶+5k,才算這個轉賬服務真正完成!,如果第一步完成,并送出資料庫,第二步操作因為網絡或者一些其它原因造成失敗,那麼甲就白白損失了5k,是不是不合理?

問:我對資料庫的操作一步,進行一步送出到緩存,将1.2.兩步代碼使用異常捕獲,發生異常則復原資料庫。未發生異常則重新整理到實體資料庫。不也可以嗎?

答:當然可以!但是首先有幾個問題要明白:有多少個方法要進行事務控制,是所有的更新操作!難道所有的方法裡面都要寫重複的代碼,如果項目比較大,有200個方法呢?2000個呢?累傻小子呢叭,這個時候,面向切面程式設計的思想就展現出了它的優越性!在java中的相關實作是反射(動态代理),而在python中的相關實作就是裝飾器,可以在業務的執行前後進行一些操作,而不用更改原有的代碼,甚至你可以完全不用了解原來的代碼裡面是怎麼寫的,你就可以實作如此強大的功能。是以,我們要做的就是在所有需要事務控制的方法上加上我們事務處理的注解即可。但是!200個方法也是個不小的工作量,加200行代碼進去,這時候就可以考慮在服務層級加事務注解了,隻要在服務類上加了注解,那麼它的所有的方法都會收到事務的控制。工作量就要小很多,并且也完全符合邏輯,在微服務橫行的當下,也不會過時!!!