记一次 Python Web 接口优化,性能提升25倍!

RocketMQ 5.3.1 集成 Spring Boot 3.4.3 就这么简单 RocketMQ 是一个分布式消息中间件,支持发布/订阅模型,具有高吞吐量、高可用性和事务消息等特性。它广泛应用于日志处理、订单系统和异步任务等场景。Spring Boot 3.4.3 集成 RocketMQ 5.3.1 的过程异常简单,仅需少量配置即可实现消息的生产和消费。本文从环境搭建到代码实现,覆盖了核心步骤。 阅读详情

来源:Lin_R   出处:

https://segmentfault.com/a/1190000020956724

背景

我们负责的一个业务平台,有次在发现设置页面的加载特别特别地慢,简直就是令人发指

让用户等待 36s 肯定是不可能的,于是我们就要开启优化之旅了。

投石问路

既然是网站的响应问题,可以通过 Chrome 这个强大的工具帮助我们快速找到优化方向。

通过 Chrome 的 Network 除了可以看到接口请求耗时之外,还能看到一个时间的分配情况,选择一个配置没有那么多的项目,简单请求看看:

虽然只是一个只有三条记录的项目,加载项目设置都需要 17s,通过 Timing, 可以看到总的请求共耗时 17.67s ,但有 17.57s 是在 Waiting(TTFB) 状态。

TTFB 是 Time to First Byte 的缩写,指的是浏览器开始收到服务器响应数据的时间(后台处理时间+重定向时间),是反映服务端响应速度的重要指标。


Profile 火焰图 + 代码调优

那么大概可以知道优化的大方向是在后端接口处理上面,后端代码是 Python + Flask 实现的,先不盲猜,直接上 Profile:

第一波优化:功能交互重新设计

说实话看到这段代码是绝望的:完全看不出什么?只是看到很多 gevent 和 Threading,因为太多协程或者线程?

这时候一定要结合代码来分析(为了简短篇幅,参数部分用 “...” 代替):

def get_max_cpus(project_code, gids):
    """
    """
    ...
    # 再定义一个获取 cpu 的函数
    def get_max_cpu(project_setting, gid, token, headers):
        group_with_machines = utils.get_groups(...)
        hostnames = get_info_from_machines_info(...)
        res = fetchers.MonitorAPIFetcher.get(...)
        vals = [
            round(100 - val, 4)
            for ts, val in res['series'][0]['data']
            if not utils.is_nan(val)
        ]
        max_val = max(vals) if vals else float('nan')
        max_cpus[gid] = max_val
       
    #  启动线程批量请求
    for gid in gids:
        t = Thread(target=get_max_cpu, args=(...))
        threads.append(t)
        t.start()
        
    # 回收线程
    for t in threads:
        t.join()

    return max_cpus

通过代码可以看到,为了更加快速获取 gids 所有的 cpu_max 数据,为每个 gid 分配一个线程去请求,最终再返回最大值。

这里会出现两个问题:

  1. 在一个 web api 做线程的 创建 和 销毁 是有很大成本的,因为接口会频繁被触发,线程的操作也会频繁发生,应该尽可能使用线程池之类的,降低系统花销;

  2. 该请求是加载某个 gid (群组) 下面的机器过去 7 天的 CPU 最大值,可以简单拍脑袋想下,这个值不是实时值也不是一个均值,而是一个最大值,很多时候可能并没有想象中那么大价值;

既然知道问题,那就有针对性的方案:

  1. 调整功能设计,不再默认加载 CPU 最大值,换成用户点击加载(一来降低并发的可能,二来不会影响整体);

  2. 因为 1 的调整,去掉多线程实现;

再看第一波优化后的火焰图:

这次看的火焰图虽然还有很大的优化空间,但起码看起来有点正常的样子了。

第二波优化:Mysql 操作优化处理

我们再从页面标记处(接口逻辑处)放大火焰图观察:

看到好大一片操作都是由 utils.py:get_group_profile_settings 这个函数引起的数据库操作热点。

同理,也是需要通过代码分析:

def get_group_profile_settings(project_code, gids):
    
    # 获取 Mysql ORM 操作对象
    ProfileSetting = unpurview(sandman.endpoint_class('profile_settings'))
    session = get_postman_session()
    
    profile_settings = {}
    for gid in gids:
        compound_name = project_code + ':' + gid
        result = session.query(ProfileSetting).filter(
            ProfileSetting.name == compound_name
        ).first()
        
        if result:
            result = result.as_dict()
            tag_indexes = result.get('tag_indexes')
            profile_settings[gid] = {
                'tag_indexes': tag_indexes,
                'interval': result['interval'],
                'status': result['status'],
                'profile_machines': result['profile_machines'],
                'thread_settings': result['thread_settings']
            }
            ...(省略)
    return profile_settings

看到 Mysql ,第一个反应就是 索引问题,所以优先去看看数据库的索引情况,如果有索引的话应该不会是瓶颈:

很奇怪这里明明已经有了索引了,为什么速度还是这个鬼样子呢!

正当毫无头绪的时候,突然想起在 第一波优化 的时候, 发现 gid(群组)越多的影响越明显,然后看回上面的代码,看到那句:

for gid in gids: 
    ...

我仿佛明白了什么。

这里是每个 gid 都去查询一次数据库,而项目经常有 20 ~ 50+ 个群组,那肯定直接爆炸了。

其实 Mysql 是支持单字段多值的查询,而且每条记录并没有太多的数据,我可以尝试下用 Mysql 的 OR 语法,除了避免多次网络请求,还能避开那该死的 for

正当我想事不宜迟直接搞起的时候,余光瞥见在刚才的代码还有一个地方可以优化,那就是:

看到这里,熟悉的朋友大概会明白是怎么回事。

GetAttr 这个方法是Python 获取对象的 方法/属性 时候会用到,虽然不可不用,但是如果在使用太过频繁也会有一定的性能损耗。

结合代码一起来看:

def get_group_profile_settings(project_code, gids):
    
    # 获取 Mysql ORM 操作对象
    ProfileSetting = unpurview(sandman.endpoint_class('profile_settings'))
    session = get_postman_session()
    
    profile_settings = {}
    for gid in gids:
        compound_name = project_code + ':' + gid
        result = session.query(ProfileSetting).filter(
            ProfileSetting.name == compound_name
        ).first()
        ...

在这个遍历很多次的 for 里面,session.query(ProfileSetting) 被反复无效执行了,然后 filter 这个属性方法也被频繁读取和执行,所以这里也可以被优化。

总结下的问题就是:

1. 数据库的查询没有批量查询;
2. ORM 的对象太多重复的生成,导致性能损耗;
3. 属性读取后没有复用,导致在遍历次数较大的循环体内频繁 getAttr,成本被放大;

那么对症下药就是:

def get_group_profile_settings(project_code, gids):
    
    # 获取 Mysql ORM 操作对象
    ProfileSetting = unpurview(sandman.endpoint_class('profile_settings'))
    session = get_postman_session()
    
    
    # 批量查询 并将 filter 提到循环之外
    query_results = query_instance.filter(
        ProfileSetting.name.in_(project_code + ':' + gid for gid in gids)
    ).all()

    # 对全部的查询结果再单条处理
    profile_settings = {}
    for result in query_results:
        if not result:
            continue
        result = result.as_dict()
        gid = result['name'].split(':')[1]
        tag_indexes = result.get('tag_indexes')
        profile_settings[gid] = {
            'tag_indexes': tag_indexes,
            'interval': result['interval'],
            'status': result['status'],
            'profile_machines': result['profile_machines'],
            'thread_settings': result['thread_settings']
        }

            ...(省略)
    return profile_settings

优化后的火焰图:

对比下优化前的相同位置的火焰图:

明显的优化点:优化前的,最底部的 utils.py:get_group_profile_settings 和 数据库相关的热点大大缩减。

优化效果

同一个项目的接口的响应时长从 37.6 s 优化成 1.47s,具体的截图:

优化总结

如同一句名言:

如果一个数据结构足够优秀,那么它是不需要多好的算法。

在优化功能的时候,最快的优化就是:去掉那个功能!

其次快就是调整那个功能触发的 频率 或者 复杂度!

从上到下,从用户使用场景去考虑这个功能优化方式,往往会带来更加简单高效的结果,嘿嘿!

当然很多时候我们是无法那么幸运的,如果我们实在无法去掉或者调整,那么就发挥做程序猿的价值咯:Profile

针对 Python 可以尝试:cProflile + gprof2dot

而针对 Go 可以使用: pprof + go-torch

很多时候看到的代码问题都不一定是真正的性能瓶颈,需要结合工具来客观分析,这样才能有效直击痛点!

其实这个 1.47s,其实还不是最好的结果,还可以有更多优化的空间,比如:

  1. 前端渲染和呈现的方式,因为整个表格是有很多数据组装后再呈现的,响应慢的单元格可以默认先显示 菊花,数据返回再更新;

  2. 火焰图看到还有挺多细节可以优化,可以替换请求数据的外部接口,比如再优化彻底 GetAttr 相关的逻辑;

  3. 更极端就是直接 Python 转 GO;

但是这些优化已经不是那么迫切了,因为这个 1.47s 是比较大型项目的优化结果了,绝大部分的项目其实不到 1s 就能返回

再优化可能付出更大成本,而结果可能也只是从 500ms 到 400ms 而已,结果并不那么高性价比。

所以我们一定要时刻清晰自己优化的目标,时刻考虑 投入产出比,在有限的时间做出比较高的价值(如果有空闲时间当然可以尽情干到底)

(完)

测风数据垂直外推(原理及Windowgrapher操作示范) 垂直外推 阅读详情

相关推荐

GD-Link离线烧录全攻略:从配置到一键下载的保姆级教程

本文详细介绍了GD-Link离线烧录的全流程,从硬件准备到软件配置,再到一键下载的实战技巧。通过保姆级教程,帮助工程师快速掌握国产MCU的离线烧录技术,适用于产线批量烧录和现场设备维护等场景,显著提升工作效率。

ww90123的博客 652

后端接口重定向_关于视频流媒体服务器搭建后端302重定向的两种方式介绍

JAVA后端重定向用spring mvc 一般为return “redirect:/url” ,原生重定向为response.sendRedirect(“url”), 有时候spring mvc在重定向的时候会生成一个SESSIONID在浏览器的地址栏以url重写的方式出现,一般为JSESSIONID=***的格式,这时可以用原生重定向response.sendRedirect(“url”)避免这...

weixin_30128191的博客 1091

最新测算源码PHP算命源码-风水八字-八字起名-塔罗牌占卜

未来,随着技术的发展和用户需求的变化,我们可以继续对系统进行升级和优化,以满足更多用户的需求和期望。数据库设计:创建一个名为bazi的数据库表,包含以下字段:id(自增ID)、name(姓名)、sex(性别)、birth_date(出生日期)、birth_time(出生时间)、bazi(八字结果)。数据库设计:创建一个包含塔罗牌信息的数据库表,如tarot_cards,包含字段:id(塔罗牌ID)、name(塔罗牌名称)、meaning(塔罗牌含义)等。根据操作系统的不同,PHP的安装方法会有所不同。

shanbeizi的博客 2418

JAVA后端重定向

备注:只供自己学习参考使用。

lw_kai的博客 2359

RV1106的ISP配置实战:MIPI与LVDS接口的硬件连接与设备树优化

本文详细解析了RV1106芯片的ISP配置实战,重点对比了MIPI CSI-2与LVDS两种主流接口的硬件连接方案与数据流差异。通过手把手的设备树代码示例,深入讲解了单路MIPI与双路LVDS摄像头的配置要点、关键属性设置及常见避坑指南,并提供了从驱动加载检查到Media Controller工具使用的系统化调试方法,帮助开发者快速打通图像采集与处理全链路。

terraform7cloud的博客 471

【干货】SAP特殊库存:Q库存(项目库存)全解析,搞懂ETO模式的核心!

在 SAP 系统中,

我小时候很黑的博客 910

接口自动化面试题,建议看完收藏!

1、get和post的区别? l http是上层请求协议,主要定义了服务端和客户端的交互规格,底层都是tcp/ip协议 l Get会把参数附在url之后,用?分割,&连接不同参数,Get获取资源,post会把参数放置在http请求体中,Post发送请求数据 l Get产生一个tcp数据包,post产生两个tcp数据包 l Get请求,浏览器会把http header和data一起发送,服务器返回200响应码 l Psot请求,浏览器先发送header,服务器响应100(continue),然后再发送

公众号:【伤心的辣条】 1813

深度学习助力台风路径预测:技术剖析与实战应用全攻略(基于LSTM模型)

摘要:深度学习技术在台风路径预测领域展现出巨大潜力。通过融合多源气象数据,深度学习模型如LSTM能有效处理时间序列数据,捕捉台风路径变化规律。与传统方法相比,深度学习模型在自动特征学习和处理复杂模式方面具有独特优势,提高了预测的准确性和实时性。然而,挑战依然存在,包括数据质量问题、模型可解释性不足以及极端台风的复杂性。未来,随着观测技术的进步和新算法的开发,深度学习将在台风路径预测中发挥更大作用,为防灾减灾提供更精准的决策支持。

专注于人工智能、软件开发、工控自动化、工厂数字化及智能化等领域,希望和大家共同进步! 2228

后端接口重定向_Java访问重定向接口

背景:开发做了一个免登陆的接口,方便我后续给管理后台做一些小工具,问题来了,给的免登陆接口是个302如图的test_login,在重定向一个200的接口(eload_admin),原本开始这样做:02这个免登陆接口时,获取登录的cookies,在把登录后的cookies给200的接口,就是正常登录成功如图:登录成功后获200页面显示的内容session就表示获取了登录态问题:但是在代码中实施遇到一...

weixin_35652574的博客 4717

学Simulink——基于Simulink的扰动观测器(DOB)补偿坡道重力分量

(质量5kg,坡度30°,目标速度1m/s)为例,基于Simulink搭建“DOB+PI”复合控制仿真平台,从系统建模、DOB算法推导、离散化实现到Simulink模块搭建,提供模块化模型、核心代码及参数整定表,帮助工程师彻底解决重力等恒值扰动引起的稳态误差问题。采用双线性变换(Tustin): s=Ts​2​z+1z−1​,其中 Ts​为控制周期。模块相加(注意符号:ufinal​=upi​−ucomp​或 upi​+ucomp​,取决于扰动定义方向,此处假设重力为正,控制力需反向抵消,故为减)。

MHD0815的博客 69

如何使用freesurfer对T1w结构像进行分区指标的提取?——皮层面积,灰质体积,平均厚度,厚度标准差,平均曲率、高斯曲率、曲率指数、折叠指数

如何使用freesurfer对T1w结构像进行分区指标的提取?——皮层面积,灰质体积,平均厚度,厚度标准差,平均曲率、高斯曲率、曲率指数、折叠指数

从事脑科学核磁共振方法学研究,在Nature communications等权威期刊发表研究论文,熟练掌握磁共振处理方法和统计学方法,欢迎大家和我交流。 3788

SpringBoot后端服务重定向

SpringBoot 中的重定向

weixin_45809780的博客 2365

(九)DFI接口时序

文章目录一、DFI Interface 一、DFI Interface   DFI接口是连接DDR Controller与DDR_PHY之间的通用接口,其信号组如下表:       

Mr.翟的博客 1万+

pythonweb接口优化_记一次 Python Web 接口优化,性能提升25倍!

原标题:记一次 Python Web 接口优化,性能提升25倍!背景我们负责的一个业务平台,有次在发现设置页面的加载特别特别地慢,简直就是令人发指 让用户等待 36s 肯定是不可能的,于是我们就要开启优化之旅了。投石问路既然是网站的响应问题,可以通过 Chrome 这个强大的工具帮助我们快速找到优化方向。通过 Chrome 的 Network 除了可以看到接口请求耗时之外,还能看到一个时间的分配情...

weixin_39985842的博客 88

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

pythonappend性能_记一次Python Web接口优化,性能提升25倍!

背景我们负责的一个业务平台,有次在发现设置页面的加载特别特别地慢,简直就是令人发指让用户等待 36s 肯定是不可能的,于是我们就要开启优化之旅了。投石问路既然是网站的响应问题,可以通过 Chrome 这个强大的工具帮助我们快速找到优化方向。通过 Chrome 的 Network 除了可以看到接口请求耗时之外,还能看到一个时间的分配情况,选择一个配置没有那么多的项目,简单请求看看:虽然只是一个只有三...

weixin_39574928的博客 364

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

pythonweb接口优化_记一次Python Web接口优化,性能提升25倍!

背景我们负责的一个业务平台,有次在发现设置页面的加载特别特别地慢,简直就是令人发指让用户等待 36s 肯定是不可能的,于是我们就要开启优化之旅了。投石问路既然是网站的响应问题,可以通过 Chrome 这个强大的工具帮助我们快速找到优化方向。通过 Chrome 的 Network 除了可以看到接口请求耗时之外,还能看到一个时间的分配情况,选择一个配置没有那么多的项目,简单请求看看:虽然只是一个只有三...

weixin_39886547的博客 91

后端接口重定向_记一次 Python Web 接口优化,性能提升25倍!

来源:Lin_R 出处:https://segmentfault.com/a/1190000020956724背景我们负责的一个业务平台,有次在发现设置页面的加载特别特别地慢,简直就是令人发指让用户等待 36s 肯定是不可能的,于是我们就要开启优化之旅了。投石问路既然是网站的响应问题,可以通过 Chrome 这个强大的工具帮助我们快速找到优化方向。通过 Chrome 的 Networ...

weixin_42571280的博客 197

python linux 优化_记一次Python Web接口优化,性能提升25倍!

背景我们负责的一个业务平台,有次在发现设置页面的加载特别特别地慢,简直就是令人发指让用户等待 36s 肯定是不可能的,于是我们就要开启优化之旅了。投石问路既然是网站的响应问题,可以通过 Chrome 这个强大的工具帮助我们快速找到优化方向。通过 Chrome 的 Network 除了可以看到接口请求耗时之外,还能看到一个时间的分配情况,选择一个配置没有那么多的项目,简单请求看看:虽然只是一个只有三...

weixin_39943220的博客 87

pythonweb并发性能python网站性能_记一次 Python Web 接口优化,性能提升25倍!

(点击上方快速关注并设置为星标,一起学Python)作者:Lin_R 出处:https://segmentfault.com/a/1190000020956724背景我们负责的一个业务平台,有次在发现设置页面的加载特别特别地慢,简直就是令人发指让用户等待 36s 肯定是不可能的,于是我们就要开启优化之旅了。投石问路既然是网站的响应问题,可以通过 Chrome 这个强大的工具帮助我们快速找到优化...

weixin_31742871的博客 221

code blocks代码性能分析_记一次Python Web接口优化,性能提升25倍!

背景我们负责的一个业务平台,有次在发现设置页面的加载特别特别地慢,简直就是令人发指让用户等待 36s 肯定是不可能的,于是我们就要开启优化之旅了。投石问路既然是网站的响应问题,可以通过 Chrome 这个强大的工具帮助我们快速找到优化方向。通过 Chrome 的 Network 除了可以看到接口请求耗时之外,还能看到一个时间的分配情况,选择一个配置没有那么多的项目,简单请求看看: 虽然只是一个只有...

weixin_39588911的博客 114

code blocks代码性能分析_记一次 Python Web 接口优化,性能提升25倍!

背景我们负责的一个业务平台,有次在发现设置页面的加载特别特别地慢,简直就是令人发指让用户等待 36s 肯定是不可能的,于是我们就要开启优化之旅了。投石问路既然是网站的响应问题,可以通过 Chrome 这个强大的工具帮助我们快速找到优化方向。通过 Chrome 的 Network 除了可以看到接口请求耗时之外,还能看到一个时间的分配情况,选择一个配置没有那么多的项目,简单请求看看:虽然只是一个只有三...

weixin_36468140的博客 181

pythonweb接口优化_[转]记一次 Python Web 接口优化,性能提升25倍!

weixin_39520775的博客 77
上一篇: Linux下删除大量文件效率对比
下一篇: 你可能还不知道,这5种动态炫酷图,其实也是用Python画的
Wang_AI
博客等级 码龄12年 991粉丝 162原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值