2010-10-18 79 views
2

我正在使用以下方法来处理基于Google App Engine db.Model的FIFO队列(see this question)。如何实现支持名称空间的FIFO队列

from google.appengine.ext import db 
from google.appengine.ext import webapp 
from google.appengine.ext.webapp import run_wsgi_app 

class QueueItem(db.Model): 
    created = db.DateTimeProperty(required=True, auto_now_add=True) 
    data = db.BlobProperty(required=True) 

    @staticmethod 
    def push(data): 
    """Add a new queue item.""" 
    return QueueItem(data=data).put() 

    @staticmethod 
    def pop(): 
    """Pop the oldest item off the queue.""" 
    def _tx_pop(candidate_key): 
     # Try and grab the candidate key for ourselves. This will fail if 
     # another task beat us to it. 
     task = QueueItem.get(candidate_key) 
     if task: 
     task.delete() 
     return task 
    # Grab some tasks and try getting them until we find one that hasn't been 
    # taken by someone else ahead of us 
    while True: 
     candidate_keys = QueueItem.all(keys_only=True).order('created').fetch(10) 
     if not candidate_keys: 
     # No tasks in queue 
     return None 
     for candidate_key in candidate_keys: 
     task = db.run_in_transaction(_tx_pop, candidate_key) 
     if task: 
      return task 

此队列按预期工作(非常好)。

现在我的代码有访问由延期队列调用这个FIFO队列的方法:

def deferred_worker(): 
     data= QueueItem.pop() 
     do_something_with(data) 

我想加强这方面的方法和队列数据结构将代表一个特定的客户端的client_ID参数需要访问自己的队列。 喜欢的东西:

def deferred_worker(client_ID): 
     data= QueueItem_of_this_client_ID.pop() # I need to implement this 
     do_something_with(data) 

我怎么能编写的队列中CLIENT_ID知道?

约束:
- 客户端的数量是动态的,而不是预定义
- TASKQUEUE不是一个选项(1. 10最大队列2.我想对我的队列完全控制)

待办事项你知道我怎么可以使用新的Namespaces api添加这个行为(记住,我没有从webapp.RequestHandler调用db.Model)?
另一种选择:

QueueItem.all(keys_only=True).filter(client_ID=an_ID).order('created').fetch(10) 

任何更好的想法:我可以用它在拉法的过滤器增加一个client_ID db.StringProperty到QueueItem?

+0

在您的应用中,客户端会话是匿名还是身份验证? – 2010-10-18 21:50:35

+0

@Paulo它是一个类客户端;我没有用户。 – systempuntoout 2010-10-18 21:52:37

+0

我认为命名空间API针对多租户问题域,所以如果你凸轮映射你的类行为到这个问题域,那么这是可能的。 – 2010-10-18 22:57:28

回答

1

假设你的“客户端类”确实是一个请求处理程序的客户端调用,你可以做这样的事情:

from google.appengine.api import users 
from google.appengine.api.namespace_manager import set_namespace 

class ClientClass(webapp.RequestHandler): 
    def get(self): 
    # For this example let's assume the user_id is your unique id. 
    # You could just as easily use a parameter you are passed. 
    user = users.get_current_user() 
    if user: 
     # If there is a user, use their queue. Otherwise the global queue. 
     set_namespace(user.user_id()) 

    item = QueueItem.pop() 
    self.response.out.write(str(item)) 

    QueueItem.push('The next task.') 

或者,你也可以将它的命名空间app-wide

通过设置默认名称空间,除非另有明确规定,否则对该数据存储的所有调用都将位于该名称空间“内”。请注意,要获取并运行任务,您必须知道命名空间。所以你可能想维护默认命名空间中的命名空间列表以进行清理。

+0

@Robert在QueueItem.pop()语句之后,我不必将set_namespace(default_namespace)设置为默认值吗?执行其他数据库操作的并发任务不可能使用错误的名称空间吗?当你调用set_namespace(user.user_id()) 它是全局设置的所有应用程序还是只为webapp.RequestHandler线程? – systempuntoout 2010-10-19 10:06:55

+0

否。如果按照建议设置名称空间,set_namespace将仅适用于**那个**请求。获取名称空间的调用是根据需要由API底层的代码进行的。 – 2010-10-19 14:25:47

+0

@Robert如果你看一下Namespaces Api页面,那么有一个例子就是使用finally来恢复已保存的名称空间。为什么? – systempuntoout 2010-10-19 14:48:47

1

正如我在回答您对原始答案的查询时所说的,您不需要做任何事情就可以使用名称空间进行此项工作:构建队列的数据存储区已支持名称空间。只需按照需要设置命名空间,如the docs中所述。

+0

对不起,我不明白。当我调用set_namespace(..)时,它是否为全部应用程序全局设置?这个调用的范围是否会引发并发问题,为其他并发调用设置错误的名称空间? – systempuntoout 2010-10-19 10:09:54

+0

它在当前请求的全局范围内设置。无论如何,Python运行时是单线程的,所以并发请求不是问题。 – 2010-10-19 22:28:59