支付系统案例
如果有一个电商系统
支付系统所处的位置
用户 >>> 下单
用户 >>> 支付
支付 >>> 调用第三方支付通道
只要网购过,这个流程都很清楚
就是对一个商品加入购物车
然后下个订单
接着对订单结算
大致如此
通过上面的流程
那么一个每日百万交易系统的压力瓶颈在那呢
最核心的就是在用户发起支付请求的时候,会生成一个订单
这个订单需要清楚的记录是谁发起的
对那个商品支付
什么渠道支付
还有支付时间
从JVM角度来讲
日交易量百万
就是需要在JVM中创建一百万个支付订单对象
所以它的压力有方方面面
并发、高性能处理请求、大量订单数据存储
抛开这些技术架构层面的问题
单单是在JVM层面
支付系统最大的压力来自于频繁的 创建 /销毁 百万个对象
那么就涉及到一个核心的问题
多少机器合适
机器内存空间多少
每台机器的堆内存多少合适
给多大的空间才能保证这么多订单的创建,而不会因为内存不够导致系统之间崩溃
解决线上最核心的一个问题
就是堆内存设置多少合适
首先要计算的是,每秒钟处理多少订单合适
假设 100W 订单 的高峰期是每天的 中午或者晚上
假设每天的高峰期是几个小时,用100W平均分配到几个小时里
那就是JVM每秒处理100个左右的样子
假设我们的支付系统部署了3台机器
那就是每台机器实际上每秒处理30笔订单
假设一次支付请求,包含一个支付订单的创建,大概需要1秒钟
那么每台机器在1秒钟接收到30笔订单支付请求
然后在JVM新生代里创建了30个支付订单的对象,做了写入数据库等处理
接着1秒后,这些订单处理完毕
新生代的这些支付订单对象就成了没人引用的垃圾对象
接着又是下一个1秒
如此循环往复
比如订单对象有3个字段
Integer userID 4个字节
Long orderTime 8个字节
Integer orderId 4个字节
一般订单这种核心类有20个变量,就按20个实例变量来算
然后一个对象就几百字节的样子
算大一点 500字节 ,不到1kb
3台机器,每秒钟30个
30*500=15000字节,大概15kb
1秒30个,处理完毕就没人引用了
循环往复
直到某一刻,新生代里都有几十个W个对象了
此时占据了几百M的空间
然后新生代可能快满了
然后就会触发 Minor GC ,就把新生代的内存给回收掉,腾出内存空间
然后继续分配新对象
预估的话一般会把之前的结果 放大 10 ~ 20 倍
也就是说每秒除了订单对象,还有数十种其余对象
那么每秒创建的对象占用空间可能就几百K到1M之间
然后如此循环往复
接着触发 Minor GC
其实一般来说这种线上业务系统,常见的机器配置是2核4G,或者是4核8G
如果我们用2核4G的机器来部署,那么还是有点紧凑的,因为机器有4G内存,但是机器本身也要用一些内存空间,最后你的JVM进程最多就是2G内存
然后这2G还得分配给方法区、栈内存、堆内存几块区域,那么堆内存可能最多就是个1G多的内存空间。
然后堆内存还分为新生代和老年代,你的老年代总需要放置系统的一些长期存活的对象吧,怎么也得给几百MB的内存空间,那么新生代可能也就几百MB的内存了。
这样的话,大家可以看到,我们上述的核心业务流程,只不过仅仅是针对一个支付订单对象来分析的,但是实际上如果扩大10倍~20倍换成对完整系统的预估之后,我们看到,大致每秒会占据1MB左右的内存空间。
那么如果你新生代就几百MB的内存空间,是不是会导致运行几百秒之后,新生代内存空间就满了?此时是不是就得触发Minor GC了?
其实如果这么频繁的触发Minor GC,会影响线上系统的性能稳定性的。
这里大家首先要明白的一点,就是频繁触发GC一定不是什么好事儿。
因此你可以考虑采用4核8G的机器来部署支付系统,那么你的JVM进程至少可以给4G以上内存,新生代在里面至少可以分配到2G内存空间
这样子就可以做到可能新生代每秒多1MB左右的内存,但是需要将近半小时到1小时才会让新生代触发Minor GC,这就大大降低了GC的频率。
举个例子:机器采用4核8G,然后-Xms和-Xmx设置为3G,给整个堆内存3G内存空间,-Xmn设置为2G,给新生代2G内存空间。
而且假设你的业务量如果更大,你可以考虑不只部署3台机器,可以横向扩展部署5台机器,或者10台机器,这样每台机器处理的请求更少,对JVM的压力更小。