Ожидание событий в других запросах в Twisted - PullRequest
0 голосов
/ 03 марта 2011

У меня есть простой сервер Twisted, который обрабатывает такие запросы (очевидно, асинхронно)

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

Где 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)

Так, что если один запрос загружает общую память, другие запросы будут вежливо ждать, пока запрос не будет завершен, а не выдавать свои собственные повторяющиеся запросы к базе данных.

Возможно ли это в Twisted?

1 Ответ

2 голосов
/ 03 марта 2011

Как настроен ваш пример, трудно понять, как у вас может возникнуть проблема, которую вы описываете.Если второй запрос поступит на ваш Twisted сервер до того, как будет возвращен вызов LoadSharedMemory, выданный первым, тогда второй запрос просто будет ждать обработки.Когда он будет окончательно обработан, 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 нетзнать обо всем этом, он просто запрашивает значение и затем использует его.

...