新闻详情

新闻详情

首页 / 资讯中心 / 详情

Starlette Lifespan 生命周期管理:启动关闭钩子、State 状态共享与类型安全访问实战

发布时间:2026/11/4 18:18:07来源:尧图网络
Starlette Lifespan 生命周期管理:启动关闭钩子、State 状态共享与类型安全访问实战
Starlette Lifespan 生命周期管理启动关闭钩子、State 状态共享与类型安全访问实战【免费下载链接】starletteThe little ASGI framework that shines. 项目地址: https://gitcode.com/gh_mirrors/st/starlette导读在 Starlette 这类 ASGI 框架中数据库连接池、HTTP 客户端、缓存等资源的创建与销毁往往需要与应用进程的启动、关闭保持同步。lifespan就是 Starlette 提供的官方机制通过一个异步上下文管理器钩子让你在应用开始接收请求之前完成初始化在所有连接关闭、后台任务结束后完成清理。本文基于 docs/lifespan.md 展开结合当前仓库源码完整讲解 lifespan 的基本用法、生命周期时序保证、state状态共享、属性式与字典式两种访问方式后者在 Starlette 0.52.0 起引入可显著提升类型安全以及在TestClient中如何正确触发 lifespan 的测试写法。认识 lifespan应用的启动与关闭钩子Starlette 应用可以注册一个 lifespan 处理器用来承载应用启动前与应用关闭时需要执行的代码。其写法依托 Python 标准库的contextlib.asynccontextmanager装饰器import contextlib from starlette.applications import Starlette contextlib.asynccontextmanager async def lifespan(app): async with some_async_resource(): print(Run at startup!) yield print(Run on shutdown!) routes [ ... ] app Starlette(routesroutes, lifespanlifespan)要点解读yield之前的代码块在应用启动阶段执行对应打印 Run at startup!yield之后的代码块在应用关闭阶段执行对应打印 Run on shutdown!。通过Starlette(..., lifespanlifespan)把该处理器挂载到应用上函数签名中的app参数即当前应用实例可用于读取app.state等。这种结构天然适配随用随建、随关随毁的资源管理模式例如async with some_async_resource():可以换成任何支持异步上下文协议的资源。生命周期时序的两条硬性保证原文档明确了两条重要的时序语义请求不会被提前处理Starlette 在 lifespan 运行完成之前不会开始处理任何进入的请求。也就是说yield真正放行了请求服务如果启动阶段抛错或未完成应用不会对外提供服务。关闭在一切收尾之后lifespan 的 teardownyield之后的代码会在所有连接都已关闭、所有进程内后台任务都已完成之后才执行。这保证了你关闭连接池时不会再收到新的请求或后台任务占用。对于需要在后台维护异步任务的场景原文档建议优先使用anyio.create_task_group()来管理这些异步任务使其纳入同一生命周期管理范围避免任务游离于应用之外。源码层面的实现印证lifespan参数最终从Starlette.__init__传入内部的Router见 starlette/applications.py 的构造函数与self.router Router(routes, lifespanlifespan)。真正的执行逻辑在 starlette/routing.py 的Router.lifespan方法中async def lifespan(self, scope: Scope, receive: Receive, send: Send) - None: started False app: Any scope.get(app) await receive() # 等待 lifespan.startup 消息 try: async with self.lifespan_context(app) as maybe_state: if maybe_state is not None: if state not in scope: raise RuntimeError(The server does not support state in the lifespan scope.) scope[state].update(maybe_state) await send({type: lifespan.startup.complete}) started True await receive() # 等待 lifespan.shutdown 消息 except BaseException: exc_text traceback.format_exc() if started: await send({type: lifespan.shutdown.failed, message: exc_text}) else: await send({type: lifespan.startup.failed, message: exc_text}) raise else: await send({type: lifespan.shutdown.complete})从这段实现可以推断几个关键事实lifespan 底层基于 ASGI 规范的lifespanscope 协议服务器先发送lifespan.startup应用在启动阶段完成后回送lifespan.startup.complete进程结束时服务器发送lifespan.shutdown应用完成 teardown 后回送lifespan.shutdown.complete。若应用没有显式注册 lifespanRouter.__init__会为其挂上一个空操作的_DefaultLifespan见 starlette/routing.py保证协议流程始终存在、应用无需关心是否有自定义生命周期逻辑。lifespan 处理器抛出的任何异常都会被捕获并转成lifespan.startup.failed/lifespan.shutdown.failed消息上报同时重新抛出方便服务器层记录错误。旧式 lifespan 写法与弃用提示从 starlette/routing.py 的源码可以看出Starlette 目前仍兼容两种旧式写法但会发出StarletteDeprecationWarning弃用告警直接传入异步生成器函数async def lifespan(app): ... yield ...直接传入同步生成器函数def lifespan(app): ... yield ...。两者都会在Router.__init__中被包装成标准上下文管理器但官方建议统一改用contextlib.asynccontextmanager装饰的形式这也是原文档所有示例采用的写法。在 starlette/types.py 中可以看到 lifespan 的类型定义StatelessLifespan Callable[[AppType], AbstractAsyncContextManager[None]] StatefulLifespan Callable[[AppType], AbstractAsyncContextManager[Mapping[str, Any]]] Lifespan StatelessLifespan[AppType] | StatefulLifespan[AppType]即不带状态共享的 lifespan 上下文产出None带状态共享的则产出Mapping[str, Any]通常是字典。Lifespan State在生命周期与请求之间共享对象启动时创建的资源数据库连接池、HTTP 客户端等如果只在 lifespan 内部持有请求处理器将无从获取。为此 Starlette 引入了state概念它本质上是一个字典用于在 lifespan 与各个请求之间共享对象。原文档给出了完整示例——在启动阶段创建一个httpx.AsyncClient并把它注入到请求处理函数中import contextlib from typing import AsyncIterator, TypedDict import httpx from starlette.applications import Starlette from starlette.requests import Request from starlette.responses import PlainTextResponse from starlette.routing import Route class State(TypedDict): http_client: httpx.AsyncClient contextlib.asynccontextmanager async def lifespan(app: Starlette) - AsyncIterator[State]: async with httpx.AsyncClient() as client: yield {http_client: client} async def homepage(request: Request) - PlainTextResponse: client request.state.http_client response await client.get(https://www.example.com) return PlainTextResponse(response.text) app Starlette( lifespanlifespan, routes[Route(/, homepage)] )这里值得注意的细节lifespan 通过yield {http_client: client}把状态以字典形式吐给应用只要资源存活在async with块内整个应用运行期间都能使用该客户端。请求端通过request.state.http_client属性式访问取回对象与 lifespan 内共享的是同一个httpx.AsyncClient实例因此连接复用、并发安全等行为完全一致。原文档特别强调请求端收到的state是 lifespan 处理器中state的浅拷贝shallow copy。即字典本身是复制出来的但字典中的值如client对象仍是同一个引用——这正是共享对象得以成立的原因。源码印证state 的流转链路结合源码可以还原 state 的完整流转路径lifespan 中yield出的字典在 starlette/routing.py 中被合并进 ASGI scope 的state字段scope[state].update(maybe_state)。当请求到达时Request.state属性见 starlette/requests.py会基于同一个scope[state]字典构造State对象self._state State(self.scope[state])从而让请求侧读到与 lifespan 相同的内容。State类的实现位于 starlette/datastructures.py它把内部数据存放在self._state字典中并同时实现了属性式访问__getattr__/__setattr__与字典式访问__getitem__/__setitem__两套协议这也正是下一节两种访问语法都能工作的底层原因。访问 State属性式与字典式两种语法state既可以用属性式语法访问request.state.foo也可以用字典式语法访问request.state[foo]。字典式语法是在Starlette 0.52.02026 年 1 月引入的其初衷是随着Request变成对 state 类型参数化generic的类型字典式访问可以带来更好的类型安全。原文档给出了使用TypedDict配合泛型Request[State]的完整示例from collections.abc import AsyncIterator from contextlib import asynccontextmanager from typing import TypedDict import httpx from starlette.applications import Starlette from starlette.requests import Request from starlette.responses import PlainTextResponse from starlette.routing import Route class State(TypedDict): http_client: httpx.AsyncClient asynccontextmanager async def lifespan(app: Starlette) - AsyncIterator[State]: async with httpx.AsyncClient() as client: yield {http_client: client} async def homepage(request: Request[State]) - PlainTextResponse: client request.state[http_client] reveal_type(client) # Revealed type is httpx.AsyncClient response await client.get(https://www.example.com) return PlainTextResponse(response.text) app Starlette(lifespanlifespan, routes[Route(/, homepage)])关键差异在于类型标注request: Request[State]把请求的类型参数声明为State这个TypedDict随后request.state[http_client]会被类型检查器精确推断为httpx.AsyncClient示例中的reveal_type(client)表明推导类型正是httpx.AsyncClient从而在编译期或编辑器内就拦截键名拼写错误、取值类型不符等问题。这种写法同样适用于 WebSocket 端点WebSocket同样是泛型化的async def websocket_endpoint(websocket: WebSocket[State]) - None: await websocket.accept() client websocket.state[http_client] response await client.get(https://www.example.com) await websocket.send_text(response.text) await websocket.close() app Starlette(lifespanlifespan, routes[WebSocketRoute(/ws, websocket_endpoint)])为什么属性式访问没有获得同等的类型推断原文档专门附了一段说明社区曾多次尝试让属性式访问request.state.http_client也获得同样的类型安全但始终没有令人满意的方案——要么会引入破坏性变更breaking changes要么受限于 Python 类型系统的能力边界typing limitations。因此最终选择了字典式访问作为类型安全的官方路径属性式访问仍然可用只是类型推断能力有限。仓库测试中也验证了这一能力在 tests/test_applications.py 中定义了CustomState类型的websocket_state与state_count端点分别通过websocket.state[count]与request.state[count]读取 lifespan 注入的状态并由 test_request_state 与 test_websocket_state 两个测试用例覆盖验证。在测试中运行 lifespan在单元测试中若直接使用TestClient(app)而不进入上下文lifespan 并不会被触发启动/关闭阶段的副作用也就不会执行。正确做法是把TestClient用作上下文管理器保证 lifespan 被调用from example import app from starlette.testclient import TestClient def test_homepage(): with TestClient(app) as client: # Applications lifespan is called on entering the block. response client.get(/) assert response.status_code 200 # And the lifespans teardown is run when exiting the block.时序说明注释即文档语义进入with块时触发应用的 lifespan 启动阶段进入yield之前此时共享状态已注入测试内发出的所有请求都能访问退出with块时运行 lifespan 的 teardownyield之后的清理代码此时资源释放完毕块外断言可验证清理副作用例如连接是否关闭、后台任务是否结束。这一机制在 starlette/testclient.py 中由TestClient.lifespan方法实现它构造{type: lifespan, state: ...}的 ASGI scope 调用应用并通过wait_startup/wait_shutdown两个协程分别等待lifespan.startup.complete或lifespan.startup.failed与lifespan.shutdown.complete或lifespan.shutdown.failed消息从而在上下文管理器的进入/退出边界完成与真实服务器一致的生命周期握手。仓库 tests/test_applications.py 中的test_app_async_cm_lifespan测试也验证了进入上下文前startup_complete为 False、退出上下文后清理完成的完整行为。参考路径速览官方文档docs/lifespan.md应用入口与lifespan参数传递starlette/applications.pylifespan 协议实现、_DefaultLifespan、旧式写法弃用starlette/routing.pyLifespan/StatelessLifespan/StatefulLifespan类型定义starlette/types.pyRequest.state属性实现starlette/requests.pyState类属性式与字典式双协议starlette/datastructures.pyTestClient生命周期握手实现starlette/testclient.py生命周期与状态共享的测试用例tests/test_applications.py【免费下载链接】starletteThe little ASGI framework that shines. 项目地址: https://gitcode.com/gh_mirrors/st/starlette创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

【外设】之大彩串口显示屏 2026/11/4 15:56:54

【外设】之大彩串口显示屏

大彩串口屏初步使用 1 .官网下载 STM32 屏幕 GUI 设计资料 http://www.gz-dc.com/category/typeid/4112 找到 STM32 Keil 工程,移植相关代码因项目而异进行移植,由于项目简单,本人只对用到的指令接口进行修改。 比如:注意事项&…

阅读更多 →
无法下载Windows系统iso文件 2026/11/3 12:09:35

无法下载Windows系统iso文件

当我遇到这个问题的时候,我打开了一个网站: 登录 然后我打算下载的时候: 突然那个官方的连接就可以下载了:

阅读更多 →
【清华代码熊】DeepSeek V4.1 Flash 后训练详解 2026/11/4 17:01:23

【清华代码熊】DeepSeek V4.1 Flash 后训练详解

📌 上期解析了 DeepSeek V4.1 Flash 模型架构改进,本期解析 DeepSeek V4.1 Flash 预训练/后训练技术: 🌟 预训练:45T 文本 多模态混合语料、直接训练 sparse attention(取消 DeepSeek V4 的 dense 冷启动&…

阅读更多 →
Shuffle-R1: Efficient RL framework for Multimodal Large Language Models via Data-centric Dynamic ... 2026/11/4 12:33:12

Shuffle-R1: Efficient RL framework for Multimodal Large Language Models via Data-centric Dynamic ...

文章主要内容和创新点 主要内容 本文聚焦于多模态大语言模型(MLLM)强化学习(RL)训练中的效率问题,提出了一个名为Shuffle-R1的框架。研究发现,当前RL训练存在两个关键缺陷: 优势值坍缩(Advantage Collapsing):批次中大多数优势值集中在零附近,导致有效梯度信号被淹…

阅读更多 →
PRvL: Quantifying the Capabilities and Risks of Large Language Models for PII Redaction 2026/11/4 1:25:31

PRvL: Quantifying the Capabilities and Risks of Large Language Models for PII Redaction

一、文章主要内容总结 本文聚焦于利用大型语言模型(LLMs)实现个人身份信息(PII)脱敏的研究,旨在解决传统脱敏方法(如基于规则的系统、领域特定命名实体识别(NER)模型)泛化能力差、跨格式/跨语境适应性弱的问题。 研究通过全面评估多种LLM架构(包括密集型LLM(D-LLM…

阅读更多 →
LLaVA-RE: Binary Image-Text Relevancy Evaluation with Multimodal Large Language Model 2026/11/4 3:51:11

LLaVA-RE: Binary Image-Text Relevancy Evaluation with Multimodal Large Language Model

文章主要内容和创新点 主要内容 本文聚焦于二进制图像-文本相关性评估任务(判断图像与文本“相关”或“不相关”),针对该任务中文本格式多样、相关性定义随场景变化等挑战,提出了基于多模态大语言模型(MLLM)的解决方案LLaVA-RE。 模型设计:LLaVA-RE基于LLaVA 1.5架构,…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉