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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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