Flask集成RQ Redis调用Playwright浏览器实例遇序列化报错
解决RQ传递Playwright浏览器实例的序列化错误
问题原因
你遇到的TypeError: cannot pickle 'LockType' object本质是:RQ需要将任务参数序列化(pickle)后存入Redis,但Playwright的browser实例内部包含锁(LockType)这类无法被序列化的对象,直接传递实例必然失败。而且多进程共享同一个Playwright浏览器实例本身就存在并发安全问题,gevent补丁无法解决这个核心矛盾。
可行解决方案
方案1:每个Worker进程独立初始化浏览器
让每个RQ Worker进程自己维护一个浏览器实例,任务函数直接复用该实例,无需从Flask进程传递。
修改任务工具函数(src/flask/utils.py)
from playwright.sync_api import sync_playwright # 每个Worker进程仅初始化一次浏览器 _browser = None def get_browser(): global _browser if not _browser: playwright = sync_playwright().start() _browser = playwright.chromium.launch(headless=True) return _browser def return_map_info(url, type): # 获取当前Worker的浏览器实例 browser = get_browser() page = browser.new_page() # 你的网页抓取逻辑 page.goto(url) # ... 执行其他Playwright操作 page.close() # 返回抓取结果
修改Flask任务入队代码(app.py)
去掉传递browser实例的参数:
# 修改后代码 task = q.enqueue(return_map_info, kwargs={ 'url':url, 'type':search_type })
方案2:用RQ Worker初始化钩子统一创建浏览器
通过RQ的worker_init信号,在Worker启动时初始化浏览器实例,所有任务复用该实例。
创建Worker初始化文件(worker_setup.py)
from rq import worker from playwright.sync_api import sync_playwright def init_browser(sender, **kwargs): # 给当前Worker实例绑定浏览器 playwright = sync_playwright().start() sender.browser = playwright.chromium.launch(headless=True) # 绑定Worker启动时的初始化钩子 worker.connect(init_browser, signal=worker.worker_init)
启动RQ Worker时加载初始化文件
rq worker -w rq.Worker -e worker_setup
修改任务函数(src/flask/utils.py)
def return_map_info(url, type): from rq import get_current_worker # 获取当前Worker的浏览器实例 worker = get_current_worker() browser = worker.browser page = browser.new_page() # 抓取逻辑... page.close() # 返回结果
同样,Flask入队时无需传递browser参数。
关键注意事项
Playwright浏览器实例是进程内资源,不能跨进程共享,每个RQ Worker进程必须拥有独立的实例,这是避免序列化错误和并发问题的核心原则。
内容的提问来源于stack exchange,提问作者NewbieProgrammer
相关产品推荐
相关产品推荐

