Общие примечания
В последнее время я сталкивался с той же проблемой при выполнении нескольких условий:
- раствор должен быть поточно-безопасным
- несколько соединений с базой данных с одной и той же машины могут быть активны одновременно, уничтожить одно соединение / запрос
- содержит подключения ко многим различным базам данных - переносимый обработчик для каждого хоста БД
У нас было следующее расположение классов ( к сожалению, я не могу публиковать реальные источники ):
class AbstractModel: pass
class FirstDatabaseModel(AbstractModel): pass # Connection to one DB host
class SecondDatabaseModel(AbstractModel): pass # Connection to one DB host
И создал несколько потоков для каждой модели.
<Ч />
Решение Python 3.2
В нашем приложении одна модель = одна база данных . Поэтому я создал « сервисное соединение » для каждой модели (чтобы мы могли выполнить KILL
в параллельном соединении). Поэтому, если был создан один экземпляр FirstDatabaseModel
, было создано 2 подключения к базе данных; если было создано 5 экземпляров, использовалось только 6 соединений:
class AbstractModel:
_service_connection = None # Formal declaration
def __init__(self):
''' Somehow load config and create connection
'''
self.config = # ...
self.connection = MySQLFromConfig(self.config)
self._init_service_connection()
# Get connection ID (pseudocode)
self.connection_id = self.connection.FetchOneCol('SELECT CONNECTION_ID()')
def _init_service_connection(self):
''' Initialize one singleton connection for model
'''
cls = type(self)
if cls._service_connection is not None:
return
cls._service_connection = MySQLFromConfig(self.config)
Теперь нам нужен убийца:
def _kill_connection(self):
# Add your own mysql data escaping
sql = 'KILL CONNECTION {}'.format(self.connection_id)
# Do your own connection check and renewal
type(self)._service_connection.execute(sql)
Примечание: connection.execute
= создать курсор, выполнить, закрыть курсор.
И сделать защитную нить безопасной, используя threading.Lock
:
def _init_service_connection(self):
''' Initialize one singleton connection for model
'''
cls = type(self)
if cls._service_connection is not None:
return
cls._service_connection = MySQLFromConfig(self.config)
cls._service_connection_lock = threading.Lock()
def _kill_connection(self):
# Add your own mysql data escaping
sql = 'KILL CONNECTION {}'.format(self.connection_id)
cls = type(self)
# Do your own connection check and renewal
try:
cls._service_connection_lock.acquire()
cls._service_connection.execute(sql)
finally:
cls._service_connection_lock.release()
И, наконец, добавьте метод выполнения по времени, используя threading.Timer
:
def timed_query(self, sql, timeout=5):
kill_query_timer = threading.Timer(timeout, self._kill_connection)
kill_query_timer.start()
try:
self.connection.long_query()
finally:
kill_query_timer.cancel()