V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
mingwiki
V2EX  ›  Python

Python 使用 psutil 计算网速和显示进程,如何提高并发?

  •  
  •   mingwiki ·
    mingwiki · 2024-04-10 15:21:15 +08:00 · 3516 次点击
    这是一个创建于 379 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我刚开始学 fastapi ,写了一些简单的接口,不想搞 netdata 或者 glances 之类的复杂界面,想把服务器基本的参数服务之类的的直接显示在接口里,其中计算网速和显示进程部分严重拖累性能,qps 仅为个位数,不知道异步怎么写,有什么改进的方案,请各位大佬指导一下谢谢。

    计算网速,用了 asyncio 但是并发不高,qps 个位数。

    async def calculate_network_speed():
        initial_time = time.time()
        initial_bytes_sent = psutil.net_io_counters().bytes_sent
        initial_bytes_recv = psutil.net_io_counters().bytes_recv
        await asyncio.sleep(1)
        current_bytes_sent = psutil.net_io_counters().bytes_sent
        current_bytes_recv = psutil.net_io_counters().bytes_recv
        elapsed_time = time.time() - initial_time
        download_speed = (current_bytes_recv - initial_bytes_recv) / elapsed_time
        upload_speed = (current_bytes_sent - initial_bytes_sent) / elapsed_time
        return download_speed, upload_speed
    

    显示进程,用的同步,写了个装饰器按时间 cache 结果,qps50 左右也不高。

    @time_cache(5)
    def get_top_processes(slice: int = 10):
        processes = [
            (
                proc.info["pid"],
                proc.info["name"],
                proc.info["cpu_percent"],
                proc.info["memory_percent"],
                " ".join(proc.info["cmdline"]),
            )
            for proc in psutil.process_iter(
                ["pid", "name", "cpu_percent", "memory_percent", "cmdline"]
            )
            if proc.info["cpu_percent"] > 0 or proc.info["memory_percent"] > 0
        ]
        top_cpu = sorted(processes, key=lambda x: x[2], reverse=True)[:slice]
        top_mem = sorted(processes, key=lambda x: x[3], reverse=True)[:slice]
        return top_cpu, top_mem
    
    def time_cache(max_age=10, maxsize=128, typed=False):
        def decorator(fn):
            @lru_cache(maxsize=maxsize, typed=typed)
            def _new(*args, __time_salt, **kwargs):
                return fn(*args, **kwargs)
    
            @wraps(fn)
            def wrapped(*args, **kwargs):
                return _new(*args, **kwargs, __time_salt=int(time.time() / max_age))
    
            return wrapped
    
        return decorator
    

    我的网站显示如下:https://api.naizi.fun/status 安装浏览器插件自动格式化一下就行了。请大佬说说 python 异步咋写,有没有好的参考?

    24 条回复    2024-04-12 20:24:01 +08:00
    BBBPineapple
        1
    BBBPineapple  
       2024-04-10 15:43:25 +08:00
    可以试试将内容作为全局变量,单独用一个线程去更新这个变量。接口直接返回全局变量的内容即可
    mingwiki
        2
    mingwiki  
    OP
       2024-04-10 15:48:50 +08:00
    @BBBPineapple #1 我一开始就是这么想的,用全局变量或者 redis mq 之类的存着,单独跑个 schedule 每秒更新,但是感觉怪怪的,我觉得是自己水平不行没办法写的更优雅。不知道 asyncio 是不是这么用的,为啥 qps 那么低我没想明白。
    shinession
        3
    shinession  
       2024-04-10 15:50:27 +08:00
    OP 这个域名好强大
    mingwiki
        4
    mingwiki  
    OP
       2024-04-10 15:58:38 +08:00
    @shinession #3 一般吧 我挂了 1000 块都没人要
    keakon
        5
    keakon  
       2024-04-10 16:34:10 +08:00
    1. psutil.net_io_counters() 可以保存成一个变量,同时拿 bytes_sent 和 bytes_recv
    2. 并发的请求在没有获得响应前,不会被缓存,因此都会进入 await asyncio.sleep(1)
    3. psutil.process_iter 需要遍历 /proc 来获取所有 pid ,这里存在磁盘 IO 和系统调用,没有异步执行,但是要改的话会很麻烦,我一般都不用 psutil:
    import asyncio
    import aiofiles.os

    async def list_dir(path):
    ----print(await aiofiles.os.listdir(path))

    asyncio.run(list_dir('.'))
    sohusi
        6
    sohusi  
       2024-04-10 16:45:47 +08:00
    单独用一个异步死循环计算,接口直接取数据。循环内部用 asyncio.sleep 等待,就不用怕阻塞事件循环,甚至不需要锁来同步
    rrfeng
        7
    rrfeng  
       2024-04-10 16:48:04 +08:00
    进程多的情况下 ps 命令也很慢。psutil 应该也差不多,都是读 /proc 计算。
    zhuisui
        8
    zhuisui  
       2024-04-10 16:50:19 +08:00
    渲染和计算分离
    mingwiki
        9
    mingwiki  
    OP
       2024-04-10 16:52:09 +08:00
    @keakon #5 感谢大佬,请问第二条异步怎么改呢有没有参考代码?第三条意思好像是需要自行写一个异步版 psutil 我先研究研究
    Hopetree
        10
    Hopetree  
       2024-04-10 16:55:19 +08:00
    你这还是在 Linux 上面运行,如果你换成 Windows 会更低,更慢。我们之前采集服务用进程采集就遇到过类似的情况,特别是 Windows 上面有时候直接卡死,因为要过滤掉一些系统进程,有两个建议:1.使用黑名单过滤一些系统进程,2.只显示自己要的字段,可参考下面我之前进行服务采集的片段


    ![]( https://tendcode.com/cdn/2024/04/202404101654151.png)
    mingwiki
        11
    mingwiki  
    OP
       2024-04-10 16:59:28 +08:00
    @Hopetree #10 感谢大佬
    Pters
        12
    Pters  
       2024-04-10 18:18:21 +08:00
    可以参考 moviepilot 项目,他就是 fastapi 写的有网速,进程等
    noahlias
        13
    noahlias  
       2024-04-10 18:45:10 +08:00
    @Pters 搜了一下并没有机器的网络和 io 有个进程
    https://github.com/search?q=repo%3Ajxxghp%2FMoviePilot%20psutil.&type=code
    LeeReamond
        14
    LeeReamond  
       2024-04-10 18:46:04 +08:00
    psutil.net_io_counters 有没有系统调用或者 IO 行为,它是同步的还是异步的,你用 asyncio 和速度变快有任何关系吗?
    MoYi123
        15
    MoYi123  
       2024-04-10 18:54:47 +08:00
    cache 里不要按调用次数缓存, 改成每秒更新一次就行吧.

    还有算 topn 可以用 heap, 虽然这点应该没多大影响.
    kneo
        16
    kneo  
       2024-04-10 19:04:53 +08:00 via Android
    先看下 CPU 占用是 usr 还是 sys 。
    so1n
        17
    so1n  
       2024-04-10 19:06:29 +08:00
    asyncio.to_thread
    mingwiki
        18
    mingwiki  
    OP
       2024-04-10 20:43:39 +08:00
    @kneo #16 cpu 占用很低,几乎没啥起伏。
    Alliot
        19
    Alliot  
       2024-04-10 22:39:10 +08:00
    循环间隔固定时间去获取数据存变量里,然后请求直接读变量返回。
    kneo
        20
    kneo  
       2024-04-10 22:40:44 +08:00
    @mingwiki 注意看下系统调用的时间。top 里的 sy 列。或者 time 执行结果。
    mingwiki
        21
    mingwiki  
    OP
       2024-04-10 22:48:26 +08:00
    @kneo #20 只有 python 和 uvicorn 占用 cpu ,没有磁盘读写,没有其他的系统进程占用
    kneo
        22
    kneo  
       2024-04-10 23:20:32 +08:00
    @mingwiki 我说的是“系统调用”。
    GeruzoniAnsasu
        23
    GeruzoniAnsasu  
       2024-04-11 01:34:47 +08:00
    @mingwiki #1 的方向就是对的。 取样是个很慢的操作。 你需要单独一个允许 block 的 routine 来处理取样周期。

    而且在 linux 里大部分的「系统信息」相关的实现最后都会退化成读 procfs / sysfs 之类的,大家都不喜欢自找麻烦去调系统 API 再自己去转换那些结构/枚举/字符串…… 所以

    不管什么实现都一样慢。你能做的只有把取样和查询隔离开。
    julyclyde
        24
    julyclyde  
       2024-04-12 20:24:01 +08:00
    这事就提高不了
    因为有几个参数需要多次取样然后相减
    间隔时间不能太短
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3022 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 13:25 · PVG 21:25 · LAX 06:25 · JFK 09:25
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.