【发布时间】:2012-05-12 13:24:22
【问题描述】:
我有一个使用 Google Cloud SQL 实例存储数据的 Google App Engine 应用。我需要我的实例能够通过 restful 调用一次为数百个客户端提供服务,每个调用都会导致一个或几个数据库查询。我已经包装了需要数据库访问的方法,并将数据库连接的句柄存储在 os.environ 中。请参阅this SO question/answer,了解我的基本做法。
但是,一旦有几百个客户端连接到我的应用并触发数据库调用,我就会开始在 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.')
【问题讨论】:
-
app.yaml 中有 threadsafe: true 吗?
-
使用 'threadsafe: true' 不能很好地与使用 os.environ 一起工作,因为连接不能跨线程共享。有关线程安全的解决方案,请参阅我的回答 stackoverflow.com/a/10438622/1373093。
-
@JJC 清除了很多东西。如果您知道的话,cloud.google.com/sql/docs/diagnose-issues#limits 和 cloud.google.com/sql/pricing#v1-pricing 提到的每层 100 个待处理连接和 250 个并发连接之间有什么区别
-
@Kartik 抱歉,我不知道。 (但你应该是一个很好的用户,并为你在 Stack Overflow 上阅读的好问题/答案投票。:-))
标签: python mysql google-app-engine wsgi google-cloud-sql