https://juejin.cn/post/7208708762265616421

问题背景:

​ 在我们的业务中,有一些推荐的场景会需要走到集团研究院的算法推荐服务,对一些用户进行个性化的课件推荐或者作者推荐,这个业务场景已经很久了,但是一直有一个很难解决的问题困扰着我们,就是我们调用研究院的接口经常性的出现偶发性的接口超时,频率还比较高。

初步排查过程:

一开始以为是研究院的推荐服务有性能问题,因为不了解推荐服务的实现逻辑,觉得算法推荐要基于大量的数据进行计算再生成结果,会不会是推荐服务里面超时了,但是和研究院的同事沟通后,他们的服务整体上的耗时有1秒的,但是一整天只有197个,完全对不上。

最后找运维确定了下整个网络链路,如下,感谢公司运维大佬的指点和分析。

 因为业务侧的服务都在公有云上,然后算法侧基于软硬件成本等原因,整体的服务都在内网的服务器上,中间通过公有网络访问,中间也没有拉专线,所以整体的网络耗时非常的不稳定,另外由于公有云现在都在杭州,内网的服务都在广州,因为跨地域的问题,本身的网络耗时也是会有一定的影响。

初步解决方案:

因此和运维大佬讨论后有两种解决方案:

  1. 公有云和私有云之间拉一条专线,解决网络不稳定的问题
  2. 推荐服务上云,不存在跨云调用。

针对两种解决方案,我们做了如下的沟通和讨论:

  1. 拉专线的好处就是网络耗时会稳定很多,但是缺点就是成本会比较大,如果只是解决这个问题,就要一条专线会有些浪费,其次就是专线只能只能解决稳定性,但是跨地域的问题还是依旧存在

  2. 方案2和研究院的同事沟通后,如果整体上云,不太现实,一则是他们的改动太大,另外就是对我们公司来说云上的进行算法计算的成本相对于内网会高很多。

在这样的背景下,原本是想计划申请一条专线解决的,但是再次和研究院的同事深入沟通后,发现其实整体的算法推荐逻辑是 "预先的离线计算推荐结果 + 实时结果查询",也就是存算分离的,也就是我们实时查询时只是查询结果,并不进行计算,而且他们的结果都是存在redis中的,并且算法离线计算每天只计算一次。

因此我们想到一个改造方案,能不能只将推荐服务上云,算法推荐的结果想办法同步到云上去就行。

redis数据上云的方案:

  1. 运维开发公有云redis的访问,内网的离线计算服务可以直接写入公有云的redis,改造非常简单,只需要离线计算和推荐服务修改redis地址即可。
  2. 开发redis数据同步服务,通过一层带鉴权和网关限制的http接口开放内网的redis有限的(固定key格式)数据访问接口,将数据同步到公有云的redis

方案1的话需要开发公有云的redis访问,和运维同学沟通过后,基于安全性和公司内部规则的约定(云上的redis禁止外网开发),方案1放弃。方案二我们分析过,因为离线计算一次后,数据当天内不在进行变更,同时数据量很小只有几十MB,因此我们选择了做一个简单的数据同步方案进行,整体的成本也很低。若下图所示

初步优化效果:

我们将用户的请求从一个要经过公网的链路,转换为内网的redis查询,不再有网络的不稳定和延时的影响,整体的性能有了大幅改善。

平均耗时基本已经在0.00Xms级别返回了,就真的是纯redis的耗时。

再次排查过程:

问题现象:

但是不管是我们的告警,还是再一步分析发现还是会出现很多超过一秒的超时请求,特别是在早上高峰期的时候,虽然平均耗时很低,整体的超时请求数量不多,但是还是会有影响,也不科学,因为理论上都走了内网,该接口的操作也仅仅是查询ES, 不应该有这么多的超时,其次也怀疑过是服务器负载的问题,也尝试了扩容服务器节点,发现效果不佳。

排查思路1:

因为请求推荐服务会需要过一层nginx,会不会是nginx 有问题导致的请求延迟,所以我们需要验证下,我们绕过nginx,直接访问本机来测试,本机会不会出现也会有延迟超过一秒的情况, 在本机通过ab压测和抓包发现,确实有超过0.5S,甚至是1s的请求出现

那就是证明了确实代码本身确实存在问题,在压测的情况下,确实会出现超时的情况,这个也就能够说明,为什么在早上高峰期的时候才会出现。

排查思路2:

通过上面的测试和分析,我们现在就到了怎么排查出代码本身存在的问题,因为研究院算法工程部用的技术栈都是python,推荐服务也是python写的,本人是java技术栈的,本身对python不慎了解,初步看了下代码,发现也是仅仅是redis的查询处理,而且也用到了python的redis连接池(专门去研究了下python中的 StrictRedis, 发现 StrictRedis 如果不用连接池,其实默认也出创建连接池的)

这块就有点诡异了,想不明白,为什么简简单单的redis查询,会有出现超时的情况,有些黔驴技穷了,这个时候没办法了,只能用上绝招火焰图了,不管是java还是python,都是有堆栈了,我们都是可以通过火焰图去抓取下火焰图,看看火焰图上有没有什么线索。

尝试学习了下python火焰图的抓取工具,没想到python还有自带的火焰图的工具 py-spy,采用了 py-spy record -o profile.svg --p pid,非常方便,没想到抓取出来还真发现了线索。

问题点

  1. 从火焰图上看,redis建连占用的cpu非常多
  2. 日志的打印占用的cpu很多

问题2 比较好解决,我们只要把打印最多的日志找到禁止打印就行,但是问题1就很奇怪了,为什么用到了连接池,但是在火焰图上看还是在依旧创建连接。我们抓包验证下,果然发现每次请求访问redis都在新建连接。

那就是代码本身肯定有问题了重新查看代码, 从代码逻辑上看,StrictRedis是在__ init __方法中初始化的, 连接池是没有问题的,本地单元测试过,那么如果有问题的话那就是 init 方法每次都执行,简单验证debug下竟然果然如此。

再来仔细分析了下 __ init __ 方法,发现这个方法并不是全局的,而是对每个请求初始化了,因此其实每次请求就要都执行 init方法,然后我们 init方法中又进行了redis的连接池初始化操作,那就很能说的通为什么每次请求都是新建redis 连接了

修改方案:

修改的方式也就很简单了,只需要将代码中的redis连接池的初始化换成全局的就行,如下

优化后的结果从抓包和连接数上看也是符合预期了

二次优化效果:

整体的耗时相比较改善前有一些下降,虽然是0.00Xs,但是超时的请求再也没有出现过

总结:

这次的优化经历整体上不算太难,但是因为整个优化涉及到里多业务的整体方案设计,不熟悉的技术栈的问题排查,所以觉得有一定的分享价值,另外个人觉得性能优化是一个系列性的问题,需要考验的点有很多,有整体的方案,有细节的分析,有工具的使用,但是更多的是对每个小点都去深究,不放过,死磕到底的态度,慢慢的会的会越来越多,经验也会越来越深!!!

[转帖]一次python服务的性能优化经历的更多相关文章

  1. Java服务端性能优化

    <Java程序性能优化>说性能优化包含五个层次:设计调优.代码调优.JVM调优.数据库调优.操作系统调优. 常用的几个代码优化方案: 使用单例 对于IO处理.数据库连接.配置文件解析加载等 ...

  2. [daily][optimize] 一个小python程序的性能优化 (python类型转换函数引申的性能优化)

    前天,20161012,到望京面试.第四个职位,终于进了二面.好么,结果人力安排完了面试时间竟然没有通知我,也没有收到短信邀请.如果没有短信邀请门口的保安大哥是不让我进去大厦的.然后,我在11号接到了 ...

  3. python可变交换性能优化

    离许多新的压力python性能优化见交换两个变量值可以使用 a,b = b,a 这样能够提高性能 >>> from timeit import Timer >>> ...

  4. 大规模服务网格性能优化 | Aeraki xDS 按需加载

    作者 钟华,腾讯云专家工程师,Istio project member.contributor,专注于容器和服务网格,在容器化和服务网格生产落地方面具有丰富经验,目前负责 Tencent Cloud ...

  5. 人人都能掌握的Java服务端性能优化方案

    作为一个Java后端开发,我们写出的大部分代码都决定着用户的使用体验.如果我们的后端代码性能不好,那么用户在访问我们的网站时就要浪费一些时间等待服务器的响应.这就可能导致用户投诉甚至用户的流失. 关于 ...

  6. 一次EF批量插入多表数据的性能优化经历

    距离上次的博客已经有15个多月了,感慨有些事情还是需要坚持,一旦停下来很有可能就会停很久或者从此再也不会坚持.但我个人一直还坚持认为属于技术狂热份子,且喜欢精益求精的那种.最近遇到两个和数据迁移相关的 ...

  7. Python 3.9 性能优化:更快的 list()、dict() 和 range() 等内置类型

    Python 的 3.9.0 版本正在开发中,计划在 2020-10-05 发布 final 版本. 官方在 changelog 中披露了很多细节,其中有一项"vectorcall" ...

  8. python django ORM 性能优化 select_related & prefetch_related

    q = models.UserInfo.objects.all() select * from userinfo select * from userinfo inner join usertype ...

  9. Python代码性能优化技巧

    摘要:代码优化能够让程序运行更快,可以提高程序的执行效率等,对于一名软件开发人员来说,如何优化代码,从哪里入手进行优化?这些都是他们十分关心的问题.本文着重讲了如何优化Python代码,看完一定会让你 ...

  10. Python 代码性能优化技巧(转)

    原文:Python 代码性能优化技巧 Python 代码优化常见技巧 代码优化能够让程序运行更快,它是在不改变程序运行结果的情况下使得程序的运行效率更高,根据 80/20 原则,实现程序的重构.优化. ...

随机推荐

  1. Shell:Lite OS在线调试工具知多少

    摘要:Shell作为Huawei Liteos在线调试工具,可以通过串口工具输入输出,支持常用的基本调试功能.同时用户可以新增定制的命令,新增命令需重新编译烧录后才能执行 本文分享自华为云社区< ...

  2. WebKit网页布局实现(0):基本概念及标准篇

    作为一个广受好评的浏览器引擎,其网页布局的质量(包括速度.效率.符合标准度等)往往是其关键,那么WebKit究竟是如何布局网页上的所有元素(包括滚动条.文字.图片.按钮.下拉框等)呢?其主要数据结构及 ...

  3. 从java注解漫谈到typescript装饰器——注解与装饰器

    之前整理过<Java注解(批注)的基本原理>,在java里面,,注解(Annotation)是油盐,对于JavaScript来说,还中世纪欧洲的东方香料 装饰器和注解 装饰器和注解之前也搞 ...

  4. PPT 玩转形状

    形状 https://www.cnblogs.com/vipsoft/p/16943810.html 形状也可以非常复杂 形状的神奇功能--合并形状 编辑顶点 https://www.cnblogs. ...

  5. schedule 定时运行 Python 函数

    安装 pip install schedule 例子 每x分钟运行一次 import schedule import time def job(): print("I'm working.. ...

  6. 盛科交换机和ovs交换机建立VxLAN隧道

    环境信息 盛科交换机信息: R3# show version CentecOS Software, E580, Version 5.3.6 Copyright (C) 2004-2017 Centec ...

  7. Mysql--日期,时间相关

    一.日期,时间戳格式转换 在数据库中,时间的存储格式一般为时间戳,但这样对于使用人员不太方便,故在查询时可以先转换格式 1.1.FROM_UNIXTIME() 将时间戳转换为日期格式 > sel ...

  8. C# 内存缓存工具类 MemoryCacheUtil

    C# 内存缓存工具类 MemoryCacheUtil using System; using System.Collections.Concurrent; using System.Collectio ...

  9. 一个非常轻量级的 Web API Demo

    一个非常轻量级的 Web API Demo,代码量很少,实现了方法拦截器,token校验,异常拦截器,缓存 创建项目:如果选择Web API,项目中东西会比较多,这里选择Empty,把下面的Web A ...

  10. 【Vue CLI】手把手教你撸插件

    本文首发于 vivo互联网技术 微信公众号链接:https://mp.weixin.qq.com/s/Rl8XLUX7isjXNUmbw0-wow作者:ZhuPing 现如今 Vue 作为主流的前端框 ...