多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

Sanic v22.3 版本深度解析:多应用同进程运行、文件扩展名路由与多项破坏性变更

Sanic v22.3 版本深度解析:多应用同进程运行、文件扩展名路由与多项破坏性变更 Sanic v22.3 版本深度解析多应用同进程运行、文件扩展名路由与多项破坏性变更【免费下载链接】sanicAccelerate your web app development | Build fast. Run fast.项目地址: https://gitcode.com/gh_mirrors/sa/sanic本篇文章以 Sanic 官方仓库的 v22.3 版本发布说明guide/content/en/release-notes/2022/v22.3.md为主线逐项拆解该版本引入的app.prepare(...)Sanic.serve()多应用并行运行机制、foo:ext文件扩展名路径参数、Authorization请求头解析增强以及空字符串匹配、GunicornWorker移除等破坏性变更。读完你将掌握 v22.3 的核心 API 用法、从旧版本迁移的完整清单并能对照仓库源码理解其底层实现原理。版本背景SCO 库统一进入 22 发布周期v22.3 是 Sanic 22 发布周期的第一个版本也是 Sanic 官方组织SCOSanic Community Organization下属多个核心库首次统一进入相同发布周期、遵循相同版本号模式的里程碑详见发布周期策略。从这一版本开始以下包使用同一套版本号节奏发布sanic-routing路由解析核心库sanic-testing测试工具库仓库内已有对应文档 guide/content/en/plugins/sanic-testing/README.mdsanic-ext扩展库文档见 guide/content/en/plugins/sanic-ext/getting-started.md这意味着升级 Sanic 时这些配套库通常也需要同步升级以保证路由行为、测试工具与扩展 API 的兼容性。完整变更明细可参考仓库内的 CHANGELOG.md 与 release-notes 变更日志索引。应用多实例并行运行prepareserve双阶段 API用法一个进程里跑多个应用v22.3 最大的功能亮点是让 Sanic 服务器支持在同一进程中并行运行多个应用实例。做法是对每个Sanic实例调用app.prepare(...)可对一个实例调用多次每次绑定唯一的 host/port 组合全部准备好之后再调用一次Sanic.serve()统一启动from sanic import Sanic app Sanic(One) app2 Sanic(Two) app.prepare(port9999) app.prepare(port9998) app.prepare(port9997) app2.prepare(port8888) app2.prepare(port8887) Sanic.serve()上述代码中两个应用会被并发启动分别绑定到 5 个不同的端口。需要注意该特性不支持通过 CLI 使用只能在 Python 代码中调用该模式定位为app.run(...)的替代方案app.run在 v22.3 中仍是完整支持的写法它本质上就是单实例场景下prepareserve的简写每个prepare调用必须绑定唯一的 host/port 组合否则会发生端口冲突。源码印证prepare的参数与校验逻辑从仓库源码 sanic/mixins/startup.py 可以看到prepare的完整签名几乎覆盖了app.run的全部运行参数host、port、dev、debug、auto_reload、versionHTTP/1.1 或 HTTP/3、ssl、sock、workers、protocol、backlog、access_log、unix、loop、reload_dir、fast、verbosity、single_process等。同时源码中还内置了若干运行期校验例如version 3HTTP/3时若已有其他实例被 prepare会抛出RuntimeError——HTTP/3 只能作为第一个被准备的实例且全局只能有一个 HTTP/3 worker同时传入fastTrue和workersX会抛出RuntimeErrorsingle_process与fast、workers 1、auto_reload同时使用会抛出RuntimeError。app.run与prepareserve的等价关系在运行指南 guide/content/en/guide/running/running.md 中也有明确说明。多实例模式最典型的落地场景之一是同时对外提供 HTTP/1.1 与 HTTP/3 服务app.prepare(version3)与app.prepare(version1)各一次此时 Sanic 会自动在 HTTP/1.1 响应上附加Alt-Svc头告知客户端 HTTP/3 可用——仓库中 sanic/touchup/schemes/altsvc.py 即负责该响应改写逻辑。BETA 功能foo:ext文件扩展名路径参数一个非常常见的需求是路由需要动态生成文件并且要求匹配到带扩展名的文件名。v22.3 新增了专门的路径参数类型foo:extapp.get(/path/to/filename:ext) async def handler(request, filename, ext): ...该写法会匹配任何以文件扩展名结尾的路径片段。你也可以进一步约束指定允许的扩展名集合或用其他路径参数类型约束文件名本身。例如只匹配纯数字文件名 .jpg扩展名app.get(/path/to/filenameint:extjpg) async def handler(request, filename, ext): ...发布说明中给出的完整示例表如下定义、示例、捕获到的 filename 与 extension定义示例filenameextensionfile:extpage.txtpagetxtfile:extjpgcat.jpgcatjpgfile:extjpg\|png\|gif\|svgcat.jpgcatjpgfileint:ext123.txt123txtfileint:extjpg\|png\|gif\|svg123.svg123svgfilefloat:exttar.gz3.14.tar.gz3.14tar.gz从表格可以看出两个关键点扩展名可通过|分隔枚举多个候选值extjpg|png|gif|svg并且支持复合扩展名exttar.gz匹配.tar.gz文件名部分可叠加其他参数类型int、float等Sanic 会自动完成类型转换因此filename参数会被解析为int/float而非字符串。破坏性变更一动态路径参数不再匹配空字符串v22.3 调整了动态路径参数的匹配规则动态参数只会匹配非空字符串。此前/foo或/foo:str这样的动态字符串参数会匹配任意字符串包括空字符串现在它只会匹配非空字符串。如果需要保留旧行为必须显式使用新的参数类型/foo:stroremptyapp.get(/path/to/foo:strorempty) async def handler(request, foo): ...升级到 v22.3 后凡是依赖动态参数可能为空的路由例如可选尾部参数都需要逐一检查并改用strorempty否则请求将无法命中原有路由。这是由路由解析逻辑变更引起的破坏性改动路由相关实现位于 sanic/router.py。破坏性变更二sanic.worker.GunicornWorker已被移除v22.3 出于升级服务器以支持多实例运行的考虑直接移除了sanic.worker.GunicornWorker——即便按常规弃用策略它本应经历一段过渡期。官方给出的原因是即便在移除之前通过 Gunicorn 部署 Sanic 也并非最优策略。如果仍需要使用gunicorn部署 Sanic官方建议采用 uvicorn 的部署方案即把 Sanic 作为ASGI 应用跑在 uvicorn worker 上。具体步骤如下。安装 uvicornpip install uvicorn然后以如下方式启动gunicorn path.to.sanic:app -k uvicorn.workers.UvicornWorker即用-k指定 worker 类为uvicorn.workers.UvicornWorker。该迁移方案在运行指南的 Gunicorn 章节中也有对应说明gunicorn myapp:app --bind 0.0.0.0:1337 --worker-class uvicorn.workers.UvicornWorker。需要提醒的是运行指南同时给出建议——除非确有需求否则应优先使用 Sanic 自带的服务器进行生产部署它在运行 Sanic 时具有更好的性能与一致性。Authorization请求头解析增强新增request.credentials在 v22.3 之前Sanic 对Authorization请求头只做部分解析通过request.token可以拿到以下两种形式中的令牌Authorization: Token SOME TOKEN HERE Authorization: Bearer SOME TOKEN HEREv22.3 扩展了对更多凭据类型的解析例如BASICAuthorization: Basic Z2lsLWJhdGVzOnBhc3N3b3JkMTIz现在可以通过request.credentials访问解析结果print(request.credentials) # Credentials(auth_typeBasic, tokenZ2lsLWJhdGVzOnBhc3N3b3JkMTIz, _usernamegil-bates, _passwordpassword123)源码印证Credentials数据类与 Basic 解码从源码看该功能的实现分为两层数据模型层sanic/models/http_types.py 定义了dataclass的Credentials包含auth_type、token、_username、_password四个字段。在__post_init__中若认证类型为 Basic会用b64decode对 token 解码并按:拆分为用户名和密码。username与password是属性访问器仅对 Basic Auth 开放其他认证类型下访问会抛出AttributeError请求对象层sanic/request/types.py 中request.credentials属性会读取Authorization头并解析出前缀与凭据构造Credentials对象后缓存到parsed_credentials解析失败ValueError则静默忽略。因此除了既有的request.token仅处理Bearer/Token前缀现在还可以通过request.credentials.auth_type区分认证类型并在 Basic 场景下直接使用request.credentials.username/request.credentials.password非常适合在中间件中做统一鉴权。CLI 参数注入应用工厂factoryv22.3 起如果使用应用工厂模式并通过 CLI 的--factory启动Sanic 会把解析后的 CLI 参数注入工厂函数def create_app(args): app Sanic(MyApp) print(args) return app$ sanic p:create_app --factory Namespace(modulep:create_app, factoryTrue, simpleFalse, host127.0.0.1, port8000, unix, certNone, keyNone, tlsNone, tlshostFalse, workers1, fastFalse, access_logFalse, debugFalse, auto_reloadFalse, pathNone, devFalse, motdTrue, verbosityNone, noisy_exceptionsFalse)进一步地配合--factory运行时你还可以向命令追加任意自定义参数它们会被一并注入Namespace$ sanic p:create_app --factory --foobar Namespace(modulep:create_app, factoryTrue, simpleFalse, host127.0.0.1, port8000, unix, certNone, keyNone, tlsNone, tlshostFalse, workers1, fastFalse, access_logFalse, debugFalse, auto_reloadFalse, pathNone, devFalse, motdTrue, verbosityNone, noisy_exceptionsFalse, foobar)工厂函数签名中的args即上方的argparse.Namespace对象。这为通过 CLI 向应用传递自定义配置提供了标准通道——例如sanic p:create_app --factory --envstaging后工厂内部即可根据args.env加载不同配置。CLI 相关实现可进一步参考 sanic/cli/arguments.py其中定义了各类 CLI 参数的解析与默认值。新增 reloader 进程级监听事件当以自动重载auto-reload模式运行时v22.3 新增了两个只在 reloader 进程上触发的监听事件reload_process_startreload_process_stop这两个事件只有在 reloader 实际运行时才会触发app.reload_process_start async def reload_start(*_): print( reload_start ) app.reload_process_stop async def reload_stop(*_): print( reload_stop )源码印证监听器注册与触发位置监听器装饰器定义在 sanic/mixins/listeners.py与before_reload_trigger、after_reload_trigger等重载事件并列。而真正的触发逻辑在 reloader 进程的实现 sanic/worker/reloader.py 中reloader 启动后、进入文件监控循环之前通过trigger_events(reloader_start, loop, app)触发reload_process_start监控循环因收到 SIGINT/SIGTERM 退出后触发reload_process_stop文件发生变化时会先触发before_reload_trigger重载后再触发after_reload_trigger后者携带changed变更文件集合。这意味着你可以在重载进程生命周期内执行清理临时资源记录重载日志等只属于 reloader 自身的逻辑而不会在 worker 进程中重复执行。监听器的loop参数不再必填v22.3 之前监听器函数约定必须接收loop参数现在可以省略。以下两种写法等效、均可正常工作app.before_server_start async def without(app): ... app.before_server_start async def with(app, loop): ...需要说明的是该改动只是放宽了签名约束loop参数仍然可用只是不再是必需项旧代码无需修改即可继续运行。移除--debug不再自动启动 reloaderv22.3 正式移除了debug 模式自动开启自动重载的行为该联动在 v21 中已被标记弃用。从此--debug/debugTrue只开启调试模式不会启动自动重载若希望同时获得调试模式与自动重载请使用--dev/devTrue。即版本说明中给出的等式dev debug mode auto reloader对应到源码 sanic/mixins/startup.py 中prepare在处理devTrue时会同时将debug与auto_reload置为True。CLI 侧-d, --dev的描述同样是 debug auto reload见 guide/content/en/guide/running/running.md 的 Development 参数表。弃用小写环境变量加载Sanic 一直支持加载带前缀的环境变量作为配置值。此前只要前缀匹配Sanic 并不区分键名的大小写但约定始终是键名应使用大写。v22.3 起加载到非大写键名时会输出弃用警告到 v22.9 时将只加载大写且带前缀的键。因此升级期间应尽快把所有小写或混合大小写的配置环境变量改为大写例如SANIC_SETTINGvalue而非sanic_settingvalue以免在 v22.9 中被静默忽略。升级到 v22.3 的迁移清单综合以上变更从 v21.x 升级到 v22.3 时建议逐项核对多应用部署若曾在同一进程内多端口提供服务改用app.prepare(...)Sanic.serve()若只是单应用app.run(...)无需改动。空字符串路由检查所有动态字符串参数路由确认不存在预期匹配空值的场景若需要改用foo:strorempty。Gunicorn 部署移除sanic.worker.GunicornWorker相关代码按 uvicorn 方案迁移为 ASGI 运行方式。认证解析需要 Basic Auth 的username/password时从request.token切换为request.credentials。开发模式把依赖debug 即自动重载的启动脚本改为--dev或devTrue。环境变量命名将所有配置环境变量规范为大写。配套库同步升级sanic-routing、sanic-testing、sanic-ext至同一 22.x 周期版本。其他动态v22.3 发布同期Packt 出版社推出了由 Sanic 作者撰写的《Python Web Development with Sanic》一书该书由 SCO 官方背书部分销售收入用于支持 Sanic 的持续开发。社区所有参与本次发布的贡献者包括文档、测试、代码等形式的贡献均在发布说明末尾被致谢完整的贡献者名单与财务捐赠渠道详见 guide/content/en/release-notes/2022/v22.3.md 原文。如果你想亲自体验这些新特性可参考 CONTRIBUTING.md 中的开发环境搭建方式在本地运行仓库自带的 tests/test_multi_serve.py 等测试用例验证多实例运行行为。【免费下载链接】sanicAccelerate your web app development | Build fast. Run fast.项目地址: https://gitcode.com/gh_mirrors/sa/sanic创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表