Python实现与MySQL长连接的客户端
下面的代码是使用Python建立的和MySQL长连接的简单客户端示例。
当和MySQL的连接断开后,会自动进行重连(被动式的重连,即只有调用增self.execute()、删self.execute()、改self.execute()、查self.query()方法出现异常的时候,才会触发重连)。可以修改“self.__check_exception_type()”方法,在该方法中完善对应的异常信息,来完善代码。
import datetime
import logging
import time
import traceback
import pymysql
import threading LOG_FORMAT = "%(asctime)s - %(levelname)s [%(filename)s-%(funcName)s] Line: %(lineno)s] - %(message)s"
logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT)
_logger = logging.getLogger() __all__ = ["MySQLClient"] class MySQLClient:
"""
self.__database_status: 用来判断当前的数据库连接是否正常。True代表数据库状态正常
self.__has_reconnect_thread: 用来判断是否已经开启了重连数据库的后台线程。True代表已经有启动线程在进行数据库的重连了 每次对数据库执行增、删、改、查等操作的时候,都要先判断下 self.__database_status 的值。
1、当值为True的时候,代表数据库连接正常,正常执行相关的增、删、改、查等操作
2、当值为False的时候,代表数据库已经是连接异常状态,那么就再检查下 self.__has_reconnect_thread 的值:
2.1、当 self.__has_reconnect_thread 的值是False的时候,说明还没有启动后台重连数据库的线程,那么会启动一个线程
进行数据库重连。后台线程重连成功后,会把 self.__database_status 的值设置成True,
再把 self.__has_reconnect_thread 变量设置为False,并退出该重连数据库的线程。
2.2、当 self.__has_reconnect_thread 的值是True的时候,说明已经有一个后台线程进行数据库的重连,就不再启动新的
重连数据库的线程了。
3、在执行增、删、改、查等操作的时候,如果碰到了数据库连接异常,那么就会把 self.__database_status 的值设置成False
"""
def __init__(self, host, username, password, database="", table_name="", port=3306, logger=None):
"""
:param host:
:param username:
:param password:
:param port:
:param logger: 使用自定义的logger
"""
self.__host = host
self.__username = username
self.__password = password
self.__port = port
self.__database = database
self.__table_name = table_name
self.__logger = logger if logger else _logger self.__database_status = False # 数据库连接状态标识位
self.__has_reconnect_thread = False # 是否已经有重连数据库的后台线程标识位 self.__connection = None # 对数据库的连接
self.__cursor = None # 所有的增、删、改、查都是使用游标即该变量进行的操作 # 实例化对象的时候就进行和数据库的连接,连接成功则可以直接调用对象的query()或者insert()等方法,连接失败则抛出异常
self.connect_to_db() # 开启定时检查数据库状态的线程
self.__thread_check_db_status() def __get_database_status(self):
return self.__database_status def __set_database_status(self, status: bool):
self.__database_status = status def __get_has_reconnect_thread(self):
return self.__has_reconnect_thread def __set_has_reconnect_thread(self, status: bool):
self.__has_reconnect_thread = status def __thread_reconnect_to_db(self, timespan=5):
"""
如果类变量 __has_reconnect_thread 是False,则启动一个线程进行数据库的重连,并且设置 __has_reconnect_thread 变量
为True 如果类变量 __has_reconnect_thread 是True,说明已经有对数据库进行重连的线程了,此时 __has_reconnect_thread 值为True 在对数据库重连的后台线程中,如果重连成功,则设置类变量 __database_status 为True并且退出该线程,同时设置
__has_reconnect_thread 为False。 :param timespan: 两次重连的时间间隔。
:return:
"""
def __thread_connect_to_database():
self.__set_has_reconnect_thread(True) # 设置全局变量,表示已经有线程进行重连数据库了。
while True: # 在while循环中一直进行重连,直到重新连上数据库
if not self.__connect_to_db_once():
self.__logger.error("Re-connect to database failed !!! Will retry after [%s] seconds" % timespan)
time.sleep(timespan)
else:
self.__set_database_status(True) # 设置数据库连接状态为True
self.__set_has_reconnect_thread(False) # 重连线程要退出了,所以设置该状态为False
self.__logger.info("Re-connect to database successfully @_@")
break # 退出循环,即退出重连线程 if not self.__get_has_reconnect_thread():
self.__set_has_reconnect_thread(True)
t = threading.Thread(target=__thread_connect_to_database, name="Thread-reconnect-to-db")
t.start()
# else:
# print("Already has thread connect to database") def __thread_check_db_status(self):
"""
以后台线程的形式,检查数据库连接状态
:return:
"""
def __inner_thread_check_db_status():
while True:
try:
if not self.__get_database_status():
self.__thread_reconnect_to_db()
except Exception:
self.__logger.error(str(traceback.format_exc()))
finally:
time.sleep(1)
t = threading.Thread(target=__inner_thread_check_db_status)
t.setDaemon(True)
t.start() def connect_to_db(self, timeout=60):
"""
调用该方法,进行数据库的连接,如果超过设置的时间还没有连接的话,则抛出异常
:param timeout:
:return: 该类对象(self)或者抛出异常
"""
# 如果数据库已经处于连接状态,则直接返回。因此该方法可以被重复调用执行。
if self.__get_database_status():
return self # 此时还没有连接到数据库,就需要进行对数据库的连接。
start_timestamp = time.time()
while True:
try:
if self.__connect_to_db_once(log=True):
self.__set_database_status(True)
break
else:
end_timestamp = time.time()
if (end_timestamp - start_timestamp) < timeout:
self.__logger.error("Connect to database failed, will re-connect after 1 seconds ...")
self.__set_database_status(False)
time.sleep(1)
else:
break
except Exception:
self.__logger.error("Connect to database exception !!! " + traceback.format_exc())
break # 连接数据库成功,则返回该类对象
if self.__get_database_status():
self.__logger.info("Connect to database successfully @_@")
return self
else: # 连接数据库失败,则返回None
message = "After [%s] seconds, still can not connect to database !!!" % timeout
self.__logger.error(message)
raise Exception(message) def __check_exception_type(self, e, trac):
"""
根据不同的异常,进行分别处理。比如有的是SQL异常,有的是数据库连接异常。
如果是数据库连接异常等,需要设置数据库连接状态,以便进行重连。
:param e: 简短的异常信息
:param trac: traceback.format_exc() 捕获到的详细信息
:return:
""" # 1、SQL语法错误的异常,不属于数据库连接异常,不需要重连
if "pymysql.err.ProgrammingError" in str(trac):
# pymysql.err.ProgrammingError: (1064, "You have an error in your SQL syntax;
# check the manual that corresponds to your MySQL server version for the right syntax to use near 'order by domain' at line 1")
self.__logger.error("SQL syntax exception !!! " + str(e))
raise Exception(e) from None # 2、SQL语法错误的异常,不属于数据库连接异常,不需要重连
if "pymysql.err.InternalError" in str(trac):
# pymysql.err.InternalError: (1054, "Unknown column 'names' in 'field list'")
self.__logger.error("SQL syntax exception !!! " + str(e))
raise Exception(e) from None # 3、连接不上数据库服务器,属于数据库连接异常,需要重连
if "pymysql.err.OperationalError" in str(trac) and "Can't connect to MySQL server on" in str(trac):
# pymysql.err.OperationalError: (2003, "Can't connect to MySQL server on '10.88.65.5' (timed out)")
self.__set_database_status(False) # 设置数据库连接状态为异常状态
self.__logger.error("Connect to MySQL server exception !!! " + str(e))
raise Exception(e) from None # 4、连接断开,属于数据库连接异常,需要重连
if "pymysql.err.OperationalError" in str(trac) and "Lost connection to MySQL server" in str(trac):
# pymysql.err.OperationalError: (2013, 'Lost connection to MySQL server during query ([WinError 10060]
# 由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败。)')
self.__set_database_status(False) # 设置数据库连接状态为异常状态
self.__logger.error("Lost connection to MySQL server exception !!! " + str(e))
raise Exception(e) from None # 5、连接持续中断,属于数据库连接异常,需要重连
if "pymysql.err.InterfaceError" in str(trac):
# pymysql.err.InterfaceError: (0, '')
self.__set_database_status(False) # 设置数据库连接状态为异常状态
self.__logger.error("Disconnect to MySQL server exception !!! " + str(e))
raise Exception(e) from None # 6、未知即未被统计到的异常,待完善,统一归入这里,需要重连
else:
self.__set_database_status(False) # 设置数据库连接状态为异常状态
self.__logger.error("Unknown exception !!! " + str(trac))
raise Exception(e) from None def __connect_to_db_once(self, log=False) -> bool:
"""
进行一次数据库的连接。
连接成功,则把获取到的连接对象设置到变量 self.__db_client 中,并返回True。同时设置全局变量 self.__connection 和 self.__cursor
连接失败,则返回False
:return:
"""
try:
connection = pymysql.connect(host=self.__host,
port=self.__port,
user=self.__username,
password=self.__password,
database=self.__database,
)
self.__connection = connection
self.__cursor = connection.cursor()
return True
except Exception as e:
try:
# 如果在单次连接的时候,出现异常并经过 self.__check_exception_type()方法处理后,该方法仍然抛出异常,那么说明
# 该次连接失败,在这个单次连接的方法中,捕获下即可
self.__check_exception_type(e, traceback.format_exc())
except Exception:
return False def query(self, sql: str) -> list:
"""
查询部分 :return: 返回列表或者抛出异常(异常是因为客户提供的SQL格式错误)
"""
ret_list = [] # 数据库连接状态异常,则调用 self.__thread_reconnect_to_db() 方法进行重连,在该方法中进行实际判断,是否启动重连的线程。
if not self.__get_database_status():
self.__thread_reconnect_to_db()
return ret_list
try:
self.__cursor.execute(query=sql)
ret_list = list(self.__cursor.fetchall())
return ret_list
except Exception as e:
self.__check_exception_type(e, traceback.format_exc()) def execute(self, sql: str, auto_commit=True) -> bool:
"""
增、删、改部分
:return: 返回布尔值True|False或者抛出异常
"""
ret = False # 数据库连接状态异常,则调用 self.__thread_reconnect_to_db() 方法进行重连,在该方法中进行实际判断,是否启动重连的线程。
if not self.__get_database_status():
self.__thread_reconnect_to_db()
return ret try:
self.__cursor.execute(sql)
if auto_commit:
self.__connection.commit()
return True
except Exception as e:
self.__check_exception_type(e, traceback.format_exc()) def manual_commit(self):
# 数据库连接状态异常,则调用 self.__thread_reconnect_to_db() 方法进行重连,在该方法中进行实际判断,是否启动重连的线程。
if not self.__get_database_status():
self.__thread_reconnect_to_db()
return False
try:
self.__connection.commit()
return True
except Exception as e:
self.__check_exception_type(e, traceback.format_exc()) def test(self):
"""
测试代码部分
:return:
"""
while True:
try:
sql = "SELECT domain, account FROM {tablename} limit 10".format(tablename=self.__table_name)
# sql = "SELECT COUNT(*) FROM {tablename}".format(tablename=self.__table_name)
sql = "update {tablename} set username='JCL10231024' where id='007e7571-c74a-47c1-99d5-a3f868bd7dd7'".format(tablename=self.__table_name)
print(sql)
ret_list = self.execute(sql, auto_commit=False)
self.manual_commit()
print("ret_list: ", ret_list)
except Exception:
pass
finally:
print("Sleep 5 seconds ...")
time.sleep(5) if __name__ == '__main__': obj = MySQLClient(host="localhost",
port=3306,
username="username",
password="password",
database="database",
table_name="table_name"
)
obj.test()
Python实现与MySQL长连接的客户端的更多相关文章
- mysql长连接和短连接的问题 转
什么是长连接? 其实长连接是相对于通常的短连接而说的,也就是长时间保持客户端与服务端的连接状态. 通常的短连接操作步骤是: 连接->数据传输->关闭连接: 而长连接通常就是: 连接-> ...
- mysql长连接和短连接的问题
什么是长连接? 其实长连接是相对于通常的短连接而说的,也就是长时间保持客户端与服务端的连接状态. 通常的短连接操作步骤是: 连接->数据传输->关闭连接: 而长连接通常就是: 连接-> ...
- mysql 长连接断开问题
从MySQL 5.0.3开始,默认情况下禁止再连接,这是5.0.13中的新选项,提供了一种以显式方式设置再连接行为的方法. mysql应用程序建立的长连接,大约过8小时会断开[没测过,网上都是这么说的 ...
- mysql长连接与短连接
什么是长连接? 其实长连接是相对于通常的短连接而说的,也就是长时间保持客户端与服务端的连接状态. 通常的短连接操作步骤是: 连接->数据传输->关闭连接: 而长连接通常就是: 连接-> ...
- mysql长连接
长连接是干嘛的: 它是做连接复用的: 在openresty中的lua-resty-mysql 里 connect方法去连接mysql时会去ngx_lua cosocket连接池中寻找是否有可用连接 ...
- php关于mysql长连接问题
1.当 函数 mysql_connect 的前三个参数(server username password)相同,并且第四个参数(new_link)不传递时候,重复调用 mysql_connect 是会 ...
- Spring Boot+Socket实现与html页面的长连接,客户端给服务器端发消息,服务器给客户端轮询发送消息,附案例源码
功能介绍 客户端给所有在线用户发送消息 客户端给指定在线用户发送消息 服务器给客户端发送消息(轮询方式) 项目搭建 项目结构图 pom.xml <?xml version="1.0&q ...
- PHP和mysql的长连接
关于 PHP MySQL 长连接.连接池的一些探索 PHP连接MySQL的方式,用的多的是mysql扩展.mysqli扩展.pdo_mysql扩展,是官方提供的.php的运行机制是页面执行完会释放所有 ...
- Ajax长连接和SignalR(刷新客户端数据)的区别
ajax实现长连接 <%@ page language="java" import="java.util.*" pageEncoding=" ...
- PHP-数据库长连接mysql_pconnect的细节
PHP的MySQL持久化连接,美好的目标,却拥有糟糕的口碑,往往令人敬而远之.这到底是为啥么.近距离观察后发现,这家伙也不容易啊,要看Apache的脸色,还得听MySQL指挥. 对于作为Apache模 ...
随机推荐
- 4. SpringMVC获取请求参数
1. 通过 ServletAPI 获取 将 HttpServletRequest 作为控制器方法的形参 , 此时 HttpServletRequest 类型的参数表示封装了当前请求的请求报文的对象 ...
- 图书商城项目练习①管理后台Vue2/ElementUI
本系列文章是为学习Vue的项目练习笔记,尽量详细记录一下一个完整项目的开发过程.面向初学者,本人也是初学者,搬砖技术还不成熟.项目在技术上前端为主,包含一些后端代码,从基础的数据库(Sqlite).到 ...
- Github入门教程(新版)
GitHub 的介绍与使用 GitHub 注册一个账号 直接在首页注册即可啦 要注意的是 第一项 username 别人是可见的 后面修改也会比较麻烦,所以起个好名字很重要 个人主页介绍 刚注册好的页 ...
- 跨越HTTP无状态边界:Cookie与Session在Django中的实战应用
本文深入探索了Django中的Cookie和Session,解析了如何应对HTTP协议的无状态性问题,说明其基础概念,分析工作原理,并讨论何时应选择使用Cookie或Session.文章进阶部分,提出 ...
- CentOS 7相关操作
防火墙操作 开启防火墙 sudo systemctl start firewalld.service 查看防火墙状态 sudo systemctl status firewalld.service 关 ...
- Hexo博客使用valine评论系统无效果及终极解决方案
注意事项 有一些博主valine评论系统无效果,有一些原因: 1.很大程度是因为next的版本升级导致某些参数设置不同 2.valine评论是基于LeanCloud,还有一个文章阅读次数功能也是用Le ...
- Struts2 小知识点
配置struts.xml文件,选择开发模式 在实际应用开发或者是产品部署的时候,对应着两种模式: 开发模式(devMode):此时 DevMode=ture: 产品模式(proMode):此时 Dev ...
- 深度学习(五)——DatadLoader的使用
一.DataLoader简介 官网地址: torch.utils.data - PyTorch 2.0 documentation 1. DataLoder类 class torch.utils.da ...
- 因为一条DDL,差点搞挂整个系统,这次真的长了教训
有一次在线上提了一个sql变更,就是下面这条, -- 修改字段的数据类型由varchar(500)变更为text ALTER TABLE t MODIFY COLUMN name text; 提完之后 ...
- 没有显示器可用的电脑找IP
一台在手边没有显示器可用的电脑找IP记录 问题 老大给我一台服务器(在我前面的工位)让我自己玩,但是不知道IP地址,我本来想用自己的显示器连上,结果两个DHMI口试过都没反应,不知道ip地址就没法连上 ...