部署在Google-App-Engine上的Python应用程序很少会引发异常

时间:2014-01-22 13:43:48

标签: google-app-engine python-2.7 stateless-session

我已经使用Python在Google-App-Engine上部署了一个网站。

由于GAE不保证“保持活着”,我已经实现了无状态服务器:

  1. 内部变量每次更改后,都会立即存储到GQL数据库中。

  2. 每当进程启动时,所有内部变量都从GQL数据库加载。

  3. 我有一个很少抛出异常的场景,我无法追踪它:

    • 客户端发送同步AJAX POST请求。

    • 服务器创建会话并在响应中发送唯一的会话ID。

    • 客户端发送一个同步的AJAX GET请求,其中session-ID为参数。

    • 服务器在响应中发送一些短信。

    由于客户端请求是同步的,因此整个序列也是同步的。

    以下是我服务器中的相关映射:

    from webapp2 import WSGIApplication
    from Handler import MyRequestHandler
    
    app = WSGIApplication([
        ('/request1'    ,MyRequestHandler), # post request
        ('/request2(.*)',MyRequestHandler), # get request
    ])
    

    以下是我服务器内的相关请求处理:

    from webapp2 import RequestHandler
    from Server  import MyServer
    
    myServer = MyServer()
    
    class MyRequestHandler(RequestHandler):
        def post(self):
            try:
                if self.request.path.startswith('/request1'):
                    sessionId = myServer.GetNewSessionId()
                    self.SendContent('text/plain',sessionId)
            except Exception,error:
                self.SendError(error)
        def get(self,sessionId):
            try:
                if self.request.path.startswith('/request2'):
                    textMessage = myServer.GetMsg(sessionId)
                    self.SendContent('text/plain',textMessage)
            except Exception,error:
                self.SendError(error)
        def SendContent(self,contentType,contentData):
            self.response.set_status(200)
            self.response.headers['content-type'] = contentType
            self.response.headers['cache-control'] = 'no-cache'
            self.response.write(contentData)
        def SendError(self,error):
            self.response.set_status(500)
            self.response.write(error.message)
    

    以下是我服务器的内部实现:

    class MyServer():
        def __init__(self):
            self.sessions = SessionsTable.ReadSessions()
        def GetNewSessionId(self):
            while True:
                sessionId = ... # a 16-digit random number
                if SessionsTable.ReserveSession(sessionId):
                    self.sessions[sessionId] = ... # a text message
                    SessionsTable.WriteSession(self.sessions,sessionId)
                    return sessionId
        def GetMsg(self,sessionId):
            return self.sessions[sessionId]
    

    最后,这是我服务器中的数据库维护:

    from google.appengine.ext import db
    
    class SessionsTable(db.Model):
        message = db.TextProperty()
        @staticmethod
        def ReadSessions():
            sessions = {}
            for session in SessionsTable.all():
                sessions[session.key().name()] = session.message
            return sessions
        @staticmethod
        @db.transactional
        def ReserveSession(sessionId):
            if not SessionsTable.get_by_key_name(sessionId):
                SessionsTable(key_name=sessionId,message='').put()
                return True
            return False
        @staticmethod
        def WriteSession(sessions,sessionId):
            SessionsTable(key_name=sessionId,message=sessions[sessionId]).put()
        @staticmethod
        def EraseSession(sessionId):
            SessionsTable.get_by_key_name(sessionId).delete()
    

    异常本身表示使用sessions密钥非法访问sessionId字典。根据我的观察,只有在服务器“已经睡眠”一段相对较长的时间(如几天左右)之后才开始在本问题开头描述的客户端 - 服务器序列时,才会发生这种情况。它可能会为这个问题的根源提供某种线索,但我无法看到它。

    我的问题:

    1. 我的设计有什么明显错误吗?

    2. 有没有人在GAE上遇到过类似的问题?

    3. 有没有人看到一个明显的解决方案,甚至是一个可能有助于理解这个问题的调试方法?

    4. 由于

1 个答案:

答案 0 :(得分:3)

您错误地认为所有请求都由同一个实例处理。事实并非如此:GAE与大多数托管环境一样,无法保证哪个服务器进程可以处理任何请求。

您的实现使用的是模块级变量myServer,它是一个具有自己的实例变量的类实例。但是每个服务器进程都有自己的myServer实例,并且它们不在进程之间共享:因此在一个请求中创建的字典条目不一定存在于第二个。

您需要了解跨实例保存此数据的方法。真的,这就是数据存储的第一个用途;如果你担心开销,你应该调查使用memcache。