系统架构师思考
创新互联建站主营常山网站建设的网络公司,主营网站建设方案,重庆APP软件开发,常山h5成都小程序开发搭建,常山网站营销推广欢迎常山等地区企业咨询
秒杀活动是指网络商家为促销等目的组织会网上限时抢购活动,这种活动具有瞬时并发量大、库存量少和业务逻辑简单等特点。
设计一个秒杀系统需要考虑的因素很多,比如对现有业务的影响、网络带宽消耗以及超卖等因素。本文会讨论秒杀系统的各个环节可能存在的问题以及解决方案。
一、JVM调优(调优原理,上线调优细节,掌握基本调优参数设置,调优一些经验),GC日志分析,进一步调优。
二、数据库调优(连接池调优,数据库常见设计调优,缓存)。
三、多级缓存优化(堆内缓存,分布式缓存,openresty内存字典, lua+redis实现接入层缓存)。
四、秒杀下单(高并发模式下实现下单操作—满足业务需求)-- Lock锁,AOP锁优化,分布式锁优化。
以用户为中心,提供快速的网页访问体验。主要参数有较短的响应时间、较大的并发处理能力、较高的吞吐量与稳定的性能参数。
可分为前端优化、应用层优化、代码层优化与存储层优化。
①服务尽量进行拆分(微服务)---- 提高项目吞吐能力。
②尽量将请求拦截在上游服务(多级缓存)--- 90% ----> 数据库压力非常小,闲庭信步,数据库架构(主从架构)。
③代理层:做限速,限流。
④服务层:按照业务请求做队列的流量控制(流量削峰)。
伸缩性是指在不改变原有架构设计的基础上,通过添加/减少硬件(服务器)的方式,提高/降低系统的处理能力。
云原生:项目运行云端,可以随时动态扩容—K8S。
8C+16G : 2000QPS +-。
(此数字是估算结果,真实结果受到代码编写数据结构,业务逻辑,架构、rt,以现实测试结果)。
SOA --- 微服务 --- 业务拆分模块 --- 新业务需求 --- 根据新业务需求创建新模块服务。
可以方便地进行功能模块的新增/移除,提供代码/模块级别良好的可扩展性。
对已知问题有有效的解决方案,对未知/潜在问题建立发现和防御机制。对于安全问题,首先要提高安全意识,建立一个安全的有效机制,从政策层面,组织层面进行保障,比如服务器密码不能泄露,密码每月更新,每周安全扫描等。
以制度化的方式,加强安全体系的建设。同时,需要注意与安全有关的各个环节。安全问题不容忽视,包括基础设施安全,应用系统安全,数据保密安全等。
基础设施安全:
硬件采购,操作系统,网络环境方面的安全。一般采用正规渠道购买高质量的产品,选择安全的操作系统,及时修补漏洞,安装杀毒软件防火墙。防范病毒,后门。设置防火墙策略,建立DDOS防御系统,使用攻击检测系统,进行子网隔离等手段。
应用系统安全:
在程序开发时,对已知常用问题,使用正确的方式,在代码层面解决掉。防止跨站脚本攻击(XSS),注入攻击,跨站请求伪造(CSRF),错误信息,HTML注释,文件上传,路径遍历等。还可以使用Web应用防火墙(比如:ModSecurity),进行安全漏洞扫描等措施,加强应用级别的安全。
数据保密安全:
存储安全(存储在可靠的设备,实时,定时备份),保存安全(重要的信息加密保存,选择合适的人员复杂保存和检测等),传输安全(防止数据窃取和数据篡改)。
常用的加解密算法(单项散列加密[MD5、SHA],对称加密[DES、3DES、RC]),非对称加密[RSA]等。
单体架构(all in one) à水平拆分/SOA架构à微服务架构 àkubernetes云原生架构(微服务迁移到云原生)à ServiceMesh (服务网格架构,下一代微服务架构,云原生架构:istio) à serverless 架构 (无服务架构)。
企业架构转型:数字化转型。
传统架构过渡到云原生架构(容器云)。
(1)单体架构——所有业务都在同一个应用中,没有进行任何拆分。
注意:集中式架构模式,所有的请求都集中在同一个服务上面,对服务压力较大;因此这样的架构适合并发较小的架构;同时 同一个服务器中,数据库,项目都会抢占服务内存,cpu资源,造成服务性能问题。
(2)单体架构优化。
(3)单体架构流量预估(单体架构真的不能承受亿级流量??)单体架构:中小型企业,创业公司。
①传统项目(并发量小,业务简单,需求固定),项目体量比较小
②小程序
③追求极致性能的项目(业务量少)
④互联网项目(中小型企业,创业公司)
某网站平均一天下单量100w单,根据100w 评估一下系统的流量!
①产生的时间段:11:00 – 2:00 5:00 – 12:00 ,订单产生时间段:12h。
②每下一单会发生多少个请求:50QPS x 3 = 150 QPS。
100w / day * 150 QPS = 1.5 亿 ----- 亿级流量。
1.5亿/12 h = 1250 QPS / 60min = 20W / 60s = 3400 QPS。
(4)单点架构优缺点。
单体架构优点:
①部署简单
②开发简单
③测试简单
④集群简单
⑤RT响应时间非常快速 —— 适合一些特点的项目(极端苛刻响应时间)。
①流量比较集中,所有的请求都集中一个服务中,单体无法应对
②无法实现敏捷开发,业务增大,代码结构越来越臃肿,维护变得非常困难单体架构:war > 1G --- IBM unix 高性能服务器 64cpus, 128GB --- 1GB
③单体架构牵一发而动全身
④扩展性差
⑤稳定性差
随着业务流量增大,需求的增多,必须对架构进行改进,就需要对项目进行业务拆分;(水平拆分,垂直拆分)。
数据库水平拆分,垂直拆分模式:
(1)水平拆分模式。
(2)垂直拆分:SOA架构。
注意:微服务架构就是水平拆分和垂直拆分的架构结合,就是微服务架构。
ServiceMesh服务网格架构,CNCF把ServiceMesh定义为云原生架构,ServiceMesh落地级实现的成熟框架:Istio框架。
问题:为什么要是有ServiceMesh架构?
Spring Cloud alibaba微服务架构存在问题?
--ServiceMesh出现就是为了解决微服务架构中存在一些问题?
①服务性能监控(Zabbix,promutheus)2、服务限流(sentinel)
②服务降级(sentinel)
③服务熔断(sentinel)
④链路追踪(skywalking)
⑥日志监控(elk)
⑦服务告警
⑧负载均衡
以上一系列的问题,作为架构师,开发人员都需要全盘的考虑;开发微服务架构在服务治理,服务监控非常困难。
以上的工作和业务没有太多的关系,但是架构人员必须考虑,架构,设计,因此这些配套工作都会大大降低我们的开发效率,提升开发难度,增加开发成本。
Serverless架构体系:无服务架构,面向未来的架构体系,从开发人员来说,不需要关心底层哪些和业务没有关系的代码,只需要开发业务即可。
例如:向CDN上传图片,视频文件。
①不需要上传到哪一个服务器
②不需要关心服务器是如何扩容的
这样的概念,思想就叫做Serverless。
总结:架构选型的时候,必须选择企业合适的架构,而不是采用最新架构。
思考题1
项目上线后,是什么原因促使必须进行jvm调优?
答案:调优的目的就是提升服务性能。
(1)jvm 堆内存空间对象太多(Java线程,垃圾对象),导致内存被占满,程序跑不动—性能严重下降。
调优:及时释放内存
(2)垃圾回收线程太多,频繁回收垃圾(垃圾回收线程也会占用内存资源,抢占cpu资源),必然会导致程序性能下降。
调优:防止频繁gc。
(3)垃圾回收导致stw(stop the world)。
调优:尽可能的减少gc次数。
思考题2
jvm调优本质是什么?
答案:jvm调优的本质就是(对内存的调优) 及时回收垃圾对象,释放内存空间;让程序性能得以提升,让其他业务线程可以获得更多内存空间;
思考题3
是否可以把JVM内存空设置的足够大(无限大),是不是就不需要垃圾回收呢?
前提条件:内存空间被装满了以后,才会触发垃圾回收器来回收垃圾。
答案:理论上是的,现实情况不行的!
(是否有这么大的空间)。
32位操作系统 === 4GB 内存。
64位操作系统 === 16384 PB 内存空间。
Jvm堆内存空间大小的设置:必须设置一个合适的内存空间,不能太大,也不能太小。
问题1:考虑到寻址速度的问题,寻址一个对象消耗的时间比较长的。
问题2:一旦触发垃圾回收,将会是一个灾难;(只能重启服务器)。
(1) gc的时间足够小(堆内存设置足够小)。
垃圾回收时间足够小,以为着jvm堆内存空间设置小一些,这样的话 垃圾对象寻址的时候消耗的时间就非常短,然后整个垃圾回收非常快速。
(2) gc的次数足够少 (jvm堆内存设置的足够大)。
Gc次数足够少,jvm堆内存空间必须设置的足够大;这样垃圾回收触发次数就会相应减少。
注意:原子1 ,原则2 相互冲突的,原则1&&原则2 。需要进行balance,内存空间既不能设置太大,也不能设置太小。
(3) 发生fullgc 周期足够长 (最好不发生full gc)。
metaspace 永久代空间设置大小合理,metaspace一旦扩容,就会发生fullgc。
老年代空间设置一个合理的大小,防止full gc。
尽量让垃圾对象在年轻代被回收(90%)。
尽量防止大对象的产生,一旦大对象多了以后,就可能发生full gc ,甚至oom。
JVM调优的本质:回收垃圾,及时释放内存空间。
但是什么是垃圾?
在内存中间中,哪些没有被引用的对象就是垃圾(高并发模式下,大量的请求在内存空间中创建了大量的对象,这些对象并不会主动消失,因此必须进行垃圾回收,当然Java垃圾回收必须我们自己编写垃圾回收代码,Java提供各种垃圾回收器帮助回收垃圾,JVM垃圾回收是自动进行的)。
一个对象的引用消失了,这个对象就是垃圾,因此此对象就必须被垃圾回收器进行回收,及时释放内存空间。
Jvm提供了2种方式找到这个垃圾对象:
(1)引用计数算法 找垃圾。
(2)根可达算法 找垃圾 hotspot 垃圾回收器都是使用这个算法。
引用计数算法:对每一个对象的引用数量进行一个计数,当引用数为0时,那么此对象就变成了一个垃圾对象。
存在问题:不能解决循环引用的问题,如果存在循环引用的话,无法发现垃圾。
这三个对象处于循环引用的状态,引用计数都不为0,因此无法判断这个3个对象是垃圾。
根据根对象向下进行遍历,如果遍历不到的对象就是垃圾。
JVM提供了3种方式清除垃圾,分别是:
①mark-sweep 标记清楚算法。
②copying 拷贝算法。
③mark-compact 标记整理(压缩)算法。
①第一种算法:mark-sweep 标记清楚算法。
①使用根可达算法找到垃圾对象,对垃圾对象进标记 (做一个标记)。
②对标记对象进行删除(清除)。
优点:简单,高效。
缺点:清除的对象都不是一个连续的空间,清除垃圾后,产生很多内存碎片;不利于后期对象内存分配,及寻址。
②第二种算法:copying拷贝算法。
一开始就把内存控制一份为2,分为2个大小相同的的内存空间,另一半空间展示空闲:
①选择(寻址)存活对象。
②把存活对象拷贝到另一半空闲空间中,且是连续的内存空间。
③把存储对象拷贝结束后,另一半空间中全是垃圾,直接清除另一半空间即可。
优点:简单,内存空间是连续的,不存在内存空间碎片。
缺点:内存空间浪费。
③第三种算法:mark-compact标记整理(压缩)算法。
①选择(寻址)存活对象。
②把存活对象拷贝到另一半空闲空间中,且是连续的内存空间。
③把存储对象拷贝结束后,另一半空间中全是垃圾,直接清除另一半空间即可。
Java提供很多的垃圾回收器:10种垃圾回收器。
特点:
1、Serial Serial Old , parNew CMS , Parallel Scavenge Parallel Old 都属于物理分代垃圾回收器;年轻代,老年代分别使用不同的垃圾回收器。
2、G1 在逻辑上进行分代的,进行在使用上非常方便,关于年轻代,老年代只需要使用一个垃圾回收器即可。
3、ZGC ZGC是一款JDK 11中新加入的具有实验性质的低延迟垃圾收集器。
4、Shenandoah OpenJDK 垃圾回收器。
5、Epsilon 是Debug使用的,调试环境下:验证jvm内存参数设置的可行性。
6、Serial Serial Old:串行化的垃圾回收器。
7、parNew CMS :并行,并发的垃圾回收器。
8、Parallel Scavenge Parallel Old :并行的垃圾回收器。
常用的垃圾回收器组合:
1、Serial + Serial Old: 串行化的垃圾回收器,适合单核心的cpu的服务情况。
2、parNew + CMS:响应时间优先组合。
3、Parallel Scavenge + Parallel Old :吞吐量优先组合。
4、g1 :逻辑上分代的垃圾回收器组合。
Serial : 年轻代的垃圾回收器,单线程的垃圾回收器;Serial Old是老年代的垃圾回收器,也是一个单线程的垃圾回收器,合适单核心cpu。
1、stw : 当进行gc的时候,整个业务线程都会被停止,如果stw时间过长,或者stw发生次数过多,都会影响程序的性能。
2、垃圾回收器线程:多线程,单线程,并发,并行。
Parallel Scavenge + Parallel Old
Parallel Scavenge + Parallel Old :
并行的垃圾回收器;吞吐量优先的垃圾回收器组合,是JDK8默认的垃圾回收器;问题 : 什么是并发,并行?
PS + PO 回收垃圾的时候,采用的多线程模式回收垃圾。
1、stw : 当进行gc的时候,整个业务线程都会被停止,如果stw时间过长,或者stw发生次数过多,都会影响程序的性能。
2、垃圾回收器线程:多线程,单线程,并发,并行。
parNew : 并行垃圾回收器,年轻代的垃圾回收器。
CMS : 并发垃圾回收器,回收老年代的垃圾。
年轻代垃圾回收器:parNew。
老年代垃圾回收器:CMS。
注意:任何的垃圾回收器都无法避免 STW ,因此jvm调优实际上就是调整stw的时间。
使用G1收集器时,它将整个Java堆划分成约2048个大小相同的独立Region块,每个Region块大小根据堆空间的实际大小。
而定,整体被控制 在1MB到32MB之间,且为2的N次幂,即1MB,2MB,4MB,8MB,16MB,32MB。可以通过-XX:G1HeapRegionsize设定。
所有的Region大小相同,且在JVM生命周期内不会被改变。
通过内存分代模型结构:大多数对象都会在年轻代被回收掉(90%+),很多对象都在15次的垃圾回收中被回收掉了,只有超过15次还没被回收掉的才会进入到老年代区域。
1、ps+po : 当堆内存被装满了,才会触发垃圾回收(eden区域满了,触发了垃圾回收,old区域满了,触发垃圾回收)。
2、cms 垃圾回收器。
①JDK1.5:68% ,当eden区域装对象达到68%时候,就会触发垃圾回收。
②JDK1.6+ : 92%才会触发垃圾回收器。
一个新对象被创建了,但是这个对象是一个大对象(查询全表),eden区域已经放不下了,此时会发生什么?
明确:jvm调优本质
1、JVM调优本质就是 gc , 垃圾回收,及时释放内存空间。
2、gc次数要少,gc时间少,防止fulllgc --- 内存参数设置。
服务器硬件配置:4cpu,8GB内存 --- jvm调优内存,考虑内存。
1、-Xmx4000m 设置JVM最大堆内存(经验值:3500m – 4000m,内存设置大小,没有一个固定的值,根据业务实际情况来进行设置的,根据压力测试,根据性能反馈情况,去做参数调试)。
2、-Xms4000m 设置JVM堆内存初始化的值,一般情况下,初始化的值和最大堆内存值必须一致,防止内存抖动。
3、-Xmn2g 设置年轻代内存对象(eden,s1,s2)。
4、-Xss256k 设置线程栈大小,JDK1.5+版本线程栈默认是1MB, 相同的内存情况下,线程堆栈越小,操作系统创建的线程越多。
nohup java -Xmx4000m -Xms4000m -Xmn2g -Xss256k -jar jshop-web-1.0-SNAPSHOT.jar --spring.config.addition-location=application.yaml > jshop.log 2>&1 &
压力测试:查看在此内存设置模式下性能情况:
根据压力测试结果,发现JVM参数设置,和之前没有设置吞吐能力没有太大的变化,因为测试样本不足以造成 gc,fullgc时间上差异。
问题:根据什么标准判断参数设置是否合理呢??根据什么指标进行调优呢?1、发生几次gc, 是否频繁的发送gc?2、是否发生fullgc ,full gc发生是否合理3、gc的时间是否合理4、oom。
输出日志启动指令如下所示:
nohup java -Xmx4000m -Xms4000m -Xmn2g -Xss256k -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:gc.log -jar jshop-web-1.0-SNAPSHOT.jar --spring.config.addition-location=application.yaml > jshop.log 2>&1 &
输出日志指令:
-XX:+PrintGCDetails 打印GC详细信息
-XX:+PrintGCTimeStamps 打印GC时间信息
-XX:+PrintGCDateStamps 打印GC日期的信息
-XX:+PrintHeapAtGC 打印GC堆内存信息
-Xloggc:gc.log 把gc信息输出gc.log文件中
执行启动指令后,在本地产生gc.log文件:
GC日志分析: 使用https://gceasy.io/导入gc.log 进行在线分析即可。
Gc日志分析报告:
总结:可以发现 业务线程执行时间占比达到99%+,说明gc时间在整个业务执行期间所占用的时间非常少,几乎不会影响程序性能;导致业务线程执行时间占比高的原因是:
1、程序样本数不够。
2、程序运行的时间不够。
3、业务场景不符合要求(查询没有太多的对象数据)。
存在问题:发生full gc。
GC详细数据分析:
查询gc内存模型:jstat -gcutil PID 查询此进程的内存模型。
Metaspace永久代空间:默认为20m(初始化大小);当metaspace被占满后,就会发生扩容,一旦metaspace发生一次扩容,就会同时发送一次fullgc 。
Sun公司推荐设置:年轻代占整个堆内存 3/8。
发现full gc 已经没有发生了。
Sun公司推荐设置:整个堆的大小=年轻代 + 老年代 + 永久代(256m)年轻代占整个堆内存3/8 , -Xmx4000m , 因此整个堆内存设置大小为4000m,也就是说年轻代大小应该设置为1.5G:
①定义年轻代:-Xmn1500m,剩下的空间就是老年代的空间。
②参数:-XX:NewRatio = 4 表示年轻代(eden ,s0,s1) 和老年代区域所占比值 1:4。
年轻代大小,老年代大小比值根据业务实际情况设置比例,(通过设置相应的比例:减少相应yonggc ,fullgc)。
JVM调优的原则中:要求尽量防止fullgc的发生;因此可以把fullgc设置的稍微大一些;以为old区域装载对象很长时间才能装满(或者永远都装不满),发生fullgc概率就非常小。
官方给定设置:可以设置eden,s区域大小:8:1:1 à -XX:SurvivorRatio = 8。
此调优的原理:尽量让对象在年轻代被回收;调大了eden区域的空间,让更多对象进入到eden区域,触发gc时候,更多的对象被回收。
nohup java -Xmx4000m -Xms4000m -Xmn1500m -Xss256k -XX:MetaspaceSize=256m -XX:SurvivorRatio=8 -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:gc.log -jar jshop-web-1.0-SNAPSHOT.jar --spring.config.addition-location=application.yaml > jshop.log 2>&1 &
可以发现业务占比时间发送提升,说明gc时间更少了。
总结:JVM调优(调整内存大小、比例) 降低 gc次数,减少gc时间,从而提升服务性能。
调优标准:项目上线后,遇到问题,调优。
1、gc消耗时间 –业务时间占比。
2、频繁发生fullgc – 调优 – stw—程序暂停时间比较长,阻塞,导致整个程序崩溃。
3、oom --- 调优。
并行的垃圾回收器:parallel scavenge(年轻代) + parallel old(老年代) ---- 是JDK默认的垃圾回收器。
nohup java -Xmx4000m -Xms4000m -Xmn1500m -Xss256k -XX:MetaspaceSize=256m -XX:SurvivorRatio=8 -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:gc.log -jar jshop-web-1.0-SNAPSHOT.jar --spring.config.addition-location=application.yaml > jshop.log 2>&1 &
显式的配置PS+PO垃圾回收器:-XX:+UseParallelGC -XX:+UseParallelOldGC。
并行垃圾回收器(年轻代),并发垃圾回收器(老年代) :ParNew + CMS (响应时间优先垃圾回收器)。
nohup java -Xmx4000m -Xms4000m -Xmn1500m -Xss256k -XX:MetaspaceSize=256m -XX:SurvivorRatio=8 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:gc.log -jar jshop-web-1.0-SNAPSHOT.jar --spring.config.addition-location=application.yaml > jshop.log 2>&1 &
显式配置:parNew+CMS垃圾回收器组合:-XX:+UseParNewGC。
-XX:+UseConcMarkSweepGC。
说明:CMS只有再发生fullgc的时候才起到作用,CMS一般情况下不会发生;因此在jvm调优原则中表示尽量防止发生fullgc; 因此CMS在JDK14被已经被废弃。
G1垃圾回收器是逻辑上分代模型,使用配置简单。
nohup java -Xmx4000m -Xms4000m -Xmn1500m -Xss256k -XX:MetaspaceSize=256m -XX:SurvivorRatio=8 -XX:+UseG1GC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:gc.log -jar jshop-web-1.0-SNAPSHOT.jar --spring.config.addition-location=application.yaml > jshop.log 2>&1 &
经过测试,发现g1 gc次数减少,由原来的28次减少为21次,但是gc总时长增加很多;时间增加,以为着服务性能就没有提升上去。
(1)避免网页出现错误。
(2)增加数据库稳定性。
(3)优化用户体验。
1、低效的SQL语句。
2、并发cpu问题(SQL语句不支持多核心的cpu并发计算,也就是说一个SQL只能在一个cpu执行结束)3、连接数:max_connections。
4、超高cpu使用率。
5、磁盘io性能问题6、大表(字段多,数据多)。
7、大事务。
数据库数据处理(困难):数据库扩容非常困难—想要通过扩容提升数据库性能Web服务器扩容是非常简单的。
web服务器是无状态服务,可以随时进行扩容;但是数据库不能随意进行扩容,一旦扩容就会影响数据完整性,数据一致性;项目架构中提升性能:
1、对项目架构、业务,缓存各方面进行优化,真正数据库请求比较少—减少数据库压力。
2、数据库设计,架构,优化。
大多数企业:数据库采用主从架构解决问题;数据分表,分库,数据归档数据,能热分离。
datasource:
#url: jdbc:mysql://127.0.0.1:3306/shop?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
url: jdbc:mysql://XX.XX.XX.XX:3306/shop?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&connectionTimeout=3000&socketTimeout=1200
网页题目:基于互联网架构演进,构建秒杀系统
文章源于:http://www.mswzjz.cn/qtweb/news27/117177.html攀枝花网站建设、攀枝花网站运维推广公司-贝锐智能,是专注品牌与效果的网络营销公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 贝锐智能