Twisted веб-сервис - SQL-соединение падает - PullRequest
2 голосов
/ 17 ноября 2011

Я работаю над веб-сервисом с Twisted, который отвечает за вызов нескольких пакетов, которые я ранее использовал в командной строке. Подпрограммы, которые обрабатывают эти пакеты, сами создавались по прототипам, но теперь готовы для интеграции в наш веб-сервис.

Короче говоря, у меня есть несколько различных модулей, которые создают внутреннее свойство соединения mysql в своих исходных формах командной строки. Возьмем для примера:

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"])

Функция getConnection - это просто помощник, который возвращает курсор mongo или mysql соответственно. Опять же, все это прототип:)

Проблема, с которой я сталкиваюсь, заключается в том, что при реализации в качестве постоянно работающего сервера с использованием WSGI-ресурса Twisted соединение sql, созданное во время инициализации, и последующие запросы, похоже, не восстанавливают его. Пример кода для небольшого серверного приложения:

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)

Первые несколько запросов работают нормально, но после истечения срока действия mysqls wait_timeout появляется ошибка dread 2006 «Mysql ушла». Насколько я понимаю, каждый запрос к ресурсу WSGI Twisted будет запускать функцию приложения, тем самым регенерируя объект искателя и повторно арендуя соединение. Если это не так, как я могу обработать запросы как таковые? Разве этот вид развертывания Twisted не является транзакционным в этом смысле? Спасибо!

РЕДАКТИРОВАТЬ : Для каждого запроса вот вспомогательная функция-прототип, вызывающая соединение:

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

1 Ответ

2 голосов
/ 17 ноября 2011

Я занимался разработкой программного обеспечения с витой, где мне приходилось использовать постоянное соединение с базой данных MySQL. я столкнулся с этой проблемой, много копался в искаженной документации и отправил несколько вопросов, но не смог найти правильного решения. Есть логический параметр, который вы можете передать, когда создаете экземпляр класса adbapi.connectionPool; однако, это никогда не казалось работающим, и я продолжал получать ошибку независимо от. Тем не менее, я предполагаю, что логическое значение пересоединения представляет собой уничтожение объекта соединения, когда происходит отключение SQL.

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

Я не проверял это, но я опубликую некоторые результаты, когда я делаю или, если кто-то еще, пожалуйста, поделитесь.

Когда я разрабатывал сценарий, я использовал Twisted 8.2.0 (я не касался Twisted некоторое время), и тогда у фреймворка не было такого явного метода keep alive, поэтому я разработал расширение ping / keepalive, использующее управляемую событиями парадигму. витая сборка в сочетании с прямым методом модуля pSQL () модуля MySQLdb (см. комментарий к коду). Как я печатал этот ответ; Тем не менее, я просмотрел текущую искаженную документацию, но все еще не смог найти явный метод или параметр keep-alive. Я предполагаю, что у Twisted нет библиотек / классов подключения к базе данных. Он использует методы, доступные для python, и обеспечивает косвенный уровень взаимодействия с этими модулями; с некоторой выдержкой для прямых обращений к используемой библиотеке базы данных. Это достигается с помощью метода adbapi.runWithConnection.

вот модуль, который я написал под twisted 8.2.0 и python 2.6; Вы можете установить интервалы между пингами. Сценарий выполняет следующие действия: каждые 20 минут он проверяет связь с базой данных, а в случае сбоя он пытается подключиться к нему каждые 60 секунд. Я должен предупредить, что скрипт НЕ обрабатывает внезапное / потерянное соединение; что вы можете обрабатывать через addErrback всякий раз, когда вы запускаете запрос через витую, по крайней мере, вот как я это сделал. Я заметил, что всякий раз, когда соединение с базой данных прерывается, вы можете узнать, есть ли оно, когда вы выполняете запрос, и событие вызывает ошибку, а затем в этот момент вы имеете дело с этим. По сути, если я не выполняю запрос в течение 10 минут, и моя база данных отключает меня, мое приложение не будет отвечать в режиме реального времени. приложение поймет, что соединение было разорвано, когда запустит следующий запрос; таким образом, база данных могла отключить нас через 1 минуту после первого запроса, 5, 9 и т. д. Я предполагаю, что этот вид восходит к первоначальной идее, которую я изложил, Twisted использует собственные библиотеки Python или сторонние библиотеки для подключения к базе данных, и поэтому некоторые вещи обрабатываются немного по-другому.



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()

дайте мне знать, как это работает для вас:)

...