Django中Celery任务执行Selenium时出现连接池已满警告问题
解决Celery+Selenium Remote连接池耗尽警告
问题根源
这个"connection pool is full, discarding connection"警告,核心是HTTP连接池资源耗尽,和你用多线程共享Selenium Remote驱动的逻辑直接相关:
- Selenium Remote驱动通过HTTP请求和Grid节点通信,每个元素操作都会建立新的HTTP连接
- 主线程+守护线程同时操作同一个驱动,会并发创建大量连接,超出默认连接池的上限
- Celery Worker自身的连接池配置和Selenium的连接池未协调,导致连接堆积无法及时释放
具体修复方案
1. 给Selenium配置自定义连接池
显式设置Selenium HTTP客户端的连接池大小,匹配你的线程数量(比如2-5,根据实际线程数调整):
from selenium import webdriver from urllib3 import PoolManager # 自定义连接池,限制最大连接数 http_client = webdriver.remote.remote_connection.HttpClient( pool_manager=PoolManager(maxsize=3) ) # 创建Remote驱动时传入自定义客户端 driver = webdriver.Remote( command_executor="http://your-grid-host:4444/wd/hub", desired_capabilities=webdriver.DesiredCapabilities.CHROME, http_client=http_client )
2. 给驱动操作加线程锁
主线程和守护线程共享驱动时,必须加锁避免并发操作导致的连接混乱和资源竞争:
import threading from selenium.webdriver.common.by import By driver_lock = threading.Lock() # 守护线程的弹窗关闭逻辑 def popup_monitor(driver): while True: with driver_lock: try: close_btn = driver.find_element(By.XPATH, "//button[text()='关闭']") if close_btn.is_displayed(): close_btn.click() except Exception: pass time.sleep(1) # 主线程操作驱动时同样加锁 with driver_lock: driver.find_element(By.ID, "username").send_keys("your-username")
3. 任务结束后彻底清理连接
任务执行完毕,必须确保驱动被正确关闭,同时清理连接池残留:
try: # 执行你的业务逻辑 ... finally: # 加锁关闭驱动,确保线程安全 with driver_lock: driver.quit() # quit会主动关闭所有和Grid的HTTP连接 # 手动清理连接池(可选,进一步避免泄漏) http_client.pool_manager.clear()
4. 调整Celery Worker的连接池配置
在Celery配置文件中,优化Worker的连接池和进程设置,避免资源冲突:
# celery.py 或 Django settings.py CELERY_WORKER_CONCURRENCY = 2 # 减少并发数,避免过多进程争抢连接 CELERY_WORKER_MAX_TASKS_PER_CHILD = 1 # 每个Worker进程处理完1个任务就重启,彻底清理连接 CELERY_BROKER_POOL_LIMIT = 10 # 增大Broker连接池上限
5. 优化弹窗处理逻辑(可选)
如果业务允许,尽量避免用守护线程,把弹窗检测整合到主线程的循环中,减少线程数:
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC # 主线程中定期检查弹窗 while True: # 先处理弹窗(超时1秒,不阻塞主流程) try: close_btn = WebDriverWait(driver, 1).until( EC.element_to_be_clickable((By.XPATH, "//button[text()='关闭']")) ) close_btn.click() except Exception: pass # 执行主业务操作 driver.find_element(By.ID, "input-field").send_keys("content") # 根据业务逻辑设置退出条件 if some_condition: break
内容的提问来源于stack exchange,提问作者NicoCaldo
相关产品推荐
相关产品推荐

