Skip to content

[功能请求] MysqlDB 支持 SSL 配置(ssl_disabled / ssl 等参数) #316

Description

@LiYilin56

Is your feature request related to a problem? Please describe.

Python 环境 OpenSSL 自身异常导致 pymysql SSL 握手失败(非 MySQL 服务器问题),但 feapder 没有提供跳过 SSL 的配置入口,用户只能修改源码或降级环境。

场景复现

问题出在 Python 端,不是 MySQL 端。

Windows 11 上,conda-forge 的 OpenSSL 3.6.0+ 调用 ssl.create_default_context() 读取 Windows 系统证书商店时就会崩溃:

ssl.SSLError: [ASN1: NOT_ENOUGH_DATA] not enough data (_ssl.c:4192)

这个错误发生在 Python 进程内部_load_windows_store_certsload_verify_locations),此时还没有和 MySQL 建立任何网络连接,与 MySQL 的 tls_versionhave_ssl 等配置无关。

pymysql 连接时默认启用 SSL,触发 create_default_context() 直接本地报错,导致 MysqlDB 初始化失败。此时只需要 ssl_disabled=True 就能跳过 SSL(连接本地 MySQL 本就不需要),但 feapder 没有提供任何配置入口

更糟糕的是,feapder 存在一个格式化 Bug(6 个 {} 占位符 vs 7 个参数),导致真正的 SSL 异常被丢弃,用户看到的错误信息是误导性的 exception: utf8mb4,完全无法定位真实原因。

为什么会走到这一步

MysqlDB.__init__ 已经支持 **kwargs 透传给 PooledDBpymysql

# feapder/db/mysqldb.py, line 43-75
def __init__(self, ip=None, port=None, db=None, user_name=None,
             user_pass=None, charset="utf8mb4", set_session=None, **kwargs):
    ...
    self.connect_pool = PooledDB(creator=pymysql, ..., **kwargs)

但问题在于:

  1. 框架内部大量 MysqlDB() 无参调用(共 8 处),都无法传递 SSL 参数:
# 所有 8 处无参调用:
feapder/commands/create/create_item.py:29      self._db = MysqlDB()
feapder/commands/create/create_table.py:23     self._db = MysqlDB()
feapder/core/base_parser.py:128               self._mysqldb = mysqldb or MysqlDB()
feapder/core/base_parser.py:224               batch_info = MysqlDB().find(sql)
feapder/core/spiders/batch_spider.py:117        self._mysqldb = MysqlDB()
feapder/core/spiders/task_spider.py:114         self._mysqldb = MysqlDB()
feapder/network/user_pool/normal_user_pool.py:67 self._mysqldb = MysqlDB()
feapder/pipelines/mysql_pipeline.py:25          self._to_db = MysqlDB()
  1. setting.py 没有 MySQL 额外参数配置项:
# feapder/setting.py — 当前只有 5 个 MySQL 配置
MYSQL_IP = os.getenv("MYSQL_IP")
MYSQL_PORT = int(os.getenv("MYSQL_PORT", 3306))
MYSQL_DB = os.getenv("MYSQL_DB")
MYSQL_USER_NAME = os.getenv("MYSQL_USER_NAME")
MYSQL_USER_PASS = os.getenv("MYSQL_USER_PASS")
# 没有 MYSQL_KWARGS 或 MYSQL_SSL_DISABLED

这意味着:即使 MysqlDB 底层支持透传,框架内 8 处无参调用用户自定义爬虫无法让用户配置这些参数。

补充说明feapder/setting.py 底部有 from setting import *,会自动加载项目 setting.py 中的值覆盖默认配置。这也是为什么 MYSQL_KWARGS 方案可行——只需在 feapder 的 setting.py 中定义默认值,在 MysqlDB.__init__ 中读取,用户即可在项目的 setting.py 中覆盖。


Describe the solution you'd like

新增 MYSQL_KWARGS 配置项(与已有的 REDISDB_KWARGS 设计模式一致),用户可通过 setting.py 传递任意 pymysql 参数。

改动 1:feapder/setting.py — 新增配置项

# 在第 22 行 MYSQL_USER_PASS 下方新增
# 连接mysql时携带的其他参数,如 ssl_disabled=True
# 参考 https://pymysql.readthedocs.io/en/latest/modules/connections.html
MYSQL_KWARGS = dict()

改动 2:feapder/db/mysqldb.py — 读取并合并配置

  def __init__(
      self, ip=None, port=None, db=None, user_name=None, user_pass=None,
      charset="utf8mb4", set_session=None, **kwargs
  ):
      ...
+     # 合并 setting 中的 MySQL 连接额外参数
+     extra_kwargs = getattr(setting, 'MYSQL_KWARGS', {})
+     extra_kwargs.update(kwargs)
+
      try:
          self.connect_pool = PooledDB(
              creator=pymysql,
              ...
              charset=charset,
              setsession=set_session,
              cursorclass=cursors.SSCursor,
-             **kwargs
+             **extra_kwargs
          )

用户使用方式

setting.py 中配置:

MYSQL_KWARGS = {
    "ssl_disabled": True,  # 禁用 SSL(适用于本地开发环境)
}

或传递其他 pymysql 参数:

MYSQL_KWARGS = {
    "ssl_disabled": True,
    "connect_timeout": 10,
    "read_timeout": 30,
    "autocommit": True,
}

为什么推荐这个方案而非新增专用配置项

方案 改动量 通用性 与现有设计一致性
MYSQL_KWARGS(推荐) 2 个文件,~4 行 可传递任意 pymysql 参数 REDISDB_KWARGS 一致
MYSQL_SSL_DISABLED 2 个文件,~6 行 只能控制 SSL 新增模式

Describe alternatives you've considered

  1. feapder create 命令行加 --ssl-disabled 参数:治标不治本,只修了 CLI,用户自己的爬虫代码仍然无法配置。

  2. MysqlDB.__init__ssl_disabled=False 参数:比方案 A 更重,且只能解决 SSL 问题,下次用户要传 connect_timeout 还得再改一次。

  3. 修改 pymysql 的全局默认配置:会影响同一环境中所有使用 pymysql 的项目,副作用太大。

  4. 降级 OpenSSL:不解决框架问题,只是绕过了触发条件。其他用户遇到类似环境兼容性问题时依然无解。


Additional context

关联 Bug

此问题与 MysqlDB 的错误日志格式化 Bug 密切相关——那个 Bug 正是因为这个 SSL 错误才被发现的。格式化 Bug 导致真实的 SSLError 被丢弃,显示为误导性的 exception: utf8mb4。建议一起修复。

环境信息

说明
feapder 1.9.3(最新)
Python 3.9.23
OpenSSL 3.6.3(conda-forge) ← 问题根因:此版本在 Windows 上无法加载系统证书商店
pymysql 2.2.8
DBUtils 3.1.2
OS Windows 11 Home China
MySQL 8.x MySQL 本身正常,ssl_disabled=True 可正常连接

临时 Workaround

# 方案 1:降级 OpenSSL
conda install openssl=3.5.5 -c conda-forge

# 方案 2:手动修改 feapder 源码(不推荐)
# 在 mysqldb.py 第 59 行 PooledDB 调用中加入 ssl_disabled=True

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions