作者:牧原
项目背景:
疫情初期某地政府决定发放一批免费口罩面向该市市民,该市市民均可免费预约领取,预约时间为早上9点-12点,因此该场景为限时抢购类型场景,会面临非常大的定时超大流量超大并发问题,在该项目的落地过程中,涉及的架构演变,做了一些记录和思考
1,原始架构图示&分析(2月2号晚上22点左右的原始架构)
1.1 客户端走https协议直接访问ecs
1.2 ECS上使用nginx自建https监听
1.3 Nginx反代tomcat,Nginx处理静态文件,tomcat处理动态请求
1.4 程序先去redis查缓存,如未命中则去数据库查询数据,同时redis与mysql之间的数据同步靠程序控制
优点:易管理,易部署
缺点:性能差,无扩展性,存在单点风险
事实证明:该应用一经上线立刻被打挂了(未知原因预约页面泄露,导致还未到预约时间即被打挂)
2,我方介入后的二代架构(24点左右找的我们,早上9点要开服,时间太紧,任务太重,程序不能动的情况下,几十万的并发架构如何做?2月3号早上9点左右的架构,4号也恢复了这个架构)
2.1 接入slb,通过镜像横向扩展负载能力
2.2 接入读写分离数据库架构,通过阿里云数据库自动进行读写分离,自动同步数据
2.3 调整nginx协议
2.4 同架构备集群启用(域名解析做了两个A记录)
2.5 分析访问日志发现失败原因在获取短信&登陆初始化cookie的点上
优点:增加了高可用性,扩展了负载能力
缺点:对流量预估不足,静态页面也在ECS上,因此SLB的出带宽一度达到最大值 5.xG,并发高达22w+,用户一度打不开页面,同时由于新网的限制客户无法自助添加解析,当晚联系不到新网客服导致CDN方案搁浅
3,知耻而后勇的第三代架构(2月4号&2月5号的架构,5号应用)
3.1 接入CDN 分流超大带宽
3.2 取消nginx的代理
3.3 做了新程序无法准时上线的灾备切换方案(没想到还真用到了)
3.4 使用虚拟服务器组做新老程序的切换,但是缺点是 一个七层监听的slb后端只能挂200个机器,再多slb也扛不住了,导致老程序刚承接的时候再度挂掉
3.5 5号使用这个架构上线,7分钟库存售罄,且体验极度流程,丝般顺滑,健康同学开发的新程序真是太爽的
优点:CDN负担静态资源的流量降低了SLB的出带宽,压测的效果也非常理想
缺点:需要多一个独立的域名在页面里面,涉及跨域,4号临开服之际测试发现入库&预约短信乱码返回,紧急切换回了老程序,即二代架构
4,理想架构
4.1 主域名接入CDN,
4.2 CDN通过设置回源http、https协议去访问SLB的不同监听实现新老程序之间的切换,具体实现为回源协议对应
不同监听,监听对应不同的程序
优点:静态加速降低SLB带宽,动态回源,无跨域问题 ,切换方便
缺点:仍需手工设置,镜像部署ecs不方便,如果时间充足,可以直接上容器的架构该有多美好呢,一个scale 可以扩出来几十上百的pod,也可以做节点自动扩容
总结
总结:时间紧任务重,遇到了N多的坑,想起来一个补一个~
1,vcpu购买额度
2,slb后端挂载额度
3,客户余额不足欠费停机
4,新网解析需要联系客服添加
5,第一次考虑CDN架构的时候未考虑跨域问题
6,新程序开发期间未连接主库测试,导致上线失败(主库乱码)
7,第一次(3号)被打挂的时候只关注了slb的流量,未详细分析失败最多的环节
8,上线前压测缺失,纯靠人工测试功能
9,压测靠人手一台jmeter(4号晚上到5号早上引入了PTS进行压测)
10,突然想起来客户原始的程序是放在windows上的,windows+烂程序性能真的极差
11,这个“小项目”前后竟然耗费了小十万,如果一开始就给我们做的话,应该可以减少一半的成本
最后的成果统计(采样分析,实际数据比这个还大):
最后上线的三代架构,为了保险起见上了150台机器,但是根据活动期间的观察,以及对压测结果的评估,上50台机器应该就可以抗住了,从持续5小时一直崩溃被终端用户骂街,到7分钟库存售罄的领导赞赏,虽然经历了3个通宵的戮战,依然可以隐隐约约感觉到身心都得到了升华~
优化参数笔记:
1,参数优化
net.ipv4.tcp_max_tw_buckets = 5000 --> 50000
net.ipv4.tcp_max_syn_backlog = 1024 --> 4096
net.core.somaxconn = 128 --> 4096
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_timestamps = 1(5和6同时开启可能会导致nat上网环境建联概率失败)
net.ipv4.tcp_tw_recycle = 1
/etc/security/limits.conf
* soft nofile 65535
* hard nofile 65535
nginx参数优化
worker_connections 1024-->10240;
worker_processes 1-->16;(根据实际情况设置,可以设置成auto)
worker_rlimit_nofile 1024-->102400;
listen 80 backlog 511-->65533;
部分场景也可以考虑nginx开启长连接来优化短链接带来的开销
2,架构优化
扩容SLB后端ECS数量,ecs配置统一
nginx反代后端upstream无效端口去除
云助手批量处理服务,参数优化,添加实例标识
云监控大盘监控,ECS slb dcdn redis
调整SLB为7层监听模式,前7后4关闭会话保持导致登录状态失效,
3,程序优化
添加gc log,捕捉gc分析问题,设置进程内存
/usr/bin/java -server -Xmx8g -verbose:gc -XX:+PrintGCDetails -Xloggc:/var/log/9052.gc.log -Dserver.port=9052 -jar /home/app/we.*****.com/serverboot-0.0.1-SNAPSHOT.jar
优化短信发送逻辑,登陆先查询redis免登session,无免登session再允许发送短信验证码(降短信的量,优化登陆体验)
jedis连接池优化
maxTotal 8-->20
acceptcount优化(对标somaxconn)
bug:
springboot1.5带的jedis2.9.1的redis连接泄漏的问题,导致tomcat 800进程用满后都无限等待redis连接。
后来进一步调研发现这个问题在2.10.2已经修复,而且2.10.2向后兼容2.9.1
4,数据库优化
redis公网地址变更为内网地址
redis session超时设置缩短,用于释放redis连接
server.servlet.session.timeout=300s
spring.session.timeout=300s
慢SQL优化(RDS的CloudDBA非常好用哟)
添加只读实例,自动读写分离
优化backlog
添加读写分离实例数量