经历
从刚参加工作到现在满打满算,有快4年时间了,期间优化过大大小小的服务,
2C2B的都有,基本上2C会严格些,因为它是面向用户的,用户体验好不好也直接
影响到用户的流失以及购买意愿等问题,响应时间尽可能在毫秒级,最慢不要超过
2秒,特别是核心的服务。2B的话只要不是特别慢,3到5秒都是可以容忍的,前提是
公司内部用户,对性能要求不是很高的受众。
下面直接说思路,我们知道外网的请求通过网络, 先是走DNS服务器将域名解析成IP,
然后请求连接,tcp三次握手,将数据分组发送到对应的服务器上,然后服务器对根据
数据包的序号重组成完整的数据,期间数据包经过交换机,多个路由器转发,链路成解
析成帧单位的数据物理层解析成bit单位的数据,再经过交换机,最后到达服务器端,
服务器端通过进程的调度,处理完逻辑后, 原路返回给用户端.所以如果某个请求响应
很慢,那么需要在这些环节上拆解问题, ping 域名返回的时间,能知道网络时间,基本上
都是100ms以内,普遍5到50ms不等. 我的经验上看基本上不是网络的问题, 当然如果内
网有墙的情况下.
网络和机器层面
其次外网进内网会经过咋们自己实现的服务网关, 服务网关主要是监控流量,黑白名单,
熔断限流鉴权转发等, 内网会对网关会有一个专门的心跳监测, 定时请求从网关到各个服务
的请求监测,如果不通会自动邮件告警, 微信告警.还有对机器的监测告警,CPU,内存,网络,硬盘
等,超过一定阈值会自动水平扩容和告警.
代码逻辑层面
服务通过切面(动态代理)记录耗时日志,如果2C接口超过1s,会发日志告警邮件,接口性能优
化,可通过批量、多线程、异步策略来优化。
1、批量,即批量请求数据库或者缓存,减少在网络上浪费的时间,经验上看,很多同学会在
for循环上单个请求数据库或者缓存,别小看for循环,尽管单个请求数据库或者缓存是50毫秒,
那么50个item,就是2.5s,还没算其他逻辑或者其他请求链路上的时间,如果批量获取数据,
然后再处理,时间是90ms,那么节省了将近2.5s, 性能提升很明显。这也是我优化过接口很常见
的场景。
2、多线程,如果主线程处理时间确实很长,可以通过多线程的方式并发处理,注意,如果使
用多线程,则注意线程安全(适当使用原子类)和CPU核数适当给大一些。多线程可以用jdk1.8最
新的parallelStream流,代码数比ThreadExecutor远远要小,内部原理采用fork/join窃取算法。
3、异步,如果只能用主线程,不可以用多线程这样的场景,逻辑特别复杂冗长,我们可以
拆分成多个接口或者采用异步的处理手段,异步是将不是特别紧要的功能通过本地队列或者第
三方队列的形式实现解耦,实现快速响应返回。比如落库,订阅通知等等,如紧要的功能是访
问缓存获得,所以只需更新缓存,然后发消息去更新数据库和订阅通知。少了更新数据库和订
阅通知逻辑处理,大大提高了接口的吞吐量。
4、此外,还有很多策略,用的比较少的,包括减少for循环,减少条件判断,运维层面的
sql调优,慢查询explain,比较复杂的垂直水平分库分表(涉及到数据迁移等操作,比较繁琐而
且风险比较大),读写分离,连接池数调优,加缓存(本地缓存、nosql,效果明显),JVM调优
(这个我接触的少,主要是处理果full gc一些故障,可以用jmap命令和MAT插件定位对象多一些)
等等,关键是找到瓶颈,定位到问题,对症下药,犹如木桶短板理论,木桶能装多少水,取决于
最短的那块木板。
5、备选短期方案,弹性容器自动扩容。但长期成本过高。
优化流程
1、先让测试人员对接口进行压测,得到旧接口的压测报告和返回数据。
2、开发人员优化接口,让测试人员对优化后的新接口重新压测和比较新旧数据的准确性,回归
原有自动化测试用例,集成测试、功能测试。
3、部署方案,如果出现问题,紧急回滚到上一版。如果出问题需要最快止损,可以通过配置中心
配置对新旧代码实现快速切换。