【发布时间】:2014-02-12 14:37:43
【问题描述】:
我已经使用 Python 在 Google-App-Engine 上部署了一个网站。
由于GAE不保证“keep-alive”,所以我实现了一个无状态服务器:
每次内部变量发生变化时,它们都会立即存储到 GQL 数据库中。
每当进程启动时,所有内部变量都会从 GQL 数据库中加载。
我有一个场景很少抛出异常,但我一直无法追踪它:
客户端发送一个同步的 AJAX POST 请求。
服务器创建一个会话并在响应中发送一个唯一的会话 ID。
客户端发送一个以会话 ID 作为参数的同步 AJAX GET 请求。
服务器在响应中发送一些文本消息。
由于客户端请求是同步的,所以整个序列也是同步的。
这是我服务器中的相关映射:
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()
异常本身表明使用sessionId 键对sessions 字典的非法访问。根据我的观察,只有在服务器“已经休眠”了一段相对较长的时间(比如几天左右)之后启动本问题开头描述的客户端-服务器序列时才会发生这种情况。虽然我看不到它,但它可能会提供某种线索来解决这个问题的根源。
我的问题:
我的设计有什么明显的问题吗?
有人在 GAE 上遇到过类似的问题吗?
有没有人看到明显的解决方案,甚至是调试方法可能有助于理解这个问题?
谢谢
【问题讨论】:
-
使用类级变量可能是个坏主意。如果您启用了线程,那么您可能会泄漏信息。
-
你指的是
class SessionsTable吗?我没有这个类的实例,所以这里应该没有任何问题,对吧?或者你的意思是全局变量myServer可能有问题,没有得到适当的保护?我没有使用任何线程,所以我有理由“担心”GAE 隐式创建它们吗?谢谢 -
所有这些都可能是个问题。如果您启用了线程,那么 appengine 将隐式创建线程来服务每个请求(如果实例快速响应)。因此,您必须将您的应用程序设计为线程安全的。永远不要在模块级别存储东西,或者作为类属性存储,除非它可以共享。即它可以作为共享数据的实例级缓存。
标签: google-app-engine python-2.7 stateless-session