MySQL OperationalError при запуске сервера Flask (Apache) в течение нескольких дней

У меня есть Flask- сервер под Apache, который я использую в качестве API-интерфейса для приложения, и когда сервер работает в течение 2 – 3 дней, он внезапно перестает работать и поднимает OperationalError: MySQL Connection not available.

Ошибка всегда возникает при login в login потому что это первый вызов, когда приложение открывается (но все методы следуют одному и тому же шаблону).

Это метод login в login :

 @app.route(LOGIN_API_URL, methods=['POST']) def login(): if (request.method == 'POST'): cursor = connection.cursor(buffered=True, dictionary=True) cursor.execute('select * from users where username = %s', (request.form['username'],)) user = cursor.fetchone() if user is None or user['password'] != str(request.form['password']): abort(403) else: cursor.execute('update users set last_login = (%s) where user_id = %s', str(int(round(time.time() * 1000))), user['user_id'],) utils.safe_commit(connection, cursor) return utils.sanitize_response({'status':200, 'message':'Logged in'}) 

И safe_commit и sanitize_response следует:

 def sanitize_response(response, is_array=False): if response is None: return '[]' if is_array else '{}' else: return jsonify(response) def safe_commit(connection, cursor): try: connection.commit() except: connection.rollback() finally: cursor.close() 

Сначала я думал, что проблема происходит, потому что я не использовал buffered=True в курсоре, который вызывает метод fetchone . Но после этого я добавил этот параметр.

Это мой файл wsgi :

 #!/usr/bin/python import sys sys.path.append("/var/www/protestr/") from protestr import app as application 

Это sites-available мне файл conf (я хотел сказать, что я пробовал множество комбинаций параметров threads и processes и эта комбинация поддерживает работу сервера в течение максимального времени, обычно 2 – 3 дня):

 <VirtualHost *:80> ServerName protestr.tk DocumentRoot /var/www/protestr/ WSGIDaemonProcess protestr user=www-data group=www-data processes=2 threads=25 WSGIScriptAlias / /var/www/protestr/protestr.wsgi <Directory /var/www/protestr> WSGIProcessGroup protestr WSGIApplicationGroup %{GLOBAL} Require all granted </Directory> </VirtualHost> 

Это содержимое файла error.log :

 [Fri May 12 03:34:14.967624 2017] [wsgi:error] [pid 18673:tid 2849002544] [remote 192.168.1.139:25727] [2017-05-12 03:34:14,963] ERROR in app: Exception on /api/v1/users/login [POST] [Fri May 12 03:34:14.967812 2017] [wsgi:error] [pid 18673:tid 2849002544] [remote 192.168.1.139:25727] Traceback (most recent call last): [Fri May 12 03:34:14.967861 2017] [wsgi:error] [pid 18673:tid 2849002544] [remote 192.168.1.139:25727] File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1982, in wsgi_app [Fri May 12 03:34:14.967900 2017] [wsgi:error] [pid 18673:tid 2849002544] [remote 192.168.1.139:25727] response = self.full_dispatch_request() [Fri May 12 03:34:14.967937 2017] [wsgi:error] [pid 18673:tid 2849002544] [remote 192.168.1.139:25727] File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1614, in full_dispatch_request [Fri May 12 03:34:14.967973 2017] [wsgi:error] [pid 18673:tid 2849002544] [remote 192.168.1.139:25727] rv = self.handle_user_exception(e) [Fri May 12 03:34:14.968007 2017] [wsgi:error] [pid 18673:tid 2849002544] [remote 192.168.1.139:25727] File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1517, in handle_user_exception [Fri May 12 03:34:14.968043 2017] [wsgi:error] [pid 18673:tid 2849002544] [remote 192.168.1.139:25727] reraise(exc_type, exc_value, tb) [Fri May 12 03:34:14.968076 2017] [wsgi:error] [pid 18673:tid 2849002544] [remote 192.168.1.139:25727] File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1612, in full_dispatch_request [Fri May 12 03:34:14.968111 2017] [wsgi:error] [pid 18673:tid 2849002544] [remote 192.168.1.139:25727] rv = self.dispatch_request() [Fri May 12 03:34:14.968144 2017] [wsgi:error] [pid 18673:tid 2849002544] [remote 192.168.1.139:25727] File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1598, in dispatch_request [Fri May 12 03:34:14.968179 2017] [wsgi:error] [pid 18673:tid 2849002544] [remote 192.168.1.139:25727] return self.view_functions[rule.endpoint](**req.view_args) [Fri May 12 03:34:14.968251 2017] [wsgi:error] [pid 18673:tid 2849002544] [remote 192.168.1.139:25727] File "/var/www/protestr/protestr.py", line 89, in login [Fri May 12 03:34:14.968290 2017] [wsgi:error] [pid 18673:tid 2849002544] [remote 192.168.1.139:25727] cursor = connection.cursor(buffered=True, dictionary=True) [Fri May 12 03:34:14.968326 2017] [wsgi:error] [pid 18673:tid 2849002544] [remote 192.168.1.139:25727] File "/usr/local/lib/python2.7/dist-packages/mysql/connector/connection.py", line 809, in cursor [Fri May 12 03:34:14.968363 2017] [wsgi:error] [pid 18673:tid 2849002544] [remote 192.168.1.139:25727] raise errors.OperationalError("MySQL Connection not available.") [Fri May 12 03:34:14.968399 2017] [wsgi:error] [pid 18673:tid 2849002544] [remote 192.168.1.139:25727] OperationalError: MySQL Connection not available. 

ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ:

Я запускаю Apache / 2.4.10 под armbian (Debian) в банановой Pi.

Я действительно не знаю, почему сервер останавливается после работы некоторое время, я думаю, что я попробовал почти все.


EDIT: я добавил также cursor.close() прежде чем выбросить ошибку 403 в метод login . Но это не актуально, поскольку я единственный, кто регистрируется в приложении, и я всегда ввожу правильные учетные данные.

EDIT 2: Как сказал @stamaimer, если я добавлю connection.ping() прежде чем получить какой-либо курсор, он работает хорошо, но этот подход кажется мне взломанным для меня способом, и я не знаю, является ли это хорошим решением или даже почему сервер MySQL отключает соединение.

2 Solutions collect form web for “MySQL OperationalError при запуске сервера Flask (Apache) в течение нескольких дней”

На основе ответа @ 9000s ping должен работать нормально при вызове с параметром reconnect=True , см. Соответствующий код . Это вызовет ping, и в случае неудачного пинга попытайтесь снова подключиться к базе данных.

Хотя это должно исправить ошибку, вам нужно немного разобраться, чтобы найти основную проблему, которая является нефункциональным подключением к MySQL.

Как уже упоминалось, это может быть результатом нескольких источников, поэтому, возможно, вы можете использовать следующий список, чтобы вести поиск:

  • Есть ли другие сообщения об ошибках в журнале, перед тем, который вы опубликовали?
  • Вы подключаетесь к MySQL через TCP / IP или через сокет? Возможно, попробуйте другой вариант, если он работает, а другой – нет, это может помочь выявить проблему.
  • Увеличьте max_timeout до большого значения, просто чтобы убедиться, что он имеет эффект.
  • Вы также можете увеличить max_allowed_packet , так как это помогло в других случаях .
  • Проверьте журналы сервера MySQL на наличие возможных проблем с потерянными соединениями.
  • Также проверьте syslog операционной системы, потому что MySQL может быть убит из-за низкой памяти , что можно устранить , установив лучшие значения в config .

Надеюсь, это поможет вам найти причину.

Этот пример как-то груб, но, надеюсь, показывает логику вокруг обработки отключений. Специфика зависит от способа получения соединений в конкретной структуре.

В приведенном ниже коде предполагается использование retry ; при необходимости вы можете использовать другую логику повтора.

Он также предполагает, что вы используете пул соединений с вашим приложением, предоставляемым инфраструктурой. Обычно вы можете попросить его повторно подключиться или, по крайней мере, закрыть незанятые подключения, чтобы другой запрос на соединение создавал новый.

 def reconnect_on_failure(func): @retry(OperationalError, delay=0.25, backoff=1.5, max_delay=5) @wraps(func) def reconnecting_func(*args, **kwargs): try: return func(*args, **kwargs) except OperationalError as e: if 'connect' in e.msg.lower(): force_reconnection_somehow() # Look at your connection pool API. raise # We want to retry on it raise Exception('Unhandled MySQL error', e) # Will not retry. return reconnecting_func @reconnect_on_failure def something(...): connection = get_connecton_somehow() # Look at the framework API. # A transaction implicitly begins with the first statement executed. cursor = connection.cursor() result = cursor.execute(...) # do stuff connection.commit() 

Вместо чрезмерно широкого Exception вы можете использовать более узкий класс, например, определенный для вашего приложения; идея заключается в том, что повышение чего-либо, кроме OperationalError , не приведет к запуску повторных попыток, и оно немедленно вызовет Exception чтобы сообщить о проблеме.

EDIT от Grender: я добавил декоратор @wraps , чтобы избежать AssertionError как видно здесь .

Python - лучший язык программирования в мире.