Python mysql(使用pymysql)自动重新连接

时间:2014-03-27 21:40:59

标签: python mysql database-connection connection-pooling pymysql

我不确定这是否可行,但我正在寻找一种在连接丢失时重新连接到mysql数据库的方法。所有连接都保存在gevent队列中,但我认为这无关紧要。我敢肯定,如果我花一些时间,我可以想出一种方法来重新连接到数据库。但是我看了一下pymysql代码,我看到Connection类中有一个'ping'方法,我不确定如何使用它。

该方法看起来会在第一次重新连接,但之后又将重新连接标志再次切换为False?我可以使用这种方法,还是有不同的方法来建立连接丢失?即使不是pymysql,人们如何处理,数据库服务器崩溃并且不得不重新建立与mysql服务器的连接?

def ping(self, reconnect=True):
    ''' Check if the server is alive '''
    if self.socket is None:
        if reconnect:
            self._connect()
            reconnect = False
        else:
            raise Error("Already closed")
    try:
        self._execute_command(COM_PING, "")
        return self._read_ok_packet()
    except Exception:
        if reconnect:
            self._connect()
            return self.ping(False)
        else:
            raise

4 个答案:

答案 0 :(得分:11)

终于得到了一个有效的解决方案,可能会帮助别人。

from gevent import monkey
monkey.patch_socket()
import logging

import gevent
from gevent.queue import Queue
import pymysql as db

logging.basicConfig(level=logging.DEBUG)
LOGGER = logging.getLogger("connection_pool")


class ConnectionPool:
    def __init__(self, db_config, time_to_sleep=30, test_run=False):
        self.username = db_config.get('user')
        self.password = db_config.get('password')
        self.host = db_config.get('host')
        self.port = int(db_config.get('port'))
        self.max_pool_size = 20
        self.test_run = test_run
        self.pool = None
        self.time_to_sleep = time_to_sleep
        self._initialize_pool()

    def get_initialized_connection_pool(self):
        return self.pool

    def _initialize_pool(self):
        self.pool = Queue(maxsize=self.max_pool_size)
        current_pool_size = self.pool.qsize()
        if current_pool_size < self.max_pool_size:  # this is a redundant check, can be removed
            for _ in xrange(0, self.max_pool_size - current_pool_size):
                try:
                    conn = db.connect(host=self.host,
                                      user=self.username,
                                      passwd=self.password,
                                      port=self.port)
                    self.pool.put_nowait(conn)

                except db.OperationalError, e:
                    LOGGER.error("Cannot initialize connection pool - retrying in {} seconds".format(self.time_to_sleep))
                    LOGGER.exception(e)
                    break
        self._check_for_connection_loss()

    def _re_initialize_pool(self):
        gevent.sleep(self.time_to_sleep)
        self._initialize_pool()

    def _check_for_connection_loss(self):
        while True:
            conn = None
            if self.pool.qsize() > 0:
                conn = self.pool.get()

            if not self._ping(conn):
                if self.test_run:
                    self.port = 3306

                self._re_initialize_pool()

            else:
                self.pool.put_nowait(conn)

            if self.test_run:
                break
            gevent.sleep(self.time_to_sleep)

    def _ping(self, conn):
        try:
            if conn is None:
                conn = db.connect(host=self.host,
                                  user=self.username,
                                  passwd=self.password,
                                  port=self.port)
            cursor = conn.cursor()
            cursor.execute('select 1;')
            LOGGER.debug(cursor.fetchall())
            return True

        except db.OperationalError, e:
            LOGGER.warn('Cannot connect to mysql - retrying in {} seconds'.format(self.time_to_sleep))
            LOGGER.exception(e)
            return False

# test (pytest compatible) -------------------------------------------------------------------------------------------
import logging

from src.py.ConnectionPool import ConnectionPool

logging.basicConfig(level=logging.DEBUG)
LOGGER = logging.getLogger("test_connection_pool")


def test_get_initialized_connection_pool():
    config = {
        'user': 'root',
        'password': '',
        'host': '127.0.0.1',
        'port': 3305
    }
    conn_pool = ConnectionPool(config, time_to_sleep=5, test_run=True)
    pool = conn_pool.get_initialized_connection_pool()
    # when in test run the port will be switched back to 3306
    # so the queue size should be 20 - will be nice to work 
    # around this rather than test_run hack
    assert pool.qsize() == 20

答案 1 :(得分:2)

好吧,我的应用程序中遇到了同样的问题,我在PyMySQL documentation上找到了一种方法,可以ping通服务器,并验证连接是否已关闭,如果连接已关闭,则它再次重新连接

from pymysql import connect
from pymysql.cursors import DictCursor

# create the connection
connection = connect(host='host', port='port', user='user', 
                     password='password', db='db', 
                     cursorclass=DictCursor)

# get the cursor
cursor = connection.cursor()

# if the connection was lost, then it reconnects
connection.ping(reconnect=True)      

# execute the query
cursor.execute(query)

希望对您有帮助。

答案 2 :(得分:1)

最简单的方法是在发送查询之前检查连接。

您可以通过创建一个包含两个方法的小类来做到这一点:var clients = (from c in db.ClientsTbls select new SelectListItem { Text = m.FirstName + " " + m.LastName, Value = m.ID.ToString() }); ViewBag.ClientsList = new SelectList(clients,"Value", "Text"); connect

query

现在,每当您使用import pymysql import pymysql.cursors class DB: def connect(self): self.conn = pymysql.connect( host=hostname, user=username, password=password, db=dbname, charset='utf8mb4', cursorclass=pymysql.cursors.DictCursor, port=3306) def query(self, sql): try: cursor = self.conn.cursor() cursor.execute(sql) except pymysql.OperationalError: self.connect() cursor = self.conn.cursor() cursor.execute(sql) return cursor db = DB() 发送查询时,请求都会自动准备遇到连接错误,并在需要时使用db.query("example SQL")重新连接。

记住:这是一个简化的示例。通常,您希望让PyMySQL帮助您转义查询中的特殊字符。为此,您必须在self.connect()方法中添加第二个参数,然后从那里开始。

答案 3 :(得分:0)

逻辑很简单,如果连接关闭,然后尝试重新连接几次,在这种情况下,我使用max尝试15次来重新连接或ping。

import pymysql, pymysql.cursors
conn = pymysql.connect(
                         host=hostname,
                         user=username,
                         password=password,
                         db=dbname,
                         charset='utf8mb4',
                         cursorclass=pymysql.cursors.DictCursor,
                         )
cursor = conn.cursor()
# you can do transactions to database and when you need conn later, just make sure the server is still connected
if conn.open is False:
   max_try = 15
   try = 0
   while conn.open is False:
       if try < max_try:
           conn.ping() # autoreconnect is true by default
       try +=1

# check the conn again to make sure it connected
if conn.open:
    # statements when conn is successfully reconnect to the server
else:
    # it must be something wrong : server, network etc