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

237 天前
 mingwiki

我刚开始学 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 异步咋写,有没有好的参考?

3267 次点击
所在节点    Python
24 条回复
BBBPineapple
237 天前
可以试试将内容作为全局变量,单独用一个线程去更新这个变量。接口直接返回全局变量的内容即可
mingwiki
237 天前
@BBBPineapple #1 我一开始就是这么想的,用全局变量或者 redis mq 之类的存着,单独跑个 schedule 每秒更新,但是感觉怪怪的,我觉得是自己水平不行没办法写的更优雅。不知道 asyncio 是不是这么用的,为啥 qps 那么低我没想明白。
shinession
237 天前
OP 这个域名好强大
mingwiki
237 天前
@shinession #3 一般吧 我挂了 1000 块都没人要
keakon
237 天前
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
237 天前
单独用一个异步死循环计算,接口直接取数据。循环内部用 asyncio.sleep 等待,就不用怕阻塞事件循环,甚至不需要锁来同步
rrfeng
237 天前
进程多的情况下 ps 命令也很慢。psutil 应该也差不多,都是读 /proc 计算。
zhuisui
237 天前
渲染和计算分离
mingwiki
237 天前
@keakon #5 感谢大佬,请问第二条异步怎么改呢有没有参考代码?第三条意思好像是需要自行写一个异步版 psutil 我先研究研究
Hopetree
237 天前
你这还是在 Linux 上面运行,如果你换成 Windows 会更低,更慢。我们之前采集服务用进程采集就遇到过类似的情况,特别是 Windows 上面有时候直接卡死,因为要过滤掉一些系统进程,有两个建议:1.使用黑名单过滤一些系统进程,2.只显示自己要的字段,可参考下面我之前进行服务采集的片段


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

还有算 topn 可以用 heap, 虽然这点应该没多大影响.
kneo
237 天前
先看下 CPU 占用是 usr 还是 sys 。
so1n
237 天前
asyncio.to_thread
mingwiki
237 天前
@kneo #16 cpu 占用很低,几乎没啥起伏。
Alliot
237 天前
循环间隔固定时间去获取数据存变量里,然后请求直接读变量返回。
kneo
237 天前
@mingwiki 注意看下系统调用的时间。top 里的 sy 列。或者 time 执行结果。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1031320

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX