首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在Django异步视图之间共享(初始化和关闭) aiohttp.ClientSession以使用连接池

如何在Django异步视图之间共享(初始化和关闭) aiohttp.ClientSession以使用连接池
EN

Stack Overflow用户
提问于 2022-06-14 09:08:56
回答 1查看 391关注 0票数 2

从3.1版开始,Django就支持异步视图,所以它很适合对外部HTTP的非阻塞调用(例如,使用艾奥赫特)。

常见下面的代码示例,我认为它在概念上是错误的(尽管它工作得非常好):

代码语言:javascript
运行
复制
import aiohttp
from django.http import HttpRequest, HttpResponse

async def view_bad_example1(request: HttpRequest):
    async with aiohttp.ClientSession() as session:
        async with session.get("https://example.com/") as example_response:
            response_text = await example_response.text()
            return HttpResponse(response_text[:42], content_type="text/plain")

此代码为每个传入请求创建一个ClientSession,这是效率低下的。然后aiohttp就不能使用例如连接池。

不要为每个请求创建一个会话。很可能每个应用程序都需要一个会话来执行所有请求。 来源:make#make-a-请求

这同样适用于httpx:

另一方面,客户端实例使用HTTP连接池。这意味着当您向同一台主机发出多个请求时,客户端将重用底层TCP连接,而不是对每个请求重新创建一个。 来源:https://www.python-httpx.org/advanced/#why-use-a-client

有没有办法在Django中全局实例化aiohttp.ClientSession,以便在多个请求之间共享这个实例?不要忘记ClientSession必须在运行的事件循环(为什么在事件循环之外创建ClientSession很危险?)中创建,因此我们不能实例化它,例如在Django设置中或作为模块级变量。

我得到的最接近的就是这个密码。但是,我认为这段代码很难看,没有解决例如关闭会话的问题。

代码语言:javascript
运行
复制
CLIENT_SESSSION = None

async def view_bad_example2(request: HttpRequest):
    global CLIENT_SESSSION

    if not CLIENT_SESSSION:
        CLIENT_SESSSION = aiohttp.ClientSession()

    example_response = await CLIENT_SESSSION.get("https://example.com/")
    response_text = await example_response.text()

    return HttpResponse(response_text[:42], content_type="text/plain")

基本上,我在寻找可以在异步上下文中使用来自FastAPI的事件的等价的创建/关闭某些资源

顺便提一下,在这两个视图之间使用k6进行性能比较:

  • view_bad_example1avg=1.32s min=900.86ms med=1.14s max=2.22s p(90)=2s p(95)=2.1s
  • view_bad_example2avg=930.82ms min=528.28ms med=814.31ms max=1.66s p(90)=1.41s p(95)=1.52s
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-06-15 15:42:05

Django没有实现ASGI生命周期协议。

参考文献:https://github.com/django/django/pull/13636

星体有吗?。FastAPI直接使用Starlette的事件处理程序实现。

以下是如何使用Django实现这一目标:

  1. 在Django的ASGIHandler子类中实现ASGI生命周期协议。
代码语言:javascript
运行
复制
import django
from django.core.asgi import ASGIHandler


class MyASGIHandler(ASGIHandler):
    def __init__(self):
        super().__init__()
        self.on_shutdown = []

    async def __call__(self, scope, receive, send):
        if scope['type'] == 'lifespan':
            while True:
                message = await receive()
                if message['type'] == 'lifespan.startup':
                    # Do some startup here!
                    await send({'type': 'lifespan.startup.complete'})
                elif message['type'] == 'lifespan.shutdown':
                    # Do some shutdown here!
                    await self.shutdown()
                    await send({'type': 'lifespan.shutdown.complete'})
                    return
        await super().__call__(scope, receive, send)

    async def shutdown(self):
        for handler in self.on_shutdown:
            if asyncio.iscoroutinefunction(handler):
                await handler()
            else:
                handler()


def my_get_asgi_application():
    django.setup(set_prefix=False)
    return MyASGIHandler()
  1. 替换application中的asgi.py。
代码语言:javascript
运行
复制
# application = get_asgi_application()
application = my_get_asgi_application()
  1. 实现一个帮助器get_client_session来共享实例:
代码语言:javascript
运行
复制
import asyncio
import aiohttp
from .asgi import application

CLIENT_SESSSION = None

_lock = asyncio.Lock()


async def get_client_session():
    global CLIENT_SESSSION

    async with _lock:
        if not CLIENT_SESSSION:
            CLIENT_SESSSION = aiohttp.ClientSession()
            application.on_shutdown.append(CLIENT_SESSSION.close)

    return CLIENT_SESSSION

用法:

代码语言:javascript
运行
复制
async def view(request: HttpRequest):
    session = await get_client_session()
    
    example_response = await session.get("https://example.com/")
    response_text = await example_response.text()

    return HttpResponse(response_text[:42], content_type="text/plain")
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72614335

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档