Мое приложение Опор использует локальный сервер MySQL через SQLAlchemy и python-MySQLdb. То, когда сервер перезапущен, открытые объединенные соединения, по-видимому, закрываются, но приложение не знает об этом и по-видимому когда это пытается использовать такое соединение, это получает "сервер MySQL, ушло":
File '/usr/lib/pymodules/python2.6/sqlalchemy/engine/default.py', line 277 in do_execute
cursor.execute(statement, parameters)
File '/usr/lib/pymodules/python2.6/MySQLdb/cursors.py', line 166 in execute
self.errorhandler(self, exc, value)
File '/usr/lib/pymodules/python2.6/MySQLdb/connections.py', line 35 in defaulterrorhandler
raise errorclass, errorvalue
OperationalError: (OperationalError) (2006, 'MySQL server has gone away')
Это исключение не поймано нигде, таким образом, оно пузырится до пользователя. Если я должен обработать это исключение где-нибудь в моем коде, покажите место для такого кода в Опоры приложение WSGI. Или возможно в самом SA существует решение?
См. РЕДАКТИРОВАТЬ внизу для проверенного решения
Я не пробовал , но, может быть, можно использовать PoolListener ?
Вы можете сделать что-то вроде этого:
class MyListener(sqlalchemy.interfaces.PoolListener):
def __init__(self):
self.retried = False
def checkout(self, dbapi_con, con_record, con_proxy):
try:
dbapi_con.info() # is there any better way to simply check if connection to mysql is alive?
except sqlalchemy.exc.OperationalError:
if self.retried:
self.retried = False
raise # we do nothing
self.retried = True
raise sqlalchemy.exc.DisconnectionError
# next, code according to documentation linked above follows
e = create_engine("url://", listeners=[MyListener()])
Таким образом каждый раз, когда соединение будет проверяться из пула, мы проверяем, действительно ли оно подключено к серверу. Если нет, мы даем sqlalchemy один шанс восстановить соединение. После этого, если проблема все еще существует, мы ее отпускаем.
PS: Я не проверял, работает ли это.
Редактировать: Что касается пилонов, то изменения в инициализации движка, показанные выше, должны быть выполнены в your_app.model.init_model (Pylons 0.9.7) или your_app.config.environment.load_environment (Pylons 1.0) функция - это это места место, где создается экземпляр движка.
РЕДАКТИРОВАТЬ
Хорошо. Мне удалось воспроизвести описанную ситуацию.Для работы приведенного выше кода необходимо внести некоторые изменения. Ниже описано, как это сделать. Также не имеет значения, 0.9.7 или 1.0.
Вам необходимо отредактировать your_app / config / environment.py. Поместите эти экспорты вверху файла:
import sqlalchemy
import sqlalchemy.interfaces
import _mysql_exceptions
И конец функции load_environment должен выглядеть так:
class MyListener(sqlalchemy.interfaces.PoolListener):
def __init__(self):
self.retried = False
def checkout(self, dbapi_con, con_record, con_proxy):
try:
dbapi_con.cursor().execute('select now()')
except _mysql_exceptions.OperationalError:
if self.retried:
self.retried = False
raise
self.retried = True
raise sqlalchemy.exc.DisconnectionError
config['sqlalchemy.listeners'] = [MyListener()]
engine = engine_from_config(config, 'sqlalchemy.')
init_model(engine)
На этот раз я смог протестировать ее (на Pylons 1.0 + SQLAlchemy 0.6.1) и она работает . :)
Вы можете использовать прокси SQLAlchemy для обработки исключений в каждом запросе sql:
from sqlalchemy.interfaces import ConnectionProxy
class MyProxy(ConnectionProxy):
def cursor_execute(self, execute, cursor, statement, parameters, context, executemany):
try:
return execute(cursor, statement, parameters, context)
except sqlalchemy.exc.OperationalError:
# Handle this exception
pass
Чтобы подключить этот прокси, вы должны сделать это в config / enviroment.py
engine = engine_from_config(config, 'sqlalchemy.', proxy=MyProxy())
Или написать промежуточное ПО для обработка исключений для каждого http-запроса:
class MyMiddleware(object):
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
try:
return self.app(environ, start_response)
except sqlalchemy.exc.OperationalError:
start_response(
'500 Internal Server Error',
[('content-type', 'text/html')])
return ['error page\n']
Чтобы подключить это промежуточное ПО в нужном вам порядке стека или просто в config / middleware.py:
# CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares)
app = MyMiddleware(app)