2011-03-03 70 views
0

我有一個處理這樣的請求(顯然,異步)凡SomeSharedMemory從數據庫加載一個簡單的扭服務器等待在其他請求事件扭曲

global SomeSharedMemory 
if SomeSharedMemory is None: 
    SomeSharedMemory = LoadSharedMemory() 
return PickSomething(SomeSharedMemory) 

我想避免從數據庫多次加載SomeSharedMemory。具體來說,當服務器首次啓動時,我們得到兩個併發傳入的請求,我們可能會看到這樣的事情:

請求1:檢查SomeSharedMemory,並不覺得 請求1:發出數據庫查詢加載SSM 要求2:檢查SSM,並不覺得 請求2:發行數據庫查詢加載SSM 請求1:查詢返回,商店SSM 請求1:返回結果 請求2:查詢返回,商店SSM 請求2:返回結果

如果有更多的併發請求,數據庫會受到攻擊。我想這樣做(見http://docs.python.org/library/threading.html#event-objects):

global SomeSharedMemory, SSMEvent 
if SomeSharedMemory is None: 
    if not SSMEvent.isSet(): 
     SSMEvent.wait() 
    else: 
     # assumes that the event is initialized "set" 
     SSMEvent.clear() 
     SomeSharedMemory = LoadSharedMemory() 
     SSMEvent.set() 
return PickSomething(SomeSharedMemory) 

,所以如果一個請求加載共享內存,其他請求會禮貌地等待,直到完成查詢,而不是發行自己的複製數據庫查詢。

這是可能的扭曲?

+0

當你說'SomeSharedMemory'時,你實際上並不是指任何形式的[Shared Memory](http://en.wikipedia.org/wiki/Shared_memory),是嗎? – MattH

回答

2

您的示例設置的方式,很難看到您如何確實存在您描述的問題。如果第二個請求在第一個發出的LoadSharedMemory調用返回之前進入Twisted服務器,那麼第二個請求將在等待處理之前等待。當它最終被處理時,SomeSharedMemory將被初始化並且不會有重複。

不過,我想也許它的情況是LoadSharedMemory是異步的,並返回一個Deferred,讓你的代碼真的看起來更像是這樣的:

def handleRequest(request): 
    if SomeSharedMemory is None: 
     d = initSharedMemory() 
     d.addCallback(lambda ignored: handleRequest(request)) 
    else: 
     d = PickSomething(SomeSharedMemory) 
    return d 

在這種情況下,完全有可能是第二個請求可能會抵達,而initSharedMemory是關閉它的事情。那麼你最終會有兩個任務嘗試初始化該狀態。

當然,要做的事情是注意到你擁有的第三種狀態。不僅有un-初始化和初始化 - ed,而且初始化 - ing。所以也代表這個狀態。我會隱藏它的initSharedMemory函數內部保持請求處理程序簡單,因爲它已經是:

initInProgress = None 

def initSharedMemory(): 
    global initInProgress 
    if initInProgress is None: 
     initInProgress = _reallyInit() 
     def initialized(result): 
      global initInProgress, SomeSharedMemory 
      initInProgress = None 
      SomeSharedMemory = result 
     initInProgress.addCallback(initialized) 
    d = Deferred() 
    initInProgress.chainDeferred(d) 
    return d 

這是一個有點毛,因爲全局的無處不在。這裏有一個稍微乾淨的版本:

from twisted.internet.defer import Deferred, succeed 

class SharedResource(object): 
    def __init__(self, initializer): 
     self._initializer = initializer 
     self._value = None 
     self._state = "UNINITIALIZED" 
     self._waiting = [] 


    def get(self): 
     if self._state == "INITIALIZED": 
      # Return the already computed value 
      return succeed(self._value) 

     # Create a Deferred for the caller to wait on 
     d = Deferred() 
     self._waiting.append(d) 

     if self._state == "UNINITIALIZED": 
      # Once, run the setup 
      self._initializer().addCallback(self._initialized) 
      self._state = "INITIALIZING" 

     # Initialized or initializing state here 
     return d 


    def _initialized(self, value): 
     # Save the value, transition to the new state, and tell 
     # all the previous callers of get what the result is. 
     self._value = value 
     self._state = "INITIALIZED" 
     waiting, self._waiting = self._waiting, None 
     for d in waiting: 
      d.callback(value) 


SomeSharedMemory = SharedResource(initializeSharedMemory) 

def handleRequest(request): 
    return SomeSharedMemory.get().addCallback(PickSomething) 

三種狀態,它們之間很好的明確的過渡,沒有全局狀態更新(至少如果你給SomeSharedMemory一些非全局範圍內),並handleRequest不知道任何的這只是要求一個價值,然後使用它。

+0

謝謝!我的例子假設我寫的代碼是異步的。正如您剛剛演示的那樣,將這些東西異步寫入會更加冗長。 – spitzanator