db.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. import os
  2. import pymysql
  3. import threading
  4. import time
  5. import atexit
  6. from collections import deque
  7. # simple direct connector (used by some scripts)
  8. from pymysql.cursors import DictCursor
  9. def get_connection():
  10. return pymysql.connect(
  11. host="120.24.26.108",
  12. port=3307,
  13. user="root",
  14. password="zhijiayun123456",
  15. database="drug_retrieve",
  16. charset="utf8mb4",
  17. autocommit=False,
  18. cursorclass=DictCursor,
  19. )
  20. # connection pool used by a_mt.py
  21. MYSQL_POOL = None
  22. class PooledConnection:
  23. def __init__(self, conn, pool):
  24. self._conn = conn
  25. self._pool = pool
  26. self._closed = False
  27. def cursor(self, *args, **kwargs):
  28. return self._conn.cursor(*args, **kwargs)
  29. def commit(self):
  30. return self._conn.commit()
  31. def rollback(self):
  32. return self._conn.rollback()
  33. def ping(self, *args, **kwargs):
  34. return self._conn.ping(*args, **kwargs)
  35. def close(self):
  36. if self._closed:
  37. return
  38. self._closed = True
  39. try:
  40. self._pool.putconn(self._conn)
  41. except Exception:
  42. try:
  43. self._conn.close()
  44. except Exception:
  45. pass
  46. def __getattr__(self, item):
  47. return getattr(self._conn, item)
  48. class MySQLConnectionPool:
  49. def __init__(self, minconn=1, maxconn=10, **connect_kwargs):
  50. self.minconn = int(minconn)
  51. self.maxconn = int(maxconn)
  52. self.connect_kwargs = dict(connect_kwargs)
  53. self._lock = threading.Lock()
  54. self._pool = deque()
  55. self._total = 0
  56. for _ in range(self.minconn):
  57. conn = pymysql.connect(**self.connect_kwargs)
  58. self._pool.append(conn)
  59. self._total += 1
  60. def getconn(self, timeout=None):
  61. start = time.time()
  62. while True:
  63. conn = None
  64. with self._lock:
  65. if self._pool:
  66. conn = self._pool.popleft()
  67. elif self._total < self.maxconn:
  68. conn = pymysql.connect(**self.connect_kwargs)
  69. self._total += 1
  70. if conn is None:
  71. if timeout is not None and (time.time() - start) >= timeout:
  72. raise TimeoutError("获取数据库连接超时")
  73. time.sleep(0.05)
  74. continue
  75. # 尝试 ping 确认连接有效,若无效则关闭并尝试重建
  76. try:
  77. conn.ping(reconnect=True)
  78. return PooledConnection(conn, self)
  79. except Exception:
  80. try:
  81. conn.close()
  82. except Exception:
  83. pass
  84. with self._lock:
  85. self._total = max(0, self._total - 1)
  86. # 尝试新建连接并返回
  87. try:
  88. new_conn = pymysql.connect(**self.connect_kwargs)
  89. with self._lock:
  90. self._total += 1
  91. return PooledConnection(new_conn, self)
  92. except Exception:
  93. # 若重建失败且超时则抛出,否则继续重试
  94. if timeout is not None and (time.time() - start) >= timeout:
  95. raise TimeoutError("获取数据库连接超时(重建失败)")
  96. time.sleep(0.1)
  97. continue
  98. def putconn(self, conn):
  99. try:
  100. conn.ping(reconnect=True)
  101. with self._lock:
  102. self._pool.append(conn)
  103. except Exception:
  104. try:
  105. conn.close()
  106. except Exception:
  107. pass
  108. with self._lock:
  109. self._total = max(0, self._total - 1)
  110. def closeall(self):
  111. with self._lock:
  112. while self._pool:
  113. c = self._pool.popleft()
  114. try:
  115. c.close()
  116. except Exception:
  117. pass
  118. self._total = 0
  119. def get_mysql():
  120. global MYSQL_POOL
  121. if 'MYSQL_POOL' not in globals() or MYSQL_POOL is None:
  122. MYSQL_POOL = None
  123. try:
  124. MYSQL_POOL = MySQLConnectionPool(
  125. minconn=1,
  126. maxconn=20,
  127. host="120.24.26.108",
  128. port=3307,
  129. user="root",
  130. password="zhijiayun123456",
  131. database="drug_retrieve",
  132. charset="utf8mb4",
  133. cursorclass=pymysql.cursors.Cursor,
  134. )
  135. except Exception:
  136. MYSQL_POOL = None
  137. if MYSQL_POOL is None:
  138. return pymysql.connect(
  139. host="120.24.26.108",
  140. port=3307,
  141. user="root",
  142. password="zhijiayun123456",
  143. database="drug_retrieve",
  144. charset="utf8mb4",
  145. cursorclass=pymysql.cursors.Cursor,
  146. )
  147. return MYSQL_POOL.getconn()
  148. # 在程序退出时确保池中所有物理连接被关闭
  149. try:
  150. atexit.register(lambda: MYSQL_POOL.closeall() if 'MYSQL_POOL' in globals() and MYSQL_POOL is not None else None)
  151. except Exception:
  152. pass