6
votes

Périodique «Connexion perdue au serveur MySQL pendant la requête» après l'ancrage de Flask Web App

J'ai une application Web Flask qui fonctionnait sur un serveur autonome en utilisant les éléments suivants:

  • Flacon / SQLAlchemy
  • MariaDB
  • uwsgi
  • nginx

Sur le serveur autonome, cette application fonctionnait correctement.

J'ai depuis "dockerized" cette application sur deux conteneurs:

Depuis la dockerisation, j'obtiens parfois cette erreur (tout le suivi posté à la fin):

Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 1193, in _execute_context
    context)
  File "/usr/local/lib/python3.6/site-packages/sqlalchemy/engine/default.py", line 509, in do_execute
    cursor.execute(statement, parameters)
  File "/usr/local/lib/python3.6/site-packages/pymysql/cursors.py", line 170, in execute
    result = self._query(query)
  File "/usr/local/lib/python3.6/site-packages/pymysql/cursors.py", line 328, in _query
    conn.query(q)
  File "/usr/local/lib/python3.6/site-packages/pymysql/connections.py", line 517, in query
    self._affected_rows = self._read_query_result(unbuffered=unbuffered)
  File "/usr/local/lib/python3.6/site-packages/pymysql/connections.py", line 732, in _read_query_result
    result.read()
  File "/usr/local/lib/python3.6/site-packages/pymysql/connections.py", line 1075, in read
    first_packet = self.connection._read_packet()
  File "/usr/local/lib/python3.6/site-packages/pymysql/connections.py", line 657, in _read_packet
    packet_header = self._read_bytes(4)
  File "/usr/local/lib/python3.6/site-packages/pymysql/connections.py", line 707, in _read_bytes
    CR.CR_SERVER_LOST, "Lost connection to MySQL server during query")
pymysql.err.OperationalError: (2013, 'Lost connection to MySQL server during query')

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/flask/app.py", line 2292, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/local/lib/python3.6/site-packages/flask/app.py", line 1815, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/usr/local/lib/python3.6/site-packages/flask/app.py", line 1718, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/local/lib/python3.6/site-packages/flask/_compat.py", line 35, in reraise
    raise value
  File "/usr/local/lib/python3.6/site-packages/flask/app.py", line 1813, in full_dispatch_request
    rv = self.dispatch_request()
  File "/usr/local/lib/python3.6/site-packages/flask/app.py", line 1799, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/usr/local/lib/python3.6/site-packages/flask_user/decorators.py", line 132, in decorator
    allowed = _is_logged_in_with_confirmed_email(user_manager)
  File "/usr/local/lib/python3.6/site-packages/flask_user/decorators.py", line 17, in _is_logged_in_with_confirmed_email
    if user_manager.call_or_get(current_user.is_authenticated):
  File "/usr/local/lib/python3.6/site-packages/werkzeug/local.py", line 347, in __getattr__
    return getattr(self._get_current_object(), name)
  File "/usr/local/lib/python3.6/site-packages/werkzeug/local.py", line 306, in _get_current_object
    return self.__local()
  File "/usr/local/lib/python3.6/site-packages/flask_login/utils.py", line 26, in <lambda>
    current_user = LocalProxy(lambda: _get_user())
  File "/usr/local/lib/python3.6/site-packages/flask_login/utils.py", line 335, in _get_user
    current_app.login_manager._load_user()
  File "/usr/local/lib/python3.6/site-packages/flask_login/login_manager.py", line 359, in _load_user
    return self.reload_user()
  File "/usr/local/lib/python3.6/site-packages/flask_login/login_manager.py", line 321, in reload_user
    user = self.user_callback(user_id)
  File "/usr/local/lib/python3.6/site-packages/flask_user/user_manager.py", line 130, in load_user_by_user_token
    user = self.db_manager.UserClass.get_user_by_token(user_token)
  File "/usr/local/lib/python3.6/site-packages/flask_user/user_mixin.py", line 51, in get_user_by_token
    user = user_manager.db_manager.get_user_by_id(user_id)
  File "/usr/local/lib/python3.6/site-packages/flask_user/db_manager.py", line 179, in get_user_by_id
    return self.db_adapter.get_object(self.UserClass, id=id)
  File "/usr/local/lib/python3.6/site-packages/flask_user/db_adapters/sql_db_adapter.py", line 48, in get_object
    return ObjectClass.query.get(id)
  File "/usr/local/lib/python3.6/site-packages/sqlalchemy/orm/query.py", line 924, in get
    ident, loading.load_on_pk_identity)
  File "/usr/local/lib/python3.6/site-packages/sqlalchemy/orm/query.py", line 1007, in _get_impl
    return db_load_fn(self, primary_key_identity)
  File "/usr/local/lib/python3.6/site-packages/sqlalchemy/orm/loading.py", line 250, in load_on_pk_identity
    return q.one()
  File "/usr/local/lib/python3.6/site-packages/sqlalchemy/orm/query.py", line 2954, in one
    ret = self.one_or_none()
  File "/usr/local/lib/python3.6/site-packages/sqlalchemy/orm/query.py", line 2924, in one_or_none
    ret = list(self)
  File "/usr/local/lib/python3.6/site-packages/sqlalchemy/orm/query.py", line 2995, in __iter__
    return self._execute_and_instances(context)
  File "/usr/local/lib/python3.6/site-packages/sqlalchemy/orm/query.py", line 3018, in _execute_and_instances
    result = conn.execute(querycontext.statement, self._params)
  File "/usr/local/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 948, in execute
    return meth(self, multiparams, params)
  File "/usr/local/lib/python3.6/site-packages/sqlalchemy/sql/elements.py", line 269, in _execute_on_connection
    return connection._execute_clauseelement(self, multiparams, params)
  File "/usr/local/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 1060, in _execute_clauseelement
    compiled_sql, distilled_params
  File "/usr/local/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 1200, in _execute_context
    context)
  File "/usr/local/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 1413, in _handle_dbapi_exception
    exc_info
  File "/usr/local/lib/python3.6/site-packages/sqlalchemy/util/compat.py", line 265, in raise_from_cause
    reraise(type(exception), exception, tb=exc_tb, cause=cause)
  File "/usr/local/lib/python3.6/site-packages/sqlalchemy/util/compat.py", line 248, in reraise
    raise value.with_traceback(tb)
  File "/usr/local/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 1193, in _execute_context
    context)
  File "/usr/local/lib/python3.6/site-packages/sqlalchemy/engine/default.py", line 509, in do_execute
    cursor.execute(statement, parameters)
  File "/usr/local/lib/python3.6/site-packages/pymysql/cursors.py", line 170, in execute
    result = self._query(query)
  File "/usr/local/lib/python3.6/site-packages/pymysql/cursors.py", line 328, in _query
    conn.query(q)
  File "/usr/local/lib/python3.6/site-packages/pymysql/connections.py", line 517, in query
    self._affected_rows = self._read_query_result(unbuffered=unbuffered)
  File "/usr/local/lib/python3.6/site-packages/pymysql/connections.py", line 732, in _read_query_result
    result.read()
  File "/usr/local/lib/python3.6/site-packages/pymysql/connections.py", line 1075, in read
    first_packet = self.connection._read_packet()
  File "/usr/local/lib/python3.6/site-packages/pymysql/connections.py", line 657, in _read_packet
    packet_header = self._read_bytes(4)
  File "/usr/local/lib/python3.6/site-packages/pymysql/connections.py", line 707, in _read_bytes
    CR.CR_SERVER_LOST, "Lost connection to MySQL server during query")
sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (2013, 'Lost connection to MySQL server during query') [SQL: 'SELECT user.is_active AS user_is_active, user.id AS user_id, user.username AS user_username, user.password AS user_password, user.reset_password_token AS user_reset_password_token, user.email AS user_email, user.email_confirmed_at AS user_email_confirmed_at, user.first_name AS user_first_name, user.last_name AS user_last_name \nFROM user \nWHERE user.id = %(param_1)s'] [parameters: {'param_1': 13}] (Background on this error at: http://sqlalche.me/e/e3q8)

Le journal MariaDB affiche les erreurs suivantes avec une journalisation détaillée:

version: "3.7"
services:
  db:
    restart: "always"
    build: ./docker/db
    volumes:
      - "~/db:/var/lib/mysql"
    environment:
      MYSQL_ROOT_PASSWORD: "password"
      MYSQL_DATABASE: "database"
      MYSQL_USER: "user"
      MYSQL_PASSWORD: "password"
    ports:
      - '3306:3306'
  nginx-uwsgi-flask:
    restart: "always"
    depends_on:
      - "db"
    build:
      context: .
      dockerfile: ./docker/nginx-uwsgi-flask/Dockerfile
    volumes:
      - "~/data/fileshare:/fileshare"
    ports:
      - "80:80"
      - "443:443"
    network_mode: "host"

Ceci est vécu par l'utilisateur comme une 502 Bad Gateway . Si l'utilisateur actualise la page, cela résoudra souvent le problème. Ce problème survient au hasard. Je n'ai pas pu le reproduire à volonté, mais avec le temps il apparaîtra inévitablement.

Qu'est-ce qui cause ce problème et comment puis-je le résoudre?

Ce que j'ai fait:

  • Vérifié que le conteneur MariaDB a un délai d'expiration est 28800. J'ai vu l'erreur se produire beaucoup plus tôt que 28800 secondes après le redémarrage de tous les conteneurs, donc je ne pense pas que ce soit en fait un problème de délai.
  • Définissez l'option pool_recycle sur 120
  • Vérifié que Flask-SQLAlchemy utilise un scoped_session qui devrait éviter ces problèmes de temporisation.
  • Changement de network_mode par défaut par un commentaire. Cela n'a pas résolu le problème.

Je pense que cela agit comme si la connexion entre flask et la base de données n'était pas fiable, mais en tant que conteneurs docker fonctionnant sur le même hôte, cela ne devrait-il pas être assez fiable?

Code pertinent:

database.py

class Config:
    ...
    SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://username:password@127.0.0.1/database?charset=utf8mb4'
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    SQLALCHEMY_ENGINE_OPTIONS = {
        'pool_recycle': 120
    }
    ...

config.py

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

docker-compose.yml

2020-05-10 18:35:32 130 [Warning] Aborted connection 130 to db: 'flspection2' user: 'fl-server' host: '172.19.0.1' (Got an error reading communication packets)
2020-05-10 18:45:34 128 [Warning] Aborted connection 128 to db: 'flspection2' user: 'fl-server' host: '172.19.0.1' (Got timeout reading communication packets)

retraçage

Lost connection to MySQL server during query


14 commentaires

Voyez-vous quelque chose dans les journaux MariaDB? Pouvez-vous essayer d'augmenter le niveau de journalisation du serveur?


@MikkoOhtamaa - J'ai édité la question avec ce qui apparaît dans le journal MariaDB lorsque cela se produit. Je vais chercher à augmenter le niveau de journalisation. Je ne sais pas comment faire ça du haut de ma tête.


log_warnings=9 niveau de journalisation ( log_warnings=9 ). Mettra à jour lorsque je capture à nouveau l'erreur.


Aussi - ce n'était pas lié à MariaDB, mais c'est arrivé avec moi il y a longtemps. Le pilote PostgreSQL Python (écrit en C) plantait. Le passage à un pilote Pure Python a aidé. C'était difficile à diagnostiquer car il ne laissait aucune trace dans les journaux.


Changer le niveau de journalisation n'a pas donné beaucoup d'informations supplémentaires, mais j'ai vu qu'il y avait deux erreurs légèrement différentes en cours (éditées ci-dessus). 1. "Vous avez une erreur lors de la lecture des paquets de communication" et 2. "Vous avez dépassé le délai de lecture des paquets de communication"


@MikkoOhtamaa - Pymysql est le pilote python pur. Je teste actuellement mysqldb qui est la bibliothèque C. Peut-être qu'avec Maria DB, le problème sera résolu dans l'autre sens.


Selon la version, Flask-SQLalchemy a quelques autres paramètres par défaut qui pourraient affecter cela. Avez-vous essayé de définir SQLALCHEMY_POOL_SIZE, SQLALCHEMY_POOL_TIMEOUT et SQLALCHEMY_MAX_OVERFLOW?


Pourquoi utilisez-vous network_mode comme host et utilisez SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://username:password@db/database?charset=utf8m‌​b4'


@TarunLalwani - Je l'ai fait de cette façon parce qu'après avoir joué, c'est ce qui semblait fonctionner. Je déduis de votre question qu'il existe un meilleur moyen?


@MattL. - J'ai seulement essayé le SQLALCHEMY_POOL_TIMEOUT. Ce n'est pas une utilisation intensive et j'ai rencontré ce problème après des heures où je suis certain que personne d'autre ne l'utilise, donc je ne m'attends pas à ce que pool_size ou max_overflow entrent en jeu.


Bien reçu! Il vaut toujours la peine de vérifier.


@TarunLalwani - Je pense que j'ai initialement mal interprété votre commentaire. J'ai changé le network_mode à la valeur par défaut et mis à jour le SQLALCHEMY_DATABASE_URI comme suggéré. Je mettrai à jour quand j'aurai des informations de déménagement.


Le changement de network_mode n'a pas résolu le problème.


Cela semble être un problème de réseau de docker entre les conteneurs. Jetez un coup d'oeil s'il vous plait. success.docker.com/article/troubleshooting-container-network‌ ing


4 Réponses :


0
votes

Je ne sais pas quelles versions vous utilisez mais avez-vous essayé de définir 'SQLALCHEMY_POOL_SIZE et' SQLALCHEMY_POOL_RECYCLE '


0 commentaires

0
votes

J'ai été confronté à un problème similaire car j'ai une application django multithread. Lorsque le thread essaie d'accéder à la base de données, la connexion a été perdue. Il y a un bug django. https://code.djangoproject.com/ticket/21597

vous pouvez le résoudre avec cette solution de contournement.

from django.db import connection

def is_connection_usable():
    try:
        connection.connection.ping()
    except:
        return False
    else:
        return True

def do_work():
    while(True): # Endless loop that keeps the worker going (simplified)
        if not is_connection_usable():
            connection.close()
        try:
            do_a_bit_of_work()
        except:
            logger.exception("Something bad happened, trying again")

————————————————
版权声明:本文为CSDN博主「orangleliu」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lzz957748332/java/article/details/41480417

Vérifiez d'abord votre connexion à la base de données, si la connexion est perdue, elle fermera votre connexion, lorsque vous tenterez à nouveau de vous connecter, vous vous connecterez.

Bkz https://blog.csdn.net/lzz957748332/article/details/41480417


0 commentaires

0
votes

Essayez ce qui suit pour exclure que vous ayez des connexions obsolètes dans votre pool:

Depuis https://docs.sqlalchemy.org/en/13/core/ Covoiturage.html#pool-disconnects

L'approche ajoute un peu de surcharge au processus de vérification de la connexion, mais elle est par ailleurs l'approche la plus simple et la plus fiable pour éliminer complètement les erreurs de base de données dues à des connexions mises en pool obsolètes. L'application appelante n'a pas besoin de se préoccuper de l'organisation des opérations pour pouvoir récupérer des connexions périmées extraites du pool.

Le test pessimiste des connexions lors de la vérification est réalisable en utilisant l'argument Pool.pre_ping, disponible depuis create_engine () via l'argument create_engine.pool_pre_ping:

engine = create_engine("mysql+pymysql://user:pw@host/db", pool_pre_ping=True)


1 commentaires

Cela n'a pas résolu le problème pour moi



2
votes

J'ai résolu ce problème en migrant d'un conteneur mariadb vers mysql . Je ne sais toujours pas quelle est la cause profonde.


1 commentaires

Après avoir essayé toutes les solutions recommandées et perdu beaucoup d'argent à cause de ce bogue ridicule, je pense que c'est l'option la plus judicieuse.