标签: python-asyncio

将 udp m2tp 单播流代理到流式 sanic.response 时 CPU 使用率差异很大

我开发了一个代理,可以让我通过 http 以 udp 单播 m2tp 流的形式使用 ISP 提供的回放视频。为此,我使用 python、asyncio 和 Sanic。

它工作得很好,但有一些我无法解释的事情。某些流使用一个 CPU 核心 (i7-3770) 的不到 8%,而其他特定流则消耗高达 30%。所有流原则上都是相同的,我不对它们进行任何处理,只是从 udp 套接字读取并写入流sanic.Response,基本上是这样的:

import asyncio_dgram
import socket

from contextlib import closing
from Sanic import Sanic, response

app = Sanic()

MIME = 'video/MP2T'

@app.get('/some/url')
async def handle_url(request):
    host = socket.gethostbyname(socket.gethostname())
    client_port = 43545
    async def my_streaming_fn(response):
        with closing(await asyncio_dgram.bind((host, client_port))) as stream:
            while True:
                data, remote_addr = await stream.recv()
                await response.write(data)

    return response.stream(my_streaming_fn, content_type=MIME)
Run Code Online (Sandbox Code Playgroud)

所有流均到达单独的 VLAN,所有 ts 数据包均为 1344 字节(无标头为 1316 …

python streaming udp python-asyncio sanic

5
推荐指数
0
解决办法
109
查看次数

从 Asyncio 事件循环部署 Celery 任务

我有一个当前使用 asyncio 编写的后端应用程序:用于 Web 服务器的 fastapi,用于异步数据库驱动程序的 sqlalchemy 1.4 + asyncpg。我需要将任务部署给将运行和更新主机应用程序的工作人员。目前我正在使用aio_pika,但想要更强大的东西,例如celerywith flower。

我知道 celery 没有与 asyncio 集成。我也读过这样的答案,我担心的是任务不是异步的,这是微不足道的。我担心从主事件循环内启动任务。

我的主要问题是my_task.delay()/是否完全my_task.apply_async()阻塞正在运行的线程?如果是这样,更好的方法是使用来自中央或 a的多处理工作人员,然后仅从该工作人员进程部署 celery 任务吗?getmp.QueueProcessPoolExecutor

我想要部署任务,并且最好在任务完成时收到通知。不过,这可以通过界面在任务本身内完成fastapi。我只是想确保部署任务不会阻止异步事件循环。

python asynchronous celery celery-task python-asyncio

5
推荐指数
1
解决办法
1774
查看次数

asyncio - 重新引发任务异常

我正在使用 asyncio 进行一些 TCP 通信。我有一个无限循环的Receive()函数。read()这作为后台任务运行,使用asyncio.create_task(Receive()).

现在,如果连接被对等方关闭,则会引发异常(或者可能是任何其他异常),我在函数中捕获该异常Receive()。但是,我想重新引发该异常,以便外部代码可以决定要做什么(例如重新连接)。

由于异常是在任务中引发的,我不知道如何检索它。

我试图创建一个例子来说明我的意思:

import asyncio

async def divide(x):
    try:
        return 1/x
    except Exception as e:
        print("Divide inner exception: ", e)
        raise   # Re-raise so main() can handle it

async def someFn():
    asyncio.create_task(divide(0))  # Exception is never retrieved
    # await divide(0) # This will raise two exceptions - the original in divide() and in main()

async def main():
    try:
        await someFn()
        # Do other things while someFn() runs …
Run Code Online (Sandbox Code Playgroud)

python python-asyncio

5
推荐指数
1
解决办法
1876
查看次数

如何向FastAPI中间件传递参数并影响其处理逻辑?

我们知道,通过request.state我们可以将一些自定义数据从中间件的处理前过程传递给处理程序,从而影响其行为,我目前想知道处理程序如何影响中间件之后的逻辑。

我的具体业务场景是,我有一个路由地址(举/api个例子),它的作用是计算一个动态结果,会消耗比较长的时间,返回比较大的json响应。从操作上来说,提高效率的一个有效方法是使用一些缓冲区(如redis)来缓存其结果,这样每次缓存命中时都可以节省计算时间。

由于我的内存有限,我想将 gzip 压缩的字节存储在缓冲区中而不是原始 json 流中,这将大大增加我可以处理的缓存量。numbers具体来说,由于响应内容较多,未经gzip处理的响应内容通常在20MB左右,而压缩后只有1MB左右。这意味着,使用 1GB 内存,在不压缩的情况下我只能缓存 50 个不同的响应,而在压缩的情况下我可以缓存 1000 个,这是一个难以忽视的显着差异。

由于这些需求,我想实现一个功能齐全的 gzip 中间件,但存在几个技术困惑。第一个是我想控制中间件是否压缩,显然,如果响应没有命中缓存而是动态生成的,那么它应该被压缩,但相反,它不应该再次压缩,因为它已经已经被压缩过一次了。第二个问题是,即使我可以控制中间件不压缩,当不需要运行压缩逻辑时,如何将其结果替换为已经压缩的字节?

由于我还不知道如何实现,请原谅我只能提供一些伪代码来说明我的想法。

下面的代码描述了一个相对复杂的不包含中间件的响应地址:

from fastapi import FastAPI, Request
import uvicorn

app = FastAPI()

@app.post("/api")
async def root(request :Request, some_args: int):

    # Using the Fibonacci series to simulate a time-consuming 
    # computational operation
    def fib_recur(n):
    if n <= 1: return n
    return fib_recur(n-1) + fib_recur(n-2)
    
    # In addition to the time-consuming calculation, the size of 
    # the returned content is …
Run Code Online (Sandbox Code Playgroud)

python python-asyncio starlette fastapi

5
推荐指数
0
解决办法
2652
查看次数

Asyncio 未并行运行 Aiohttp 请求

我想使用 python 并行运行许多 HTTP 请求。我尝试使用 asyncio 这个名为 aiohttp 的模块。

import aiohttp
import asyncio

async def main():
    async with aiohttp.ClientSession() as session:
        for i in range(10):
            async with session.get('https://httpbin.org/get') as response:
                html = await response.text()
                print('done' + str(i))

loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Run Code Online (Sandbox Code Playgroud)

我期望它并行执行所有请求,但它们是一一执行的。虽然,我后来使用线程解决了这个问题,但我想知道这有什么问题吗?

python python-asyncio aiohttp

5
推荐指数
1
解决办法
3641
查看次数

asyncio call_soon 和 call_soon_threadsafe 之间的区别?为什么 call_soon 线程不安全?

最近读了 asyncio 的代码,对loop.call_soon和loop.call_soon_threadsafe感到困惑。我发现的唯一区别是call_soon_threadsafe中有一个_write_to_self。众所周知,事件循环在特定线程中运行。事件循环安排的所有任务都按顺序进行。我们可以假设任务在被一一调度时是线程安全的。那么call_soon中怎么会出现线程不安全的情况呢?

python thread-safety python-asyncio

5
推荐指数
1
解决办法
2110
查看次数

如何使用高级 asyncio API 干净地终止子进程?

这是我尝试使用高级 asyncio 子进程 API过早终止子进程的程序:

import asyncio

async def test():
    process = await asyncio.create_subprocess_shell(
        "sleep 2 && echo done",
        stdout=asyncio.subprocess.PIPE,
    )
    await asyncio.sleep(1)
    process.kill()
    await process.wait()
    # process._transport.close()

asyncio.run(test())
Run Code Online (Sandbox Code Playgroud)

退出时,它将以下内容写入stderr:

$ python3.9 test.py

Exception ignored in: <function BaseSubprocessTransport.__del__ at 0x1065f0dc0>
Traceback (most recent call last):
  File "/usr/local/Cellar/python@3.9/3.9.2_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/base_subprocess.py", line 126, in __del__
    self.close()
  File "/usr/local/Cellar/python@3.9/3.9.2_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/base_subprocess.py", line 104, in close
    proto.pipe.close()
  File "/usr/local/Cellar/python@3.9/3.9.2_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/unix_events.py", line 536, in close
    self._close(None)
  File "/usr/local/Cellar/python@3.9/3.9.2_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/unix_events.py", line 560, in _close
    self._loop.call_soon(self._call_connection_lost, exc)
  File "/usr/local/Cellar/python@3.9/3.9.2_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/base_events.py", line 746, …
Run Code Online (Sandbox Code Playgroud)

python subprocess kill python-3.x python-asyncio

5
推荐指数
0
解决办法
778
查看次数

如何并发多次运行相同的异步函数?

设置:

所以我有一个由客户端对象组成的客户端数组。还有两个异步函数,一个用于处理来自 WebSocket 的传入消息,另一个用于使用给定的客户端对象执行一些 API 调用。

当某个消息从 WebSocket 连接到达时,function_a应该检测到它并在client 数组中启动for 循环。对于客户端数组中的每个客户端对象,我想触发function_b。但function_b应该为每个客户端并发运行。

示例代码:

clients = {}

async def function_a(ws_message): # this is being called each time when a new message arrives
    if ws_message == 'signal_message':
        for client in clients:
             await function_b(client)

async def function_b(client):
    # get client parameters
    # make an API request with those parameters
Run Code Online (Sandbox Code Playgroud)

更新|| 使用asyncio.gather()编写代码 …

python concurrency python-asyncio

5
推荐指数
1
解决办法
4958
查看次数

如何使装饰器与异步函数一起工作?

通用问题:

我有一个装饰器@some_package.decorator期望函数包装为返回类型T。

### runs
@some_package.decorator
def my_func(param) -> int:
    return len(param)

### I also want to be able to wrap an async function
@some_package.decorator
async def my_func(param):
    await asyncio.sleep(1)
    return len(param)
Run Code Online (Sandbox Code Playgroud)

然而,该函数可以是syncor async,因此可能返回coroutine[Any]。

我怎样才能使装饰器await具有功能

我的具体用例:我有一些代码在以下情况下运行良好sync=True

import uplink
class MyAPI(Consumer):
        def __init__(
        self,
        password: str,
        sync: bool = True,
        auto_auth: bool = True,
        **kwargs
    ):
        self.sync = sync
        self.client = None

        if not self.sync:
            self.client = AiohttpClient()

        # initialise …
Run Code Online (Sandbox Code Playgroud)

python api async-await python-asyncio

5
推荐指数
0
解决办法
2506
查看次数

Python aiohttp ClientSession 请求内存泄漏?

我相信在使用 aiohttp ClientSession 请求时,我在长期存在的应用程序中发现了内存泄漏。如果按顺序等待每个发出请求的协程,那么一切似乎都很好。然而,并发运行时似乎存在请求上下文管理器对象的泄漏。

请考虑以下示例代码:

import logging
import tracemalloc
import asyncio
import aiohttp


async def log_allocations_coro():

    while True:
        await asyncio.sleep(120)
        snapshot = tracemalloc.take_snapshot()
        top_stats = snapshot.statistics('lineno')
        str_list = [str(x) for x in top_stats[:5]]
        logging.info("\n".join(str_list))


async def do_request():

    try:
        async with session.request("GET", "http://192.168.1.1") as response:
            text = await response.text()
    except:
        logging.exception("Request failed")


async def main():

    tracemalloc.start()
    asyncio.ensure_future(log_allocations_coro())
    
    timeout = aiohttp.ClientTimeout(total=1)
    global session
    session = aiohttp.ClientSession(timeout=timeout)

    while True:

        tasks = [ do_request(), do_request() ]
        await asyncio.gather(*tasks)
        await asyncio.sleep(2)


if __name__ == '__main__': …
Run Code Online (Sandbox Code Playgroud)

python python-asyncio aiohttp

5
推荐指数
0
解决办法
1331
查看次数