Синглтоны Google App Engine (Python) - PullRequest
2 голосов
/ 12 января 2010

Стандартный способ создания синглетонов в Python -

class Singleton(object):
    _instance = None
    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
        return cls._instance

Однако в App Engine это не работает, поскольку серверов может быть много, и мы получим по одному экземпляру на сервер. Итак, как бы мы сделали это для сущности движка приложения?

Что-то вроде:

class MySingleton(db.models):
    def __init__(self):
        all = MySingleton.all()
        if all.count() > 0:
             return all.fetch(1).get()

        super(MySingleton, self).__init__ (*args, **kwargs)

Это приводит к ошибке отвода, поскольку get() вызывает __init__.

Как мы собираемся его использовать :

Мы просто хотим представить файл конфигурации, то есть:

{ 'sitename': "My site", 'footer': "This page owned by X"}

Ответы [ 4 ]

4 голосов
/ 12 января 2010

Синглтоны обычно плохая идея, и мне было бы интересно посмотреть, что делает это исключением. Как правило, они просто замаскированные глобалы, и помимо всех старых проблем с глобалами (например, см. http://c2.com/cgi/wiki?GlobalVariablesAreBad,, в частности, бит вверху, говорящий о нелокальности, неявной связи, проблемах параллелизма, а также тестировании и ограничение), в современном мире вы получаете дополнительные проблемы, вызванные распределенными и параллельными системами. Если ваше приложение потенциально работает на нескольких серверах, можете ли вы безопасно и правильно использовать оба экземпляра приложения на одном и том же экземпляре Singleton?

Если объект не имеет своего состояния, тогда ответ - да, но вам не нужен синглтон, только пространство имен.

Но если у объекта действительно есть какое-то состояние, вам нужно беспокоиться о том, как два экземпляра приложения будут синхронизировать детали. Если два экземпляра попытаются одновременно прочитать и записать один и тот же экземпляр, ваши результаты могут оказаться неверными. (Например, синглтон HitCounter, который считывает текущее значение, добавляет 1 и записывает текущее значение, может пропустить попадания таким образом - и это пример наименьшего вреда, который я могу придумать.)

Я в основном незнаком с ним, поэтому, возможно, Google App Engine имеет некоторую транзакционную логику, чтобы справиться со всем этим для вас, но это, вероятно, означает, что вам придется добавить некоторые дополнительные вещи, чтобы справиться с откатами и тому подобным.

Итак, мой основной совет - посмотреть, сможете ли вы переписать алгоритм или систему, не прибегая к использованию синглтона.

2 голосов
/ 14 января 2010

Если вы не собираетесь хранить данные в хранилище данных, почему бы вам просто не создать модуль с переменными вместо db.Model?

Назовите свой файл mysettings.py и внутри него напишите:

sitename = "My site"
footer = "This page owned by X"

Тогда модуль python фактически становится «синглтоном». Вы даже можете добавить функции, если это необходимо. Чтобы использовать это, вы делаете что-то вроде этого:

import mysettings
print mysettings.sitename

Вот как django справляется с этим со своими DJANGO_SETTINGS_MODULE

Обновление : Звучит так, будто вы действительно хотите использовать db.Model, но используйте memcached, поэтому вы получаете только один объект один раз. Но вам придется придумать способ очистить его, когда вы меняете данные, или иметь тайм-аут, чтобы он получал время от времени. Я, вероятно, пошел бы с версией тайм-аута и сделал бы что-то подобное в mysettings.py:

from google.appengine.api import memcache
class MySettings(db.Model):
   # properties...

def Settings():
    key = "mysettings"
    obj = memcache.get(key)
    if obj is None:
       obj = MySettings.all().get()  # assume there is only one
       if obj:
            memcache.add(key, zone, 360)
       else:
            logging.error("no MySettings found, create one!")
    return obj

Или, если вы не хотите использовать memcache, просто сохраните объект в переменной уровня модуля и всегда используйте функцию Settings () для ссылки на него. Но тогда вам придется реализовать способ очистки до тех пор, пока экземпляр интерпретатора не будет переработан. Я бы обычно использовал memcached для такого рода функций.

1 голос
/ 12 января 2010

__init__ не может с пользой return ничего: как в первом примере, вместо __new__ переопределить!

0 голосов
/ 22 августа 2012

Я не думаю, что есть настоящий «одноэлементный» объект, который вы можете хранить в распределенной среде с несколькими запущенными экземплярами. Самое близкое к этому можно использовать memcache.

Возможно, лучше думать меньше с точки зрения синглетонов и больше с точки зрения согласованности данных. Для этого App Engine предоставляет транзакции , которые позволяют вам отследить любые изменения в объекте, которые могут произойти во время работы с ним.

...