2 votes

Service web Twisted - perte de connexion sql

Je travaille sur un service web avec Twisted qui est responsable de l'appel de plusieurs paquets que j'avais précédemment utilisés en ligne de commande. Les routines gérées par ces paquets étaient en cours de prototypage mais sont maintenant prêtes à être intégrées dans notre service web.

En bref, j'ai plusieurs modules différents qui créent tous une propriété de connexion mysql en interne dans leur forme originale de ligne de commande. Prenons l'exemple suivant :

class searcher:
  def __init__(self,lat,lon,radius):
    self.conn = getConnection()[1]
    self.con=self.conn.cursor();

    self.mgo = getConnection(True)

    self.lat = lat
    self.lon = lon
    self.radius = radius
    self.profsinrange()
    self.cache = memcache.Client(["173.220.194.84:11211"])

La fonction getConnection est juste une aide qui renvoie un curseur mongo ou mysql respectivement. Encore une fois, tout ceci est prototypique :)

Le problème que je rencontre est que lorsqu'il est implémenté en tant que serveur fonctionnant en permanence en utilisant la ressource WSGI de Twisted, la connexion sql créée lors de l'initialisation est interrompue, et les requêtes suivantes ne semblent pas la régénérer. Exemple de code pour une petite application serveur :

from twisted.web import server
from twisted.web.wsgi import WSGIResource
from twisted.python.threadpool import ThreadPool
from twisted.internet import reactor
from twisted.application import service, strports
import cgi

import gnengine
import nn

wsgiThreadPool = ThreadPool()
wsgiThreadPool.start()

# ensuring that it will be stopped when the reactor shuts down
reactor.addSystemEventTrigger('after', 'shutdown', wsgiThreadPool.stop)

def application(environ, start_response):
    start_response('200 OK', [('Content-type','text/plain')])
    params = cgi.parse_qs(environ['QUERY_STRING'])
    try:
      lat =  float(params['lat'][0])
      lon = float(params['lon'][0])
      radius = int(params['radius'][0])
      query_terms = params['query']
      s = gnengine.searcher(lat,lon,radius)
      query_terms = ' '.join( query_terms )
      json = s.query(query_terms)
      return [json]
    except Exception, e:
      return [str(e),str(params)]

    return ['error']

wsgiAppAsResource = WSGIResource(reactor, wsgiThreadPool, application)

# Hooks for twistd
application = service.Application('Twisted.web.wsgi Hello World Example')
server = strports.service('tcp:8080', server.Site(wsgiAppAsResource))
server.setServiceParent(application)

Les premières requêtes fonctionnent bien, mais après que mysqls wait_timeout expire, la redoutable erreur 2006 "Mysql a disparu" fait surface. J'avais cru comprendre que chaque requête à la ressource WSGI Twisted exécuterait la fonction d'application, régénérant ainsi l'objet de recherche et libérant à nouveau la connexion. Si ce n'est pas le cas, comment puis-je faire en sorte que les requêtes soient traitées comme telles ? Ce type de déploiement Twisted n'est-il pas transactionnel dans ce sens ? Merci de votre compréhension.

EDIT : A la demande, voici le prototype de la fonction d'aide qui établit la connexion :

def getConnection(mong = False):
    if mong == False:
    connection = mysql.connect(host = db_host,
                   user = db_user,
                   passwd = db_pass,
                   db = db,
                   cursorclass=mysql.cursors.DictCursor)
    cur = connection.cursor();
    return (cur,connection)
    else:
    return pymongo.Connection('173.220.194.84',27017).gonation_test

2voto

NetSkay Points 152

J'étais en train de développer un logiciel avec Twisted où je devais utiliser une connexion constante à une base de données MySQL. J'ai rencontré ce problème et en fouillant dans la documentation de Twisted et en posant quelques questions, je n'ai pas réussi à trouver une solution adéquate. Il y a un paramètre booléen que vous pouvez passer lorsque vous instanciez la classe adbapi.connectionPool ; cependant, cela n'a jamais semblé fonctionner et j'ai continué à obtenir l'erreur, quelle qu'elle soit. Je pense que le booléen reconnect représente la destruction de l'objet de connexion lorsque la déconnexion SQL se produit.

adbapi.ConnectionPool("MySQLdb", cp_reconnect=True, host="", user="", passwd="", db="")

Je n'ai pas encore testé cette méthode, mais j'afficherai les résultats lorsque je l'aurai fait ou si quelqu'un d'autre l'a fait, merci de nous en faire part.

Lorsque j'ai développé le script script j'utilisais twisted 8.2.0 (je n'ai pas touché à twisted depuis un moment) et à l'époque le framework n'avait pas de méthode explicite de maintien en vie, j'ai donc développé une extension ping/keepalive utilisant le paradigme événementiel sur lequel twisted s'appuie en conjonction avec la méthode ping() du module MySQLdb (voir le commentaire du code). Pendant que je tapais cette réponse, j'ai regardé la documentation actuelle de twisted et je n'ai toujours pas trouvé de méthode ou de paramètre explicite pour le keep-alive. Je pense que c'est parce que twisted lui-même n'a pas de bibliothèques/classes de connectivité de base de données. Il utilise les méthodes disponibles dans Python et fournit une couche indirecte d'interfaçage avec ces modules ; avec une certaine exposition pour les appels directs à la bibliothèque de base de données utilisée. Ceci est accompli en utilisant la méthode adbapi.runWithConnection.

voici le module que j'ai écrit sous twisted 8.2.0 et python 2.6 ; vous pouvez régler les intervalles entre les pings. ce que fait le script, c'est que toutes les 20 minutes, il envoie un ping à la base de données et s'il échoue, il tente de se reconnecter à elle toutes les 60 secondes. Je dois avertir que le script ne gère PAS les connexions soudaines/perdues ; ce que vous pouvez gérer avec addErrback chaque fois que vous exécutez une requête via twisted, du moins c'est ainsi que je l'ai fait. J'ai remarqué que lorsque la connexion à la base de données est interrompue, vous ne pouvez savoir si elle l'a été que lorsque vous exécutez une requête et que l'événement soulève un errback, et c'est à ce moment-là que vous le gérez. En gros, si je n'exécute pas de requête pendant 10 minutes et que ma base de données me déconnecte, mon application ne réagira pas en temps réel. L'application se rendra compte que la connexion a été interrompue lorsqu'elle exécutera la requête suivante ; la base de données pourrait donc nous avoir déconnectés 1 minute après la première requête, 5, 9, etc... ..... Je pense que cela revient en quelque sorte à l'idée originale que j'ai énoncée, twisted utilise les propres bibliothèques de Python ou des bibliothèques tierces pour la connectivité avec les bases de données et, pour cette raison, certaines choses sont gérées un peu différemment.

from twisted.enterprise import adbapi
from twisted.internet import reactor, defer, task

class sqlClass:
        def __init__(self, db_pointer):
                self.dbpool=db_pointer
                self.dbping = task.LoopingCall(self.dbping)
                self.dbping.start(1200) #20 minutes = 1200 seconds; i found out that if MySQL socket is idled for 20 minutes or longer, MySQL itself disconnects the session for security reasons; i do believe you can change that in the configuration of the database server itself but it may not be recommended.
                self.reconnect=False
                print "database ping initiated"

        def dbping(self):
                def ping(conn):
                        conn.ping() #what happens here is that twisted allows us to access methods from the MySQLdb module that python posesses; i chose to use the native command instead of sending null commands to the database.
                pingdb=self.dbpool.runWithConnection(ping)
                pingdb.addCallback(self.dbactive)
                pingdb.addErrback(self.dbout)
                print "pinging database"

        def dbactive(self, data):
                if data==None and self.reconnect==True:
                        self.dbping.stop()
                        self.reconnect=False
                        self.dbping.start(1200) #20 minutes = 1200 seconds
                        print "Reconnected to database!"
                elif data==None:
                        print "database is active"

        def dbout(self, deferr):
                #print deferr
                if self.reconnect==False:
                        self.dbreconnect()
                elif self.reconnect==True:
                        print "Unable to reconnect to database"
                print "unable to ping MySQL database!"

        def dbreconnect(self, *data):
                self.dbping.stop()
                self.reconnect=True
                #self.dbping = task.LoopingCall(self.dbping)
                self.dbping.start(60) #60
if __name__ == "__main__":
        db = sqlClass(adbapi.ConnectionPool("MySQLdb", cp_reconnect=True, host="", user="", passwd="", db=""))
        reactor.callLater(2, db.dbping)
        reactor.run()

Faites-moi savoir comment cela fonctionne pour vous :)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X