SQLAlchemy中scoped_session實現線程安全


  不多說,先上代碼

from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session

engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/s6", max_overflow=0, pool_size=5)
Session = sessionmaker(bind=engine)

# session = Session()
session = scoped_session(Session)
 obj1 = Users(name = "alex1" )
 session.add(obj1)
  
 # 提交事務
 session.commit()
 # 關閉session
 session.close()

   在上面代碼中,從連接池中拿連接的時候,Session直接實例化,或者scoped_session進行實例化也可以

  而且調用時,方法名都是一樣的,比如session.add(),我們會猜他兩是繼承關系

  但實際是class scoped_session(object),並沒有繼承Session,而且在scoped_session里也沒有找到add方法和commit方法... 那它怎么實現的?

  

  在scoped_session類下面,有個for循環,其中public_methods,在這里面有我們想看到的add,commit方法,上面把每個方法都設置到scoped_session類里,還要看下instrument(meth)到底是個啥玩意?

    public_methods = (
        '__contains__', '__iter__', 'add', 'add_all', 'begin', 'begin_nested',
        'close', 'commit', 'connection', 'delete', 'execute', 'expire',
        'expire_all', 'expunge', 'expunge_all', 'flush', 'get_bind',
        'is_modified', 'bulk_save_objects', 'bulk_insert_mappings',
        'bulk_update_mappings',
        'merge', 'query', 'refresh', 'rollback',
        'scalar')

   在這個函數最終返回了一個do函數,並把name,也就是函數名傳了進入實現閉包

def instrument(name):
    def do(self, *args, **kwargs):
        return getattr(self.registry(), name)(*args, **kwargs)
    return do

   所以當執行session.add時,它會去執行do函數,里面封裝的name就是add

  do函數中self.registry是個啥玩意呢?其中self指的是scoped_session對象,所以我們可以去看scoped_session進行實例化是都干了些啥

        #session_factory就是傳入的Session = sessionmaker(bind=engine)
        self.session_factory = session_factory

        if scopefunc:
            self.registry = ScopedRegistry(session_factory, scopefunc)
        else:#scopefunc此時沒值,所以走這里
            self.registry = ThreadLocalRegistry(session_factory)

   我們會看到我們需要的self.registry就在上面代碼中,那現在主要ThreadLocalRegistry實例化后返回個啥?

    def __init__(self, createfunc):
        self.createfunc = createfunc
        self.registry = threading.local()

   createfunc就是傳入的session_factory,也就是Session,所以scoped_session對象的registry,是一個ThreadLocalRegistry對象,封裝了Session和local對象

 

   所以self.registry()會執行ThreadLocalRegistry里的__call__方法

    def __call__(self):
        try:
            return self.registry.value  #剛開始value沒值,會報錯
        except AttributeError:
            val = self.registry.value = self.createfunc()  #走這里Session(),也就是數據庫連接操作句柄,並把句柄放入local中
            return val

   so,self.registry()得到的就是數據庫操作句柄,所以do函數里最終還是獲取到Session里的方法,並執行

  當然第一次add操作,會實例化,第二次就直接去local中取,就不走異常分支了

  所以這種方式在線程上是更安全的


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM