我有一个 Google App Engine 应用程序,它使用 Google Cloud SQL 实例来存储数据。我需要我的实例能够通过 restful 调用一次为数百个客户端提供服务,每个调用都会导致一个或几个数据库查询。我已经包装了需要数据库访问的方法,并将数据库连接的句柄存储在 os.environ 中。请参阅此SO 问题/答案,了解我的基本操作方式。
但是,一旦有几百个客户端连接到我的应用程序并触发数据库调用,我就会开始在 Google App Engine 错误日志中收到这些错误(当然,我的应用程序返回 500):
could not connect: ApplicationError: 1033 Instance has too many concurrent requests: 100 Traceback (most recent call last): File "/base/python27_run
有经验的 Google App Engine 和 Google Cloud SQL 用户有什么建议吗?提前致谢。
这是我在需要数据库连接的方法周围使用的装饰器的代码:
def with_db_cursor(do_commit = False):
""" Decorator for managing DB connection by wrapping around web calls.
Stores connections and open connection count in the os.environ dictionary
between calls. Sets a cursor variable in the wrapped function. Optionally
does a commit. Closes the cursor when wrapped method returns, and closes
the DB connection if there are no outstanding cursors.
If the wrapped method has a keyword argument 'existing_cursor', whose value
is non-False, this wrapper is bypassed, as it is assumed another cursor is
already in force because of an alternate call stack.
Based mostly on post by : Shay Erlichmen
At: https://stackoverflow.com/a/10162674/379037
"""
def method_wrap(method):
def wrap(*args, **kwargs):
if kwargs.get('existing_cursor', False):
#Bypass everything if method called with existing open cursor
vdbg('Shortcircuiting db wrapper due to exisiting_cursor')
return method(None, *args, **kwargs)
conn = os.environ.get("__data_conn")
# Recycling connection for the current request
# For some reason threading.local() didn't work
# and yes os.environ is supposed to be thread safe
if not conn:
conn = _db_connect()
os.environ["__data_conn"] = conn
os.environ["__data_conn_ref"] = 1
dbg('Opening first DB connection via wrapper.')
else:
os.environ["__data_conn_ref"] = (os.environ["__data_conn_ref"] + 1)
vdbg('Reusing existing DB connection. Count using is now: {0}',
os.environ["__data_conn_ref"])
try:
cursor = conn.cursor()
try:
result = method(cursor, *args, **kwargs)
if do_commit or os.environ.get("__data_conn_commit"):
os.environ["__data_conn_commit"] = False
dbg('Wrapper executing DB commit.')
conn.commit()
return result
finally:
cursor.close()
finally:
os.environ["__data_conn_ref"] = (os.environ["__data_conn_ref"] -
1)
vdbg('One less user of DB connection. Count using is now: {0}',
os.environ["__data_conn_ref"])
if os.environ["__data_conn_ref"] == 0:
dbg("No more users of this DB connection. Closing.")
os.environ["__data_conn"] = None
db_close(conn)
return wrap
return method_wrap
def db_close(db_conn):
if db_conn:
try:
db_conn.close()
except:
err('Unable to close the DB connection.', )
raise
else:
err('Tried to close a non-connected DB handle.')