1. 背景

电商系统里,支付系统的位置:

image.png

2. 支付的核心业务流程

image.png

支付系统还包含应用管理、账户管理、渠道管理、支付交易、对账管理、清算管理、结算管理等各种功能模块。

3. 分析系统压力

用户发起一个支付请求,会生成一个支付订单,比如一个PayOrder

每日要发生百万次交易,从JVM的角度来看,就是每天会在JVM中创建上百万个支付订单对象。

image.png

支付系统的压力有很多方面,包括高并发访问、高性能处理请求、大量的支付订单数据需要存储,等等技术难点。但是抛开这些系统架构层面的东西,单单是在JVM层面,我们的支付系统最大的压力,就是每天JVM内存里会频繁的创建和销毁100万个支付订单

核心问题:

  1. 多少台机器?配置?
  2. JVM堆内存大小?

4. 支付系统每秒需要处理多少笔支付订单

计算峰值:高峰期几个小时,100万支付订单,平均到每秒多少订单?

假设每秒100笔订单左右,部署4台机器,则每台机器每秒处理25笔订单,

image.png

5. 每个支付订单处理要耗时多久?

每台机器一秒钟接收到30笔支付订单的请求,然后在JVM的新生代里创建了30个支付订单的对象,做了写入数据库等处理
接着1秒之后,这30个支付订单就处理完毕,然后对这些支付订单对象的引用就回收了,这些订单在JVM的新生代里就是没人引用的垃圾对象了。

6. 每个支付订单大概需要多大的内存空间?

估算一个订单对象的大小:一个Integer类型的变量数据是4个字节,Long类型的变量数据是8个字节,

一般来说,比如支付订单这种核心类,你就按20个实例变量来计算,然后一般大概一个对象也就在几百字节的样子。我们算他大一点好了,就算一个支付订单对象占据500字节的内存空间,不到1kb。

7. 每秒发起的支付请求对内存的占用

30 * 1KB = 30KB

8. 让支付系统运行起来分析一下

业务系统运行模型:

  1. 系统每秒来30个支付请求,JVM创建30个支付订单对象,30kb。
  2. 1秒之后,这30个对象就没人引用了,就成为新生代里的垃圾,等待被回收。
  3. 系统持续创建30个支付订单对象,不停地在新生代中放入30个对象,新生代中的垃圾对象持续累加
  4. 直到某一刻,新生代快满了,触发MinorGC,回收掉新生代中的垃圾对象,腾出空间,继续在新生代中分配对象。

9. 对完整的支付系统内存占用需要进行预估

系统中除了核心的支付订单对象之外,还会有大量的其他对象。

怎么估算?

支付订单对象大小 * 10~20倍

几百kb~1M

10. 支付系统的JVM堆内存应该怎么设置?

常见的机器配置是2核4G,或者是4核8G。

如果选4G内存,JVM进程最多2G,这2G还得分配给方法区、栈内存、堆内存几块区域,那么堆内存可能最多就是个1G多的内存空间。堆内存还分新生代、老年代,则新生代最多就几百M。 按之前估算每秒产生1M大小的对象,那么相当于几百秒之后就进行MinorGC,GC有点频繁,会影响线上系统的性能稳定性。

可以考虑采用4核8G的机器来部署支付系统。

JVM进程可以分配到4G内存,新生代可以分配到2G 新生代每秒多1MB左右的内存,需要将近半小时到1小时才会让新生代触发Minor GC,大大降低了GC的频率 可以这样设置:-Xms3G -Xmx3G -Xmn2G

业务量增大,可以水平扩展(加机器)

11. 合理设置方法区

几百M,够用

12. 合理设置栈

默认就行,Linux/64 默认1024kb

11. 不合理的设置内存

流量暴增 —> 系统性能急速下降,响应、处理变慢 —> 少数请求需要几十秒处理 —> 老年代内存占用变大 —> 频繁FGC

12. 总结

  1. 抽象核心业务流程,预估系统业务量、访问量
  2. 分析系统压力,推算峰值的每秒并发量
  3. 计算每秒请求对内存的占用,即每秒钟产生对象的大小
  4. 选择合理的机器配置,设置合理的堆内存
  5. 不合理的预估业务系统压力,不合理的设置内存大小,就可能会导致很大的问题
  6. 上线新系统的时候,应该对系统压力作预估,JVM内存、磁盘、带宽、数据库压力等给出合理的配置