打入第三方内部之压测篇
文摘
游戏
2023-11-03 18:00
浙江
作为游戏QA,各位以甲⽅的⾝份或多或少都做过⼀些第三⽅接⼝相关的测试(或者功能QA负责的玩法⾥有⽤到),但蛮少有机会作为⼄⽅向别⼈提供接⼝。作为甲⽅,我们总是会抱怨某某第三⽅怎么这里也不提供环境,那里也没测到位,⼀压tps抖得跟喝醉了酒⼀样,或者⼀上线发现限流跟预期不符等等问题,那么作为⼄⽅,从他们的视⻆来看这些问题会怎么解读?结合甲⽅⼄⽅的经验,是不是中间环节有⼀些事情沟通有偏差,流程可以更优化呢?下⾯主要以作者个⼈经历为基准,基于这些经历得到的⼀些他认为的三⽅接⼝合作之道。这⼏年陆陆续续做了⼀些压测⼯作,有⼀些打破我常规认知的事情,其中⼀点就是对压测的基础概念的理解。在我以往的观念中,常做压⼒测试的QA应该对TPS、QPS、并发、线程数、同步/异步请求这些概念是理解清晰且⽅向一致的,但就是这样的惯性思维,在⼀次过往案例中导致了收尾阶段的⼿忙脚乱。⾸先,先梳理⼀下⼏个指标的概念。Transations Per Second,顾名思义,每秒(完成)吞吐量/事务量,其代表的客⼾端发起⼀个请求到服务器响应的过程,包含了如下3个过程:2.服务端内部处理、运算,这中间可能存在内部的请求;3.服务端返回响应结果,客⼾端成功收到响应,这是吐;这3个流程完整执⾏⼀轮,视为⼀次吞吐,记为1TPS。Queries Per Second,每秒(完成)请求量,这是⼀个数据库中的概念,每秒查询执⾏条数,被引申到压测中,但在压测中是有⽚⾯性的,仅仅代表服务器每秒能够⽀撑的查询次数,如果⼀次请求事务是包含多部分请求,则 TPS * N = QPS,因此在压测⼯作中并不建议使⽤QPS作为接⼝性能的指标来进⾏衡量。即同时发起请求的用户数量,在jmeter中为同时发起的线程数量,jmeter是接⼝压测中普遍使⽤⼀个压测⼯具,很多接⼝压测平台底层封装的都是jmeter,⽐如杭研NPT、伏羲质测平台。并发是⼀个很容易混淆的概念,常常有⼈将并发与TPS划等号,或者认为QPS是并发,在游戏中将其理解为同时发起请求的玩
家数量,或同时发起请求的服务器进程数量,这⾥的关键是同时,这是与TPS/QPS最⼤的差异,TPS/QPS代表的是1秒内总共能处理的事务/请求数量,这些请求并不是同时的,是有先后顺序的。如果把某个第三⽅服务进⾏拆解,可以将其分拆为n个最小服务单元,每个最⼩服务单元是串⾏提供服务,即每次同时处理1个请求,处理完了再吞⼊下⼀个请求进⾏处理。假设每个最小服务单元每秒提供m次吞吐,那么这个服务每秒可提供的TPS是:其中,x为请求从客⼾端发送到第三⽅服务的耗时,y为请求从第三⽅服务返回到客⼾端的耗时,z为请求在第三⽅服务⾥排队等待+实际处理的耗时。可以注意到RT公式中的z耗时中有⼀部分是排队等待,⽽这就是并发数带来的。假设1个服务的同时处理能⼒是100个请求,此时如果通过1000个并发线程进⾏模拟施压,会发⽣这样的事情:
- 施压开始,1000个并发线程(同步发送)同时发送请求,共1000个请求被送达服务端;
- 服务端捞取其中最快到达的100个请求进⾏处理,其余900个进⼊排队队列;
- 服务端第⼀次捞取的100个请求处理完毕(假设每个请求处理时间完全相同,为10毫秒),再捞100个,此时剩余800个请求依然在排队队列中;
- 服务器第⼀次处理完毕的100个请求返回到并发线程处,这100个线程再次发送100个请求,请求到达服务端,进⼊排队队列,此时排队队列⻓度900;
⼤家可以看到,前100个请求在服务端的总耗时为10毫秒,第101到200个请求服务端总耗时为20毫秒,...,第901个开始的请求其服务端总耗时为90(排队等待)+10(实际处理)=100毫秒,⽽这就是⾼并发下出现⾼响应时间的原因。因此总结下并发数和TPS、RT的影响关系:- 当并发数⼩于服务同时处理能⼒时,其TPS表现为逐步上升,RT表现为稳定在较⼩值;
- 当并发数等于服务同时处理能⼒时,其TPS表现为到达峰值,RT表现为稳定在较⼩值;
- 当并发数⼤于服务同时处理能⼒时,其TPS表现为缓慢下降(过⼤的排队队列、⽹络通信开销会对服务造成额外开销影响性能),RT表现为迅速增⼤,直到出现请求超时现象(队列末尾的请求等待时间⼤于系统设置的最⼤响应时间);
这个说的是并发线程(游戏中代表玩家客⼾端、服务端⾮直连进程、服务端直连进程)的请求发送⽅式,其中异步请求类似于游戏中的进程间通信RPC,A进程只管⾃⼰发,并不会等请求都得到响应才发送下⼀个请求,⽽是“想发就发”。继续⽤上⾯的例⼦来解释,假设这1000个并发线程是异步发送的,每个线程每10毫秒发送1个请求,那么:- 第10毫秒后,排队队列中有900个原先排队+1000个新增=1900个;
- 第20毫秒后,排队队列中有800个原先排队+2000个新增=2800个;
- 第x个10毫秒后,排队队列中有1000*x+1000-100*x=900x+1000个请求;
可以预想到,当请求是异步的,且请求发送频率接近或等于系统理论最佳处理响应时间时,请求的实际表现为RT迅速增⼤直到⽆限趋近最⼤响应时间,超时请求数量指数爆炸,系统的表现为迅速瘫痪到⼏乎⽆服务能⼒。⚠️OK,到这⾥基本梳理完毕,在实际合作对接中,强烈建议重点对⻬以下概念:
- ⽬标并发 ——希望是怎样的并发下,能够达到上述TPS、RT
对⻬基础概念后,双⽅对于服务的整体⽬标已经有了⼀个相对清晰的⽬标,下⼀步要做的是对⻬限流⽅案,主要是两⽅⾯:分为两个⽅⾯,⼀是具体的三⽅服务受资源限制(CPU、GPU、内存资源),其计算能⼒有上限,需要将集群接收的流量有选择性地放⾏,超过限流值的部分则直接返回掉。⼆是服务所在的基础系统有流量能⼒上限,从⼊⼝到⽹关到中间代理,包括基础系统的各中间件,因此需要层层限流,避免下⼀环节被过载流量打爆。越⽕爆的游戏越容易遭到觊觎,受到攻击的情况也越多,第三⽅服务虽然通常不会影响游戏主流程,但是也要做好恶意攻击防御,⼀种基础的防御就是限流,在集群最前侧拦⼀个qps能⼒极⾼的⽹关(⽐如nginx内核的),过滤到计划外的流量,只放⾏后端能处理过来的流量进⼊。计数器是最简单的限流算法,指定时间间隔内对接⼝请求次数进⾏限制,也称固定窗⼝限流。但计数器限流在⼀些极端情况下有较⼤缺陷,它有个潜在的默认条件是流量是稳定均衡的,否则会出现窗⼝切换间的放⾏流量激增。⽐如限流每秒1000,如果这1000集中在前1秒的最后⼏毫秒,然后下1秒的前⼏毫秒⼜爆发式进来1000流量,那这2000流量都会被放进来,但是对于后端来说短短⼗数毫秒之间就接到了2000请求。
滑动窗⼝是固定窗⼝限流法的优化版本,其将限流窗⼝划分为⼩窗⼝,每个⼩窗⼝记录期间的请求总数量和放⾏数量,如果放⾏总和已超限流值,则剩余请求拒绝,然后向后滑动1个⼩窗⼝计算新的限流窗⼝是否到限流值,不到就在新的⼩窗⼝⾥放⾏⼀部分流量,否则继续限流,以此类推,如下图。漏桶算法类似于提供⼀个虚拟的“桶”去装进来的请求,桶的容量固定,超过容量的请求则溢出,表现为拒绝。桶的流出速度固定,即固定时间单位向下游放⾏固定数量的请求。漏桶算法能起到削峰和缓冲的作⽤,对于后端服务来说,⽆论⼊⼝处流量有多⼤,其总是恒定速度和压⼒处理。令牌桶算法与漏桶算法相似,区别在于桶内装的是令牌,每个请求进来后必须获得⼀个令牌才能被放⾏,否则将被拒绝,令牌使⽤完毕后重新进⼊桶内等待下次发放。⚠️综上,限流⽅案对齐,⾸先游戏⽅和业务⽅要根据具体服务和业务架构,敲定限流的⽅式(最好不要使⽤简单的计数器限流),然后根据调⽤量级评估、⾼峰值和发⽣时间段评估等确定具体的限流值(略⾼于调⽤预估、略低于服务实际提供能⼒)。尽管我们⼀定会为即将到来的上线冲击做好万全的准备,扩好容、备好资源、做好限流、强化系统稳定性、强化服务稳定性、健壮数据库调⽤等,但是意外往往会在不经意间到来,因此⽆论准备⼯作做得多完善,我们总要去假设这么3个问题:这是⼀个监控告警完善的⼯作,针对第三⽅服务,常⻅http/RPC监控有:短暂故障,可能是服务本⾝有bug,也能是短时⾼流量冲击,或者是部分上有链路异常,因此可以从以下几方面做好预案:如果⼀旦不幸发⽣短时间内服务完全瘫痪、⽆法修复,除了游戏侧的保底以外,还需要在游戏侧和第三⽅都具有热更关闭接⼝访问的能⼒,避免在服务瘫痪后,游戏侧每个仍然往第三⽅请求直到超时才启⽤保底。参差不⻬的第三⽅交付质量会拖累产品⽅,延⻓产品⽅压测验收流程。
这个统⼀指两方面:第三⽅与游戏⽅的标准统⼀,以及第三⽅内部各服务之间的标准统⼀。
- 压测标准——全量资源规格、并发量、⽬标tps和rt,重点是不要过度预估,避免出现预估⼀万实际100
- 验收标准——交付后怎么验收,验收通过后如何确保环境不再变化,⼀旦要改需要⾛怎样的正式审批流程
- 交付由统⼀接⼝⼈负责,避免出现n多接⼝⼈,给游戏⽅造成困扰
交付给游戏⽅的服务⼀压后发现超时了、tps不达标了、不响应了,往往是缺乏稳定性测试(⻓时间、多模块混合并⾏压测)、全链路压测(调⽤的第三⽅由于收费问题、环境问题此前是mock的,导致问题被隐藏),这些问题应当在三⽅内部就解决掉,⽽不是带到游戏⽅去暴露。这⼀节主要从第三⽅的⻆度谈谈有哪些措施可以帮助提⾼三⽅内部、三⽅与游戏⽅之间的测试效率、测试质量。作为游戏⽅QA,也可以在⽇后与三⽅合作时遵循下列⼏个关键点去和三⽅沟通,去提醒他们这些点上是否有提升空间,从⽽确保合作的顺畅。
一方面,游戏正式上线前往往会提前给玩家体验⼀些引流性质、分流性质的前置玩法活动,这些玩法中可能也会应⽤到⼀些第三⽅服务,对于第第三方而言就会存在多个不同的上线⽇期。另⼀⽅⾯,内部测试环境、游戏⽅验收环境与正式环境应当有物理隔离,避免测试过程影响到正式环境。因此,应当准备以下几套环境:
⽤于游戏⽅验收,将内部测试与游戏验收的环境进⾏隔离,主要是便于双⽅并⾏开展,避免出现⼀⽅压测,另⼀⽅必须让道的情况。实际如果存在资源不⾜等问题,测试环境和验收环境可酌情合并为⼀套,但会增加并⾏开展测试⼯作的难度。用于游戏方上线后正式调⽤的环境,应当保证在回归完毕后不再做任何调整。这套环境可以在临上线前再准备,主要⽤于游戏上线后出现的⼀些调整,必须在预发布环境先进⾏验证,确保⽆误后再在正式环境进行调整。业务架构要及时准备好,以提供给内部压测的同学,以及游戏⽅有需要时可以进⾏参考。所谓“及时”,指的是在⼀切正式压测⼯作开展之前,就应当画好清晰明了的业务架构图。业务架构不能仅仅只是粗略的模块图,需要精确到具体的⼦模块:对于每⼀个具体的服务,要能够清晰地知道从游戏⽅请求,到返回游戏⽅这⼀整个链路的具体流转过程。监控⽅⾯主要是两个维度的⼯作,⼀是统⼀监控平台,⼆是确保监控的有效性。⼀个统⼀的监控平台不仅内部测试时⽅便,在对外沟通、数据留存等⽅⾯也有很⼤的优势。⼀个复杂的三⽅服务可能基础系统、服务本⾝、中间件分属多个不同的部⻔提供,⽐如⽇志存储与分析是⼀个部⻔提供,对象存储是另⼀个部⻔提供,数据库相关中间件⼜是⼀个部⻔,这就会导致性能和流量监控分属不同的部⻔在各⾃负责,往往容易反应迟滞和沟通不及时,实际上线时流量⼀打进来需要多部⻔同时保持在线,本⾝⻛险也⽐较⼤。在这⼀块上的对接上还是花费了不少不必要的沟通成本,如果有⼀个统⼀、易上⼿、查找⽅便的平台,这部分的成本可以节约下来。对于接⼝tps、rt而言,如果刷新频率是30秒甚⾄超过1分钟,那么这个实时性可以说是糟糕的。⼀个比较好的刷新频率是5秒,即保障了数据的相对实时(最多5秒延迟),也确保了监控本⾝不会对应⽤造成明显的性能开销。另⼀⽅⾯,云资源的特殊性也带来了监控上的⼀些难点,异构环境、集群内流量等情况的监控有时候反而会带来统计上的不准确,这⼀块也是需要提前做好保障的。控精准每⼀条告警都要跟进,确认有问题的⻢上联系开发同学查看,并将告警转发到涉及业务的⼤群⾥,以让相关⼈员可以第⼀时间知悉告警的发⽣和后续修复、测试进展。并且,这种态度应当是由上⽽下的,从经理级别⼈物到开发负责⼈再到qa团队,都是同⼀个标准和态度去贯彻。告警的初衷是将⻛险或者故障通过popo、邮件等形式及时推送出来,以便进⾏及时的跟进和处理,将⻛险灾害防患于未然,或将既定的事故影响程度和范围控制在最⼩范围内。在实际⼯作中,我的感受是告警⼀来容易被忽略,⼆来容易被滥⽤,于我⽽⾔,告警的特点应当是少⽽精的:- 所谓精,是指精准命中。我们需要的是准确的警报,⽽不是模棱两可甚⾄时对时不对的⼴撒⽹式推送,狼来了的故事⼤家⼩学都学过,告警就像是故事中放⽺孩⼦的呐喊,如果推送了不准确的告警出来,⼀次两次之后⼤家就都不信任这个告警了,它的严肃性就被⼤打折扣,不会再有⼈认真对待它,当真有“狼”来了,即便有及时的告警,也会被“村⺠们”当做是有⼀次戏耍,从⽽造成严重的事故后果。
- 所谓少,是指避免滥⽤。告警是⼀把好⽤的剑,但是剑就有双刃,⽤得好⽆往不利,⽤不好反噬其主。据我所观察,有时候告警容易被⽤成“这个推送到群⾥吧”“那个也推送到群⾥吧”,哪怕告警达成的规则并不完全明确,也会希望先将信息往群⾥推,推了⼤家就会看到,看到了就总会有⼈去跟进。事实上,这种⽅式弊⼤于利,每个⼈的精⼒是有限的,每天不同时间段的精神状态和专注度也是有差异的,过多的告警只会消耗掉⼤家的精⼒,从⽽导致更该做的事情没有及时得到处理。
⚠️告警的配置要慎重,切勿⼀味“政治正确”,别⼈组⾥这也有监控那也有告警,我们就也都要有!这种观念是不可取的,及时的告警跟进和少⽽精的告警环境是相辅相成的。
事前准备充分,事中不慌不乱,事后省时省心。⼀个好的开始是成功的⼀半,不要怕麻烦,不要担心对方异样的眼光,宁可前期被当做是个傻⼦,也不要事情发⽣时真成了⼀个傻⼦。脸⽪厚,能成事。