一个重量级HTTP api的304优化分析与突发失效问题解决
背景
最近查看nginx log排查问题时,意外中发现重量级的主页 list api 304比例已暴跌至不到1%,之前该比例长期维持在30%以上,近期也未改动过相关逻辑,跟进后最终发现是服务端本地cache混用导致的问题。
304优化原因
app每次冷启初始化时都会请求重量级的HTTP主页list api,其会拉取全量1000个item(游戏关卡)组成的list数据一次性返回,返回的响应数据经gzip压缩之后依然有将近300KB大小(解压后近3MB),单api如此大的数据传输对于网络带宽和传输速度都有明显的影响。
该api返回包括静态数据和动态数据部分,静态数据变动一般由运营在后台配置修改导致,一周变动次数有限,动态数据则和用户自身游玩行为有关,整体而言对于该api相当一部分用户多次请求得到的数据应该都是相同的,所以去年和客户端一起对该api进行了HTTP 304优化。
304对客户端请求耗时与带宽影响分析
之前测试了日本与美国东海岸之间主页list api返回304与完整300KB大小的耗时对比,两者耗时相差超过600ms--作为对比服务端处理耗时upstream_response_time一直稳定在100ms左右。
从网络协议与数据传输的角度解释这一现象,C/S 首先建立TCP连接,而后基于TCP建立HTTPS连接,服务端收到客户端完整请求并通过一系列业务逻辑生成最终返回数据-无论是否启用304到这一步为止流程都是一样的。
接下来返回数据这一步就差别大了,如果返回数据很少--如1KB,一个TCP包即可将完整响应数据发挥给客户端,日本到美国东海岸ping耗时(RTT)约为160~200ms(以下取160ms为例),网络良好的情况下客户端将在服务端发送数据包后80ms收到该响应,而如果300KB数据,则需要至少200个TCP包(根据RFC6928定义TCP滑动窗口初始值为10MSS,后续根据网络情况会动态调整),最终体现为客户端完全收到300KB响应数据需要4个乃至更多个RTT(>=640ms)的时间,也就是300KB数据传输上比1KB数据传输要多耗时至少560ms,而实际上包越多,在TCP通信过程中发生丢包、重传、拥塞控制等意外情况的概率也就越大,其受网络不稳定因素影响而耗时更长的可能也要大得多。
至于所需带宽对比则很明显接近1:300。
去年对该接口做完304支持优化后,当时的效果是超过30%的请求都可以通过304直接返回,一个简单的304优化就能使该接口使用带宽资源降低近30%,对于美国地区用户而言超过30%请求耗时减少600+ms,平均耗时降低200+ms,应该说还是相当划算的。
304比例异常暴跌
测试环境尝试复现
偶然发现线上304比例暴跌后,首先尝试在测试环境复现问题,结果测试环境居然没法复现出来,无论是浏览器、curl命令还是app直接请求,结果都显示同一用户多次请求会正常返回304,陷入沉思==!
线上环境复现
测试环境无法复现,直接尝试线上环境验证,结果发现问题必现--对于同一个用户的连续两次请求,简单粗暴将两次api请求返回的数据copy到本地比对其变化,发现1000个item中有个别item有会多余的字段,看到字段名立刻明白了问题所在:本地cache被混用了。
本地cache的引入
对于该api返回的1000个item,每个item都需要单独调用normalize函数进行一系列的处理--如配置资源地址到完整资源包格式转换、动态文案组装等,即便单个item normalize调用只需要0.1ms,*1000之后也会变成100ms,所以服务端对于每个item的normalize结果都做了一个短期的本地cache,命中缓存的情况下1000个normalize所花费的时间由100ms减少成了数ms,这个优化已经上线很长时间,直到最近才发现它可能会导致304机制的失效。
对normalize函数启用本地cache的Python代码实现大概类似如下:
class ItemModel:
local_item_cache = LocalCache(10000)
@local_item_cache.cache(60)
def normalize(self, phone_type, item_id):
# 资源包格式转换、动态文案组装等
...
return self
cache混用问题的引入
近期app新增加了一个游戏模式,服务端对于该模式下的用户每次均会从一个100+item的池子中按一定策略随机选定一个返回,该item在返回前一样会调用normalize函数,如果命中本地cache直接返回,否则执行normalize函数并将结果存入本地cache后再返回。
问题出在这里:主页list item normalize与新游戏模式下item normalize使用的是同一个本地cache,出于节约拷贝开销的考虑,本地cache命中返回的其实是一个对象引用,而新模式在normalize之后,还会针对该游戏模式新增数个额外字段、并修改部分已有字段的内容-会有部分随机数值策略,这会直接修改掉底层本地cache缓存的实际对象内容,这样主页list api执行item的normalize时也会读到这些被额外修改过的缓存对象,同一个用户两次主页 list api请求如果读取到的1000 item中存在任意一个item受到新游戏模式修改影响就可能导致最终数据不一致--另外线上Python服务为多主机、多进程部署,每个进程都会维护自己独立的本地cache,此种情况下不同进程本地cache缓存的对象数据基本都是不一致的,用户请求可能落在任意进程上更加重了这种可能性--其返回的数据etag也会变化,也就不可能触发304机制了。
测试环境为什么无法复现呢?
因为测试环境就几个内部人员,同时存在请求主页list和新游戏模式游玩用户的时候很少--简单来说就是并发度不够,所以绝大部分情况下该问题很难触发。
后续处理
新游戏模式每次随机一个item返回其实并无太大性能开销,而且其请求量也并不算高,所以直接不使用本地cache每次都完整执行normalize函数即可,暂时也没必要为其单独开辟一个cache专用。
按以上方案处理上线后,主页list api的304比例直接恢复至30%~50%之间浮动。
转载请注明出处,原文地址:https://www.cnblogs.com/AcAc-t/p/http_304_and_local_cache_bug.html
参考
https://www.cnblogs.com/xiaolincoding/p/12732052.html
https://blog.csdn.net/sinat_20184565/article/details/104851413
https://www.cnblogs.com/acac-t/p/http_304_and_local_cache_bug.html
一个重量级HTTP api的304优化分析与突发失效问题解决的更多相关文章
- 一个扩展搜索API的优化过程
概述 API 是一个服务的门面,就像衣装是人的形象一样. 优雅的 API 设计,能让业务方使用起来倍儿爽,提升开发效率,降低维护成本:糟糕的 API 设计,则让业务方遭心,陷入混沌. 本文将展示一个扩 ...
- 如何设计一个优秀的API(转载)
最近在整理框架的一些 API,觉得很有必要总结一下 API 兼容性的设计.下图是我自己当下的一些总结,慢慢维护: 网上搜索了一下,一个多月前,“标点符”已经发布了下面这篇文章,觉得写得非常不错,转载于 ...
- 如何设计一个优秀的API(转)
到目前为止,已经负责API接近两年了,这两年中发现现有的API存在的问题越来越多,但很多API一旦发布后就不再能修改了,即时升级和维护是必须的.一旦API发生变化,就可能对相关的调用者带来巨大的代价, ...
- [daily][optimize] 一个小python程序的性能优化 (python类型转换函数引申的性能优化)
前天,20161012,到望京面试.第四个职位,终于进了二面.好么,结果人力安排完了面试时间竟然没有通知我,也没有收到短信邀请.如果没有短信邀请门口的保安大哥是不让我进去大厦的.然后,我在11号接到了 ...
- Mysql 索引优化分析
MySQL索引优化分析 为什么你写的sql查询慢?为什么你建的索引常失效?通过本章内容,你将学会MySQL性能下降的原因,索引的简介,索引创建的原则,explain命令的使用,以及explain输出字 ...
- iOS离屏渲染之优化分析
在进行iOS的应用开发过程中,有时候会出现卡顿的问题,虽然iOS设备的性能越来越高,但是卡顿的问题还是有可能会出现,而离屏渲染是造成卡顿的原因之一.因此,本文主要分析一下离屏渲染产生的原因及避免的方法 ...
- mySql索引优化分析
MySQL索引优化分析 为什么你写的sql查询慢?为什么你建的索引常失效?通过本章内容,你将学会MySQL性能下降的原因,索引的简介,索引创建的原则,explain命令的使用,以及explain输出字 ...
- 一次 group by + order by 性能优化分析
一次 group by + order by 性能优化分析 最近通过一个日志表做排行的时候发现特别卡,最后问题得到了解决,梳理一些索引和MySQL执行过程的经验,但是最后还是有5个谜题没解开,希望大家 ...
- 如何设计一个优秀的API
如何设计一个优秀的API - 文章 - 伯乐在线 http://blog.jobbole.com/42317/ 如何设计一个优秀的API - 标点符 https://www.biaodianfu.co ...
- 【MySQL 高级】索引优化分析
MySQL高级 索引优化分析 SQL 的效率问题 出现性能下降,SQL 执行慢,执行时间长,等待时间长等情况,可能的原因有: 查询语句写的不好 索引失效 单值索引:在 user 表中给 name 属性 ...
随机推荐
- Nginx使用Lua脚本加解密RSA字符串
本文主要介绍使用Lua脚本对采用RSA加密后的字符串进行解密的过程. 使用第三方类库lua-resty-rsa,参考地址:https://github.com/spacewander/lua-rest ...
- Raw数据相关概念
什么是"RAW"? 维基百科的字面解释是:原始图像文件包含从数码相机.扫描器或电影胶片扫描仪的图像传感器所处理数据.RAW文件包含创建一个可视图像所必须的相机传感器数据信息. 通俗 ...
- Valine评论插件因为LeanCloud国内域名解析问题无法正常使用的解决方法
近日,LeanCloud 国内域名解析存在问题,Valine评论插件的评论内容都储存在LeanCloud,使用Valine评论插件的个人博客的评论及阅读数会显示失败. 关于 LeanCloud 国内域 ...
- System类_Calendar类_Date类_小记
PrintStream(字节打印流) ps = System.out ; 标准输出流 PrintWriter(字符打印流) InputStream in = System.in; 标准输入流 常用的成 ...
- GitLab 无仓库 中了勒索病毒
坑爹的记录一下,并没有解决 Gitlab 昨天(2021-11-29)打开之后看不到项目了,下面这个吊样子 最后发现中病毒了,一堆的这个吊毛文件,复制一个打开看了一下 你别说这个黑客网页写的还不错,这 ...
- 深度系统安装wine
step1: 输入命令: sudo dpkg --add-architecture i386 step2: 1.切换成管理员权限: sudo su 2.打开源文件 vi /etc/apt/source ...
- 一 APPIUM基本理论知识(转)
1.APPIUM介绍 Appium 是一个自动化测试开源工具,支持 iOS 平台和 Android 平台上的原生应用,web 应用和混合应用.所谓的"移动原生应用"是指那些用 iO ...
- c# 如何将程序加密隐藏?
下面将介绍如何通过LiteDB将自己的程序进行加密,首先介绍一下LiteDB. LiteDB LiteDB是一个轻量级的嵌入式数据库,它是用C#编写的,适用于.NET平台.它的设计目标是提供一个简单易 ...
- Git-入门使用资料
一.Git入门教程 Git入门视频,针对于小白快速入门,时常约2~3小时 Git入门视频 相关课件资料: https://pan.baidu.com/s/1U-s4OmkToXJ5Y7BbJ7w2Ww ...
- Unity TextMeshPro 添加中文字体遇见的问题以及解决方案
前言 按标准官方教程为 Unity TextMeshPro 添加中文字体时出现了各种奇奇怪怪的问题,于是有了这篇随笔. 中文字体解决方案 以下步骤适用于 TextMeshPro 3.0.6. 字符数量 ...