本文从全网系统架构,部署组网梳理,全链路压测和容量评估,限流,降级和超时,如何防爬、防刷,动态配置,性能优化如何做,如何分析,遇到的性能问题和优化,全量压测等方面来全面剖析有货如何应对双十一这种大促。
1.全网架构梳理,梳理出核心调用链路涉及到的服务,这是后面主要压测和扩容的依据。部署组网梳理,细到每个域名下挂了几个nginx,nginx下有几台服务,redis是如何访问,数据库是如何访问,访问代理或中间件的扩容,服务配置是不是全部对等,这里需要全网的一张拓扑图,能很清楚系统是怎样的结构,除了CMDB之外,还需要做一次double check,在实际压测过程中会遇到clb上挂的nginx不是实际的nginx,nginx后端挂的服务机器少,redis域名指向的是单台tweproxy,机器配置不对等导致的短板效应等等。这是在自动扩容,集群是否是期望的,后面还需要优化的
关于容量评估:
首先我们可以知道用户的典型访问模型
1、预估流量来源,典型的几个访问路径如下
2、基于页面来源,所有访问接口均带上来源页面,评估每个接口都是哪些页面带来的流量,流量占比如何分配,这样能很好的识别出来核心页面都涉及到哪些接口,然后识别出来哪些接口是核心接口,哪些是非核心接口,再基于接口的调用量就能评估出核心接口的流量分配占比
3、基于大数据分析用户历史行为访问路径和流量占比
4、和营销负责人明确双十一所有的活动,评估流量可能的来源,和双十一互动玩法,梳理出可能会在核心链路里面遗漏的接口
5、对于核心接口读流量容量评估,直接基于平常访问量放大15-20倍左右,对于核心交易链路上的接口可以基于活动玩法,累积加购,收藏,UV等数据评估量,然后放大10-20倍,给系统有一定的冗余量
6、所有的扩容都不是对等的,流量洪峰不会全网的业务场景涉及的接口都过来,所以提前和业务了解到整个双十一的促销活动玩法才能很好的评估扩容需求。输入每秒加入购物车1000TPS/S,同时下单1000TPS/S,此时只需要保证核心接口接口和被依赖接口支持这个峰值,对于非核心接口做好降级,异步,失败熔断取默认就好,例如获取用户可用有货币,失败不影响流程。获取用户地址信息,地址信息在调用方缓存一份,失败可以取缓存数据,等等。对于一个集群的扩容,需要评估出来这个集群对外提供的接口每个接口的并发,这样全量压测时,按照指定流量压测,才能很好的评估出这个集群如何扩容
1、面向用户流量的限流,基于全网压测的结果,和系统评估出来的承受出来的最大能力,做好每个接口相应的限流,和限流之后的提示,对于接口慢的,流量突发的(秒杀)要做好限流控制和测试,保证不搞垮系统
2、后台mq消息限流,因为系统扩容 ,消费能力大大增加,对于一些大批量写的操作要做好限流,不能因为面向用户的流量做了限流,系统本身的处理能力没做限流,导致大批量写(发券,发站内信)将数据库写爆掉。
3、redis的写也要做好限流,推荐和实时推荐会大批量往redis写入数据,要控制写入频率和时间,不能把redis和带宽写爆掉
4、任务系统,梳理系统中是否有任务会定时在流量爆发点执行,要把任务错开做好相应应对策略
5、对于系统内部调用也要做好限流,面向后台,其它业务线
1、依赖第三方接口,要做好降级和超时时间控制,比如掉微博,微信等第三方接口。
2、降级测试要做好,降级之后app不能出现崩溃情况,要保证基本的购物体验
3、什么样的接口降级能带来效果,针对流量大的接口,且非核心的接口,对系统压力也较大的。比如全球购的购物车数量和有货购物车数量是同一个级别的调用次数,在全球购系统中是第一的调用,如果为了支持这个接口的容量去扩容很多机器,显然得不偿失。另外像商品详情页的是否收藏的调用,不显示收藏对用户体验影响不大,这个可以在app端直接去降级。对于系统中识别出来的一些在核心链路里面的非核心接口也可以支持降级,对于一些非核心接口,可能会突然爆发一些问题,比如掉第三方大量超时,事先也没有埋好降级开关,此时我们就需要通过限流去保证线程不会都被挂住,今年双十一就紧急限流了一个非核心接口,掉微博大量超时。
4、所有的通信都需要做好超时控制,db,redis,memcache,http调用等等
1、传统的根据头里面的use agent判断爬虫基本无用。对于识别出来的爬虫,将流量导入另外一个缓存集群。基于访问频率控制,基于ip控制访问频率,识别出来可以把请求转向或加强提示验证。基于用户浏览点击行为分析(正常用户的行为和异常用户行为分布不一样)来加强验证,基于大数据spark实时计算对于敏感接口访问频率自动控制封杀
2、所有接口加签,敏感接口加密,session校验
1、对于接口缓存时长,mq消费速率,降级配置,限流配置均能动态调整
慢的接口可以延长提前将缓存时间,提高缓存命中率,减少服务回源
mq消费速率会随着集群的机器越多,消费能力越强,此时需要动态去调整消费速率,防止把服务,数据库压垮
1、集群负载不高,可并发量就是上不去,首先要排除nginx和应用服务器的带宽是否有到瓶颈,可借助nmon工具监控。如果没有瓶颈,说明是流量进不来,此时可能是从内网走nat出去被带宽限制了。
2、cpu压不上去,此时可能有什么锁的操作(比如logback打日志),或者线程队列满了,io满了等等,此时可以通过打印jstack看程序都在执行什么,如果jstack分析不变,可以借助jvisualvm。同时各种池(线程池,连接池)要支持可监控,线上一般不太好用jvisualvm连上去监控
3、cpu高,也可以借助jstack命令找到耗cpu高的线程号都在干啥
4、集群响应时间慢,此时要去看是慢在那里,是接口慢还是nginx返回慢,如果是nginx慢,要看nginx是否有负载,带宽是否足,ngxin是否有锁操作(在实际压测pc,h5过程中,因为一个锁参数,导致压不上去)。如果是接口慢,要看是网关哪台机器慢,如果是普遍慢,就需要看服务调用是那里慢,数据库慢慢,redis是否慢,是否哪台机器特别慢。一层层排查,这里就需要有个全网调用链,和全网健康度来协助定位问题
5、回源服务很多,导致整体响应时间变成,此时要考虑为啥没命中缓存,是不是缓存服务器内存不够
6、应用整体健康,带宽也充裕,此时要考虑压力机本身的性能或者带宽也会影响性能,导致请求无法发出来
7、nat网关出口ip太少,高并发下导致和目标服务器建立的端口不足导致的性能问题
8、4层HTTP代理和TCP代理对性能的影响,TCP性能高于HTTP很多
9、关于内存溢出,疯狂fullgc,可以借助jstatjmapjcmd命令来查看应用gc情况和内存情况,来判断堆分配是否合理,内存溢出的点大致在那里,如果无法很直观的看到内存溢出点,可以生成heap文件借助mat或者IBM的heap工具分析
10、接口慢,如何定位到接口那里慢,此时我们可以隔离一台机器出来,用一些工具来分析,比如Greys
https://yq.aliyun.com/articles/2390
1、日志打印不规范,打印之前先执行了tostring,打印日志之前,日志里面不要做计算逻辑,如果存在计算逻辑,先判断下日志级别,正常大促情况下日志级别都是warn
2、缓存设计不规范,空对象没缓存,缓存key中存在动态变量,比如uid,对于可以长时间缓存的数据,因为带了uid导致几乎没有命中率
3、代码中大量使用反射,导致性能低下
4、接口不分场景的任意编排,导致不同场景的重复接口调用
5、大而全的接口,对于不同场景,提供的数据太多,接口设计需要区分场景和调用模型,比如不能在详情页掉了一个在很少场景才会触发的第三方接口
6、对于静态配置或基本不会怎么变的信息直接用本地缓存,支持失效回源
7、去除无用的调用,比如为了所谓的用户体验,会提前加载很多数据,比如在列表页直接把所有filter查询回来,而不是用户主动触发,在购物车中把商品购买数量限制查询回来,而不是在结算页去限制,很多调用都是无用的,不同页面的流量比例是不一样的,不能在一个大流量的页面把小流量页面的事情全做了
8、在用fastjson做序列化时要注意屏蔽掉spring自带的序列化器,在压测过程中会发现走到jackson的序列化器里面,消耗CPU,register-defaults="false"通过这个参数控制
9、提交订单场景,用券场景,针对少部分场景做了全局的分布式原子计数控制,得不偿失,牺牲99%的性能场景
10、缓存批量清理,异步清理
11、对于核心日志异步打印,比如订单提交日志,支付日志等等,默认在大促只会打印warn日志,日志对于系统性能的影响是特别大的。log4J2本身是异步模型,考虑到项目中全部是用的logback替换成本很高,可以用logback的异步appender代替,性能基本和log4j2一致,性能可以提高4到5倍,additivity="false"参数很重要,如果日志文件已经单独拆成一个文件,这里一定要配置成false,不然会往root的appender再打写一份日志,且是循环写的
12、并发调用没有先后依赖关系的接口
13、报文大小会直接影响序列化和网络传输,报文尽可能的只返回调用端需要的字段
14、去除没必要的对key的md5,本地缓存时不需要做什么序列化,直接保存在内存中的,去除不必要的sort排序
15、spring在匹配非rest风格时的性能损耗,
rest风格
非rest风格
对于非rest风格的url,spring会启用antmatcher方式正则一个个去匹配,且在url越来越多的时候性能更糟糕,替代方式如下:
16、对于搜索,gateway(网关),需要合理分配年轻代的堆内存大小,因为都是大对象,且并发高。年轻代大小太小,会导致对象很快到old区,不能在年轻代被回收,触发fullgc,导致cpu居高不下
17、pc h5并发量大时,因为掉接口是走内网dns解析,dns解析服务器延时高,导致超时。可以在调用方缓存几秒dns解析结果,另外架设nscd dns缓存服务器
18、memcache和redis内存太小,导致频繁LRU淘汰,命中率低
19、用ehcache需要严格控制堆大小,如果用个数,需要知道单个对象的大小,如果无法评估,建议用占用堆大小控制,不然会导致内存溢出
20、List Map StringBuilder等没有初始化容量大小,导致频繁扩容影响性能
1、确定好每个接口的并发和并发线程数,做好数据池,合理分配压力机
2、做好数据隔离,不能污染用户数据,浏览记录隔离(防止对用户数据产生影响),mq队列隔离(防止影响正常队列),下单不能用正式商品(会锁库存),用券,用有货币等等对于写的数据,均要用压测用户来做,对于一些实时的数据统计产品的影响,比如统计下单量,收订金额等等(可以通过订单类型来过滤)
3、压测时我们经常会碰到这些问题
gateway调用服务异常,是啥ip到哪个服务异常,服务调用服务,是哪个ip异常,那么多机器,查起来很累。
接口到底有没有触发限流,没办法很好的知道限流策略是否合理,什么导致的触发限流,缺乏限流监控告警。
接口好慢,到底是nginx慢,还是后端服务慢,还是客户端网络问题,还是哪台机器慢,数据库慢。
全网的机器配置到底是怎样的,是不是我要的配置,是否都对等,会不会有的是4c,有的是8c。各个域名下挂的解析到底对不对,挂的nginx对不对,需要全局拓扑图。
全网资源使用情况的拓扑图啊,能很快定位到那个服务器是否存在瓶颈。
热点系统,为啥我的带宽如此的不均匀,到底是谁把我的带宽打的这么高。
针对上面的问题我们需要全网调用链,全网健康度分析,代码埋点借助influx分析聚合,当发生异常时,通过调用链能快速定位到是那个服务那个ip节点出了问题,也能分析出平均每个节点响应的平均响应时间,那个调用慢等等,全网健康度通过采集所有服务器数据,应用指标数据,上报到kafka,通过spark分析聚合各种维度的数据,能实时看到整个应用大盘的健康状况。通过对缓存和db访问数据的上报到influx,借助spark和influx聚合也能分析出热点key,命中率,热点statement,那种类型的key访问频率高等等。通过全网的一个可视化的自动化cmdb,就能看到每个服务器的配置情况(CPU,内存)