代码之家  ›  专栏  ›  技术社区  ›  jamesaharvey

使用python在google app engine的app start中加载全局可访问的singleton

  •  3
  • jamesaharvey  · 技术社区  · 16 年前

    使用google app engine,是否可以在app启动时初始化全局可访问的singleton?我有一个大型的静态树结构,需要在每个请求上使用它,并希望预先对其进行初始化。树结构太大(20+MB),无法放入memcache,我正在尝试找出其他的替代方案。

    编辑:只是为了根据我到目前为止收到的答案增加一些清晰度。我正在将单词字典加载到trie/前缀树结构中。由于单词词典是固定的,因此trie是不变的。我正在基于一个输入字符串生成变位词,因此一个请求可以在一个请求上访问相当数量的trie,可能更多的是1MB,但是我不确定。

    这是我将单词字典加载到的python结构。

    class Node(object):
    
        def __init__(self, letter='', final=False):
            self.letter = letter
            self.final = final
            self.children = {}
    
        def add(self, letters):
            node = self
            for index, letter in enumerate(letters):
                if letter not in node.children:
                    node.children[letter] = Node(letter, index==len(letters)-1)
                node = node.children[letter]
    
    4 回复  |  直到 16 年前
        1
  •  3
  •   Nick Johnson    16 年前

    对于一个请求,您需要访问多少这棵树?您以什么方式查询它?它曾经改变过吗?

    如果它是不可变的,那么就不需要“单例”(singleton),这意味着它是可变的——只需要一种访问每个实例上数据的方法。根据您需要如何访问它,您可以将其存储为数据文件、blob或数据存储在数据存储中。

        2
  •  4
  •   Alex Martelli    16 年前

    每个请求可能来自完全不同的进程,在不同的服务器上,甚至可能位于不同的数据中心(嘿,可能位于不同的 大陆 )对于同一应用程序的不同请求的处理程序来说,除了数据存储之外,没有任何东西可以保证“全局访问”(如果事情太忙,甚至memcache的条目也可能随时消失:这是一个 隐藏物 毕竟!-)

    也许您可以将“静态树结构”保存在与应用程序代码一起上载的数据文件中,并从磁盘访问它而不是“初始化”。

    编辑 :根据要求,这里有一个“轻量级类映射树到数组”方法的粗糙和现成的示例,我在一个注释中提到了这个方法——既没有调优,也没有经过很好的测试。我以一个具有整数有效负载的二进制搜索树为例,并假设出于某种原因,在“轻”树中保持其表示的“重”树中的精确结构是很重要的。即使有了这些简化,仍然有很多代码,但是,下面是:

    import array
    import random
    
    def _doinsert(tree, payload):
      if tree is None: return HeavyTree(payload)
      tree.insert(payload)
      return tree
    
    class HeavyTree(object):
      def __init__(self, payload):
        self.payload = payload
        self.left = self.right = None
      def insert(self, other):
        if other <= self.payload:
          self.left = _doinsert(self.left, other)
        else:
          self.right = _doinsert(self.right, other)
      def walk(self):
        if self.left:
          for x in self.left.walk(): yield x
        yield self.payload
        if self.right:
          for x in self.right.walk(): yield x
      def walknodes(self):
        yield self
        if self.left:
          for x in self.left.walknodes(): yield x
        if self.right:
          for x in self.right.walknodes(): yield x
    
    data = [random.randint(0, 99) for _ in range(9)]
    print 'data: ',
    for x in data: print x,
    print
    theiter = iter(data)
    thetree = HeavyTree(next(theiter))
    for x in theiter: thetree.insert(x)
    
    print
    print 'Heavy tree:'
    print 'nodes:',
    for x in thetree.walknodes(): print x.payload,
    print
    print 'inord:',
    for x in thetree.walk(): print x,
    print
    
    class LightTree(HeavyTree):
      def __init__(self, base, offset):
        self.base = base
        self.offset = offset
      @property
      def payload(self):
        return self.base[self.offset]
      @property
      def left(self):
        return self._astree(self.offset+1)
      @property
      def right(self):
        return self._astree(self.offset+2)
      def _astree(self, i):
        offset = self.base[i]
        if offset < 0: return None
        return LightTree(self.base, offset)
    
    def heavy_to_light(heavy):
      for i, node in enumerate(heavy.walknodes()):
        node.id = i * 3
      base = array.array('l', (i+1) * 3 * [-1])
      for node in heavy.walknodes():
        base[node.id] = node.payload
        if node.left: base[node.id+1] = node.left.id
        if node.right: base[node.id+2] = node.right.id
      return LightTree(base, 0)
    
    print
    print 'Light tree:'
    light = heavy_to_light(thetree)
    print 'nodes:',
    for x in light.walknodes(): print x.payload,
    print
    print 'base :',
    for x in light.base: print x,
    print
    print 'inord:',
    for x in light.walk(): print x,
    print
    

    典型运行将显示:

    data:  27 79 90 60 82 80 3 94 76
    
    Heavy tree:
    nodes: 27 3 79 60 76 90 82 80 94
    inord: 3 27 60 76 79 80 82 90 94
    
    Light tree:
    nodes: 27 3 79 60 76 90 82 80 94
    base : 27 3 6 3 -1 -1 79 9 15 60 -1 12 76 -1 -1 90 18 24 82 21 -1 80 -1 -1 94 -1 -1
    inord: 3 27 60 76 79 80 82 90 94
    

    当然,每次都有详细的变量,因为数据是随机生成的。

    也许这类事情对于任何一个没有开始使用好的旧Fortran的人来说太麻烦了(因此不可避免地学会了如何将逻辑指针表示为数组的索引),就像我几十年前在EE学校所做的那样;-。但是从文件直接将这样的数组加载到内存中 非常快(相比于不粘东西之类的东西)。-)

        3
  •  3
  •   Liron Shapira    16 年前

    我认为Google为运行您的项目的每个实例提供了300MB的本地内存。所以您所要做的就是将树结构存储到某个模块的变量中。

    每当Google为你的应用程序启动一个新的进程时,它会运行一次代码来构建你的树,然后你就可以访问它来处理将来的请求。只要确保构建树不到30秒,因为它必须在任何随机请求的时间范围内发生,这使得谷歌决定启动一个新的进程。

        4
  •  0
  •   Peter Recore    16 年前

    Memcache和数据存储是跨所有实例进行真正全局访问的最佳选择。但是,全局变量仍然可以用于缓存每个实例中的数据结构。trie结构不是很容易分解成块,以便每个块都能放入memcache吗?一旦将trie放入memcache,只要从memcache访问trie的一块,就可以将其存储在该实例的全局变量中。在几个请求过程中,您将在运行的每个实例上构建一个trie的完整副本。这有点复杂,但最终会给你最好的表现。