PyMySQL和eventlet實現數據庫連接池

eventlet的db_pool文檔地址:http://eventlet.net/doc/modules/db_pool.html

需要先安裝pymysql 0.9.3模塊和eventlet 0.25.1模塊。具體代碼如下:

import pymysql
from eventlet.db_pool import RawConnectionPool, ConnectionPool, DatabaseConnector

# 一種簡單的數據庫連接池
pool = RawConnectionPool(pymysql, min_size=0, max_size=4, max_idle=10, max_age=30, connect_timeout=5,
                         host='masaike.mysql.rds.aliyuncs.com',
                         port=3306,
                         user='mysqlsa',
                         password='123456',
                         db='call_center_robot')
is_dev = False
if is_dev:
    con_pool = ConnectionPool(pymysql,
                              host='masaike.mysql.rds.aliyuncs.com',
                              port=3306,
                              user='call_center_sql',
                              password='123456',
                              db='call_center_robot')
else:
    # 直接創建數據庫連接池
    con_pool = ConnectionPool(pymysql,
                              host='masaike.mysql.rds.aliyuncs.com',
                              port=3306,
                              user='mysqlsa',
                              password='123456',
                              db='call_center_robot')


def search_sql(total_sql):
    '''
    單庫查詢
    連接數據庫,執行sql查詢
    :param total_sql:
    '''
    # 從連接池拿連接對象
    with con_pool.item() as db:
        try:
            with db.cursor(pymysql.cursors.DictCursor) as cursor:
                # 使⽤execute⽅法執⾏SQL語句
                cursor.execute(total_sql)
                # 獲取數據
                data = cursor.fetchall()
                db.commit()
        except:
            data = ''
            # 把連接放回連接池
            con_pool.put(db)
    return data


# 多個主機的數據庫信息
db_info = {
    # 連接池名稱(原文檔這裏是數據庫host)
    'test': {
        'port': 3306,  # 端口
        'user': 'mysqlsa',  # 用戶名
        'host': 'masaike.test.mysql.rds.aliyuncs.com',  # 主機名
        'password': '123456',  # 密碼
        'db': 'call_center_robot'  # 數據庫名
    },
    'prod': {
        'port': 3306,
        'host': 'masaike.prod.mysql.rds.aliyuncs.com',
        'user': 'call_center_sql',
        'password': '123456',
        'db': 'call_center_robot'
    },
}
# 創建數據庫連接池池
db_pool = DatabaseConnector(pymysql, db_info)


def search_sql_pool(total_sql, pool_name='deault', db_name=''):
    '''
    多庫查詢
    :param total_sql: 被執行的sql
    :param pool_name: 連接池名稱
    :param db_name: 數據庫名
    :return:
    '''
    # 從數據庫連接池池中獲取連接池
    conn_pool = db_pool.get(pool_name, db_name)
    with conn_pool.item() as db:
        try:
            with db.cursor(pymysql.cursors.DictCursor) as cursor:
                # 使⽤execute⽅法執⾏SQL語句
                cursor.execute(total_sql)
                # 獲取數據
                data = cursor.fetchall()
            # 提交
            db.commit()
        except:
            data = ''
            # 把連接放回連接池
            conn_pool.put(db)
    return data


if __name__ == '__main__':
    data = search_sql('select now()')
    print(data)
    data = search_sql_pool('select now()', 'test', 'call_center_robot')
    print(data)

說明:對於上面代碼裏的多個主機數據庫信息字典有一點說明,在原文檔使用host作爲字典的key,看一下文檔。

但是一般數據庫主機的host都很長,作爲參數傳遞不方便。 查看了eventlet.db_pool的DatabaseConnector源碼,發現源碼有一處問題,也可能是作者故意設置的。

class DatabaseConnector(object):
    """
    This is an object which will maintain a collection of database
    connection pools on a per-host basis.
    """

    def __init__(self, module, credentials,
                 conn_pool=None, *args, **kwargs):
        """constructor
        *module*
            Database module to use.
        *credentials*
            Mapping of hostname to connect arguments (e.g. username and password)
        """
        assert(module)
        self._conn_pool_class = conn_pool
        if self._conn_pool_class is None:
            self._conn_pool_class = ConnectionPool
        self._module = module
        self._args = args
        self._kwargs = kwargs
        # this is a map of hostname to username/password
        self._credentials = credentials
        self._databases = {}

    def credentials_for(self, host):
        if host in self._credentials:
            return self._credentials[host]
        else:
            return self._credentials.get('default', None)

    def get(self, host, dbname):
        """Returns a ConnectionPool to the target host and schema.
        """
        key = (host, dbname)
        if key not in self._databases:
            new_kwargs = self._kwargs.copy()
            new_kwargs['db'] = dbname
            new_kwargs['host'] = host
            new_kwargs.update(self.credentials_for(host))
            dbpool = self._conn_pool_class(
                self._module, *self._args, **new_kwargs)
            self._databases[key] = dbpool

        return self._databases[key]

DatabaseConnector的get方法裏,如果key對應的連接池不存在,則會創建一個新的連接池,credentials_for方法會根據host返回數據庫信息,最後用credentials_for返回的數據庫信息更新當前的new_kwargs。所以如果把db_info字典的key改成連接池名稱,把host放到連接池名稱對應的字典中,真正的host會把當前new_kwargs裏的host(連接池名稱)更新了,如果這是這個模塊作者故意做的,不得不感嘆大佬的編程藝術~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章