Обработайте перезапуск mysql в SQLAlchemy

Мое приложение Опор использует локальный сервер 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 существует решение?

8
задан wRAR 13 June 2010 в 17:39
поделиться

2 ответа

См. РЕДАКТИРОВАТЬ внизу для проверенного решения

Я не пробовал , но, может быть, можно использовать 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) и она работает . :)

6
ответ дан 5 December 2019 в 18:56
поделиться

Вы можете использовать прокси 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)
3
ответ дан 5 December 2019 в 18:56
поделиться
Другие вопросы по тегам:

Похожие вопросы: