簡體   English   中英

Python mysql(使用pymysql)自動重新連接

[英]Python mysql (using pymysql) auto reconnect

我不確定這是否可能,但我正在尋找一種在連接丟失時重新連接到 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

好吧,我在我的應用程序中遇到了同樣的問題,我在PyMySQL 文檔上找到了一個方法,它可以 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)

我希望它有所幫助。

終於找到了一個可行的解決方案,可能會對某人有所幫助。

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

最簡單的方法是在發送查詢之前檢查連接。

您可以通過創建一個包含兩個方法的小類來做到這一點: connectquery

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")發送查詢時,請求都會自動准備好遇到連接錯誤,並在需要時使用self.connect()重新連接。

請記住:這是一個簡化的示例。 通常,您希望讓 PyMySQL 幫助您轉義查詢中的特殊字符。 為此,您必須在query方法中添加第二個參數並從那里開始。

邏輯很簡單,如果連接關閉然后嘗試重新連接幾次,在這種情況下我使用最大嘗試 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

舊的,但我在訪問程序中的托管數據庫時遇到了類似的問題。 我最終使用的解決方案是創建一個裝飾器,以便在進行查詢時自動重新連接。

給定一個連接函數:

def connect(self):
    self.conn = mysql.connector.connect(host=self.host, user=self.user, 
    database=self.database, password=self.password)
    self.cursor = self.conn.cursor()
    print("Established connectionn...")

我建立

def _reconnect(func):
    @wraps(func)
    def rec(self,*args,**kwargs):
        try:
            result = func(self,*args,**kwargs)
            return result
        except (mysql.connector.Error, mysql.connector.Warning) as e:
            self.connect()
            result = func(self,*args,**kwargs)
            return result
    return rec 

這樣任何使用連接的函數現在都可以這樣裝飾

@_reconnect
def check_user_exists(self,user_id):
    self.cursor.execute("SELECT COUNT(*) FROM _ where user_id={};".format(user_id))
    if self.cursor.fetchall()[0][0]==0:
        return False 
    else:
        return True

此裝飾器將重新建立連接並重新運行任何涉及到數據庫查詢的函數。

每次進行查詢時,您都可以使用屬性來保持連接處於活動狀態:

import pymysql
import pymysql.cursors
import pandas as pd 

class DB:
    def __init__(self, hostname='1.1.1.1', username='root', password='password',
                 database=None, port=3306, charset="utf8mb4"):
        self.hostname = hostname
        self.database = database
        self.username = username 
        self.password = password
        self.port = port
        self.charset = charset
        self.connect()
    
    @property
    def conn(self): 
        if not self.connection.open:
            print('Going to reconnect')
        self.connection.ping(reconnect=True)
        return self.connection

    def connect(self):
        self.connection = pymysql.connect(
                             host=self.hostname,
                             user=self.username,
                             password=self.password,
                             db=self.database,
                             charset=self.charset,
                             cursorclass=pymysql.cursors.DictCursor,
                             port=self.port)

    def query(self, sql):
        return pd.read_sql_query(sql, con=self.conn)

db = DB(hostname='1.1.1.1', username='root', password='password', database=None, port=3306, charset="utf8mb4")

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM