记一次 Python Web 接口优化

详解Java从后台重定向(redirect)到另一个项目的方法 主要介绍了详解Java从后台重定向(redirect)到另一个项目的方法,非常具有实用价值,需要的朋友可以参考下 立即下载


作者: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 而已,结果并不那么高性价比。

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

(完)





手把手教你玩转RISC-V指令集​​​专栏--​​1.2 对比x86/ARM:RISC-V的三大核心优势(开放、灵活、可扩展)​ ​:RISC-V以“开放打破垄断、灵活适配场景、扩展定义未来”的三大优势,正在重塑计算架构格局。其核心价值不仅是技术革新,更是对全球半导体产业链话语权的重新分配。 阅读详情

相关推荐

NotImplementedError: Cannot copy out of meta tensor; no data! Please use torch.nn.Module.to_empty()

Please use torch.nn.Module.to_empty() instead of torch.nn.Module.to()输出(llama) (base) [root@localhost home]# nvidia-smi Unable to determine the device handle for GPU0000:09:00.0: Unknown Error。这是典型的“孤儿进程”,但它还是。(僵尸进程),占用 GPU 资源。如果进程仍然不消失,并且。,这意味着它的父进程是。

m0_61636632的博客 2098

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

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

weixin_30128191的博客 1094

宽带波束形成

介绍了宽带波束形成器的时域滤波结构,以及波束响应的计算公式,宽带波束形成器的波束响应可表示为权重向量和方向向量的积

... 2491

JAVA后端重定向

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

lw_kai的博客 2359

彻底卸载Ubuntu双系统,并完美回收100G空间给D盘

本文记录了安全卸载Ubuntu双系统并回收磁盘空间的全过程。作者首先通过Windows命令行彻底清除Ubuntu启动项,避免GRUB引导报错;随后在磁盘管理中删除Linux分区,将100G未分配空间合并至相邻的D盘。针对C盘空间不足问题,作者采用Dism++清理系统文件,并将用户文档、聊天记录等迁移至扩容后的D盘。最终实现了引导纯净、空间合理分配的目标,特别强调分区扩展必须遵循"相邻原则",建议通过间接迁移而非直接扩容来优化系统盘空间管理。

2403_85993278的博客 1514

Abaqus CEL分析必备:Hypermesh网格导出与inp文件合并技巧

本文详细介绍了在Abaqus CEL分析中使用Hypermesh生成六面体网格并合并多组件inp文件的完整流程。从单元类型设置(EC3D8R)、网格质量优化到inp文件合并技巧,提供了一套解决流固耦合仿真前处理痛点的实战方案,帮助工程师高效完成复杂CEL分析任务。

weixin_29279101的博客 218

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

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

SAP CO实战:如何用KKA2/CJ88结果分析码搞定项目收入成本匹配(附真实案例)

本文深入解析了SAP CO模块中结果分析码KKA2/CJ88的核心应用,通过真实案例详细阐述了如何在成本波动剧烈的销售型项目中,运用结果分析实现收入与成本的精准匹配。文章重点拆解了不同结果分析方法的适用场景与财务影响,并提供了从配置、操作到问题排查的完整实战指南,帮助财务顾问提升项目财务管理能力。

qsc9012345的博客 609

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

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

weixin_35652574的博客 4719

高通SDX35:SGMII接口PHY RTL8211FSI-CG驱动适配

高通SDX35 RTL8211F SGMII

不秃头的程序猿Gabriel的博客 788

基于STM32设计的遥控灭火消防小车_296

随着城市化进程的加速和火灾发生频率的增加,传统的消防方式在应对初期火灾时存在反应慢、效率低等问题。为了提高火灾响应速度和灭火效率,本设计开发了一款基于STM32单片机的遥控灭火消防小车。该小车配备了烟雾检测、温湿度监测、火源定位、灭火操作等功能,能够通过蓝牙和WIFI实现远程控制与实时视频传输。设计采用了MQ2烟雾传感器、SHT30温湿度传感器、火光传感器和ESP32摄像头模块等硬件设备。系统不仅能够自动检测和定位火源,还可以手动调整灭火器对准位置,进行干粉灭火。此外,蜂鸣器用于火源报警,确保操作人员及时得

726

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

来源:Lin_R 出处:https://segmentfault.com/a/1190000020956724背景我们负责的一个业务平台,有次在发现设置页面的加载特别特别地慢,简直...

Oner.wv的专栏 505

【即插即用模块】注意力篇 | MICCAI 2023 | MFE:多尺度特征提取,告别固定感受野,猛涨2个点!

本文提出一种用于低分辨率乳腺癌病理图像超分辨率与分类的网络SHISRCNet。针对现有方法处理低分辨率图像效果差、感受野固定等问题,设计了多特征提取模块(MFE),通过多速率空洞卷积覆盖全尺度感受野(局部到全局),结合递进残差连接和动态权重融合机制,实现自适应多尺度特征增强。实验表明,该模块在超分辨率任务中PSNR提升近2个点,分类准确率显著提高。代码提供详细注释,支持即插即用,适用于各类多尺度视觉任务。

主要更新底层视觉(去噪、超分等)相关的科研内容,形式为【论文精读】+【论文复现】 207

pythonweb接口优化_记一次 Python Web 接口优化

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

weixin_39965673的博客 64

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

flask 查询优化_记一次 Python Web 接口优化

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

weixin_39992665的博客 375

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

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

作者:Lin_R https://segmentfault.com/a/1190000020956724背景我们负责的一个业务平台,有次在发现设置页面的加载特别特别地慢,简直就是令人...

Python小二 667

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

后端接口重定向_记一次 Python Web 接口优化

点击上方Python知识圈,设为星标回复1024获取Python资料作者:Lin_R出处:https://segmentfault.com/a/1190000020956724阅读文本大概需要 5分钟近期精彩文章:Python100例(附PDF下载地址)背景我们负责的一个业务平台,有次在发现设置页面的加载特别特别地慢,简直就是令人发指让用户等待 36s 肯定是不可能的,于是我们就要开启...

weixin_39953629的博客 128

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

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

weixin_39792472的博客 103
上一篇: 还真就这么简单,来个例子,再解释一次 EM 算法
下一篇: Nginx 一个牛X的功能,流量拷贝!
Wang_AI
博客等级 码龄12年 997粉丝 162原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值