背景

最近查看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优化分析与突发失效问题解决的更多相关文章

  1. 一个扩展搜索API的优化过程

    概述 API 是一个服务的门面,就像衣装是人的形象一样. 优雅的 API 设计,能让业务方使用起来倍儿爽,提升开发效率,降低维护成本:糟糕的 API 设计,则让业务方遭心,陷入混沌. 本文将展示一个扩 ...

  2. 如何设计一个优秀的API(转载)

    最近在整理框架的一些 API,觉得很有必要总结一下 API 兼容性的设计.下图是我自己当下的一些总结,慢慢维护: 网上搜索了一下,一个多月前,“标点符”已经发布了下面这篇文章,觉得写得非常不错,转载于 ...

  3. 如何设计一个优秀的API(转)

    到目前为止,已经负责API接近两年了,这两年中发现现有的API存在的问题越来越多,但很多API一旦发布后就不再能修改了,即时升级和维护是必须的.一旦API发生变化,就可能对相关的调用者带来巨大的代价, ...

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

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

  5. Mysql 索引优化分析

    MySQL索引优化分析 为什么你写的sql查询慢?为什么你建的索引常失效?通过本章内容,你将学会MySQL性能下降的原因,索引的简介,索引创建的原则,explain命令的使用,以及explain输出字 ...

  6. iOS离屏渲染之优化分析

    在进行iOS的应用开发过程中,有时候会出现卡顿的问题,虽然iOS设备的性能越来越高,但是卡顿的问题还是有可能会出现,而离屏渲染是造成卡顿的原因之一.因此,本文主要分析一下离屏渲染产生的原因及避免的方法 ...

  7. mySql索引优化分析

    MySQL索引优化分析 为什么你写的sql查询慢?为什么你建的索引常失效?通过本章内容,你将学会MySQL性能下降的原因,索引的简介,索引创建的原则,explain命令的使用,以及explain输出字 ...

  8. 一次 group by + order by 性能优化分析

    一次 group by + order by 性能优化分析 最近通过一个日志表做排行的时候发现特别卡,最后问题得到了解决,梳理一些索引和MySQL执行过程的经验,但是最后还是有5个谜题没解开,希望大家 ...

  9. 如何设计一个优秀的API

    如何设计一个优秀的API - 文章 - 伯乐在线 http://blog.jobbole.com/42317/ 如何设计一个优秀的API - 标点符 https://www.biaodianfu.co ...

  10. 【MySQL 高级】索引优化分析

    MySQL高级 索引优化分析 SQL 的效率问题 出现性能下降,SQL 执行慢,执行时间长,等待时间长等情况,可能的原因有: 查询语句写的不好 索引失效 单值索引:在 user 表中给 name 属性 ...

随机推荐

  1. Codeforces Round #881 (Div. 3) A-F

    比赛链接 A 代码 #include <bits/stdc++.h> using namespace std; using ll = long long; int a[57]; bool ...

  2. Redis 备忘录

    redis是什么 Redis 是一个高性能的key-value数据库 常用操作 下载 官网:https://redis.io/ Linux版:https://redis.io/download Win ...

  3. 利用python分析pdf数据,分析上市公司财报

    import re import os.path import matplotlib import matplotlib.pyplot as plt from pdfminer.pdfparser i ...

  4. webpack配置文件的分离

    配置文件的分离 目的就是让开发环境, 生产环境,测试环境的配置分隔开 步骤一: 在项目根目录下创建一个 build 文件夹专门用来存放配置文件,再创建三个js文件, base.config.js 文件 ...

  5. Win11 开启 telnet

    'telnet' 不是内部或外部命令,也不是可运行的程序或批处理文件. 首先我们打开底部"开始菜单",如图所示.接着打开其中的"设置". 然后进入左边的&quo ...

  6. C# OpenCVSharp图像入门_给绿幕图片视频加背景

    OpenCV的全称是Open Source Computer Vision Library,是一个跨平台的计算机视觉库.OpenCV是由英特尔公司发起并参与开发,以BSD许可证授权发行,可以在商业和研 ...

  7. 2023-08-12:用go语言写算法。实验室需要配制一种溶液,现在研究员面前有n种该物质的溶液, 每一种有无限多瓶,第i种的溶液体积为v[i],里面含有w[i]单位的该物质, 研究员每次可以选择一瓶

    2023-08-12:用go语言写算法.实验室需要配制一种溶液,现在研究员面前有n种该物质的溶液, 每一种有无限多瓶,第i种的溶液体积为v[i],里面含有w[i]单位的该物质, 研究员每次可以选择一瓶 ...

  8. 使用C++界面框架ImGUI开发一个简单程序

    目录 简介 使用示例 下载示例 main文件 设置ImGui风格 设置字体 主循环 添加Application类 中文编码问题 界面设计 关于imgui_demo.cpp 创建停靠空间 创建页面 隐藏 ...

  9. 知识图谱(Knowledge Graph)- Neo4j 5.10.0 Desktop & GraphXR

    下载地址:https://neo4j.com/download/ 安装 下载时会产生激活码(保存下来) 下载完成后安装 运行后,输入激活码 进入主页面 运行自带的电影知识谱图测试是否安装成功 安装 G ...

  10. 6、Spring之基于xml的自动装配

    6.1.场景模拟 6.1.1.创建UserDao接口及实现类 package org.rain.spring.dao; /** * @author liaojy * @date 2023/8/5 - ...