服务端质量保障之内存管理(一)

2023-10-22 19:06:13 浏览数 (1)

对一个系统而言,保持一个系统能够持续稳定地提供服务的能力而言显得尤为重要。我们常常谈用户体验,其实良好的用户体验不仅仅指的是用户交互以及系统的易用性,也包含了系统可持续提供服务的能力。作为质量交付团队,不仅仅需要考虑被测对象背后的业务价值和给用户带来商业上的赋能,也需要考虑提供业务背后的底层服务的计算能力,因为底层服务的稳定性才能够保障上层应用的产品业务特性,以及业务带来的商业价值。

当雪崩的时候,没有一片雪花是漂亮。

因此,服务端质量保障就显得尤为重要了。

服务端涉及到的知识体系包含了针对内存、数据引擎、计算资源、数据库访问隔离、计算资源优先级以及等等的知识。本文章主要阐述下内存管理的部分,其他部分后续文章逐步的更新。

在应用程序中都会涉及到文件的读写操作,这个过程很多时候都会使用到内存缓存。内存缓存本质上就是计算机的内存。在计算机中的应用程序从启动到运行的过程中,会把数据从计算机中的外存数据加载到计算机的内存缓存中,最终CPU从内存缓存中加载数据进行计算,使用内存缓存的优势具体总结如下。

  • 提升数据的I/O速度
  • 提升系统的性能
  • 保护后台系统稳定

使用内存缓存的优势是显而易见的,但是如果内存使用不当,带来的后果也是非常严重的,甚至某些时候是灾难级的。如在疫情期间,XX城市核酸检测系统大面积崩溃,其实本质上就是内存管理不当最终导致了缓存穿透,然后导致系统无法正常地响应来自客户端的请求。下面针对内存泄漏、内存溢出、缓存穿透、缓存雪崩进行详细的阐述。

内存泄露

内存泄露指的是程序创建了太多的对象并且都分配了内存资源,在运行的过程中,没有释放内存资源导致内存泄露。

内存溢出

内存溢出指的是程序在执行的过程中内存不够向计算机进行申请的时候,但是计算机没有足够的内存分配给当前运行的程序使用,最终导致内存溢出。很多时候,导致内存溢出主要是如下几个方面,具体如下。

  • 内存中加载了太多的数据量来进行I/O,导致超过内存剩余空间
  • 计算机运行的程序申请内存使用后,没有正常释放,由内存泄露最终导致内存溢出
  • 程序存在死循环或者是循环次数太多,产生了过多重复的实例。

计算机的资源是有限的,任何在计算机运行的程序计算资源都需要进行适当的分配。如在一个SAAS的系统中,文件上传服务需要处理来自众多客户端上传文件的请求,那么这个过程就会消耗内存。那么最初针对文件上传的服务分配多少内存资源合理呢?这就需要结合实际的业务场景进行分析和进行性能压力测试后才能够给出合理的值。如业务诉求是最大上传的文件大小是500M,并且满足N个文件同时上传的要求。此时,作为质量交付团队就需要验证验证这部分。假设最初服务分配的是2G的内存资源,下来就需要质量交付团队根据业务的诉求来验证2G的内存资源第一是否满足业务的诉求,第二在满足业务诉求的基础上是否会内存泄露的情况。

在实际企业的案例中,经常会遇到服务端响应慢,或者是OOM的情况。此时就需要分析.hprof的文件是什么原因导致了内存泄漏。下面我结合一个实际的案例来演示内存泄露后如何自动的获取到.hprof文件和打印出详细的GC日志,案例代码如下。

代码语言:javascript复制
@RestController
public class HeapController
{
  @RequestMapping("/heap")
  public void   heap ()
{
    List<Person> lists=new ArrayList<Person>();
    while (true)
    {
      int i=0;
      lists.add(new Person(i  ,UUID.randomUUID().toString()));
    }
  }
}

应用程序启动时在IDEA配置的关于GC与内存泄漏后的文件命令如下

代码语言:javascript复制
-Xmx32M
-Xms32M
-XX: PrintGCDetails
-XX: PrintGCTimeStamps
-XX: PrintGCDateStamps
-Xloggc:./gc.log
-XX: HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=./

执行应用程序,然后进行调用,内存泄露后,就会显示出内存泄露的错误日志信息,如下是内存泄露后IDEA显示的错误日志:

代码语言:javascript复制
java.lang.OutOfMemoryError: GC overhead limit exceeded

如下图是内存泄露后自动获取到的.hprof文件和GC日志,具体如下:

.hprof的文件分析可以使用MAT的工具来进行分析,GC的日志分析会显示出GC吞吐(73.941%)与GC停顿时间(平均37.6ms,最大是150ms),详细数据具体如下图所示。

缓存穿透

缓存穿透指的是客户端向服务端查询数据请求,查询的数据在内存和持久层都不存在,而内存并没有起到数据缓存的意义。特别是在被测试的服务在数据处理上使用了缓存机制,需要特别的关注下缓存穿透的问题,那么可以在测试策略上进行这样设计,如客户端发送请求到持久层,持久层没有查询到数据,那么在缓存中存入空值,而且在内存中的空值有效期设置短一些,让客户端在持久层添加或者查询数据,以保证系统的正常运行。

缓存雪崩

缓存雪崩指的是内存缓存出现异常,所有的数据查询请求全部查询持久层,数据库无法正常响应,最后导致数据库卡死。缓存雪崩的核心并不是内存缓存出现异常导致,而是系统在整个架构设计上设计不合理导致,系统架构的设计需要考虑客户端的高并发情况下系统如何稳定的持续提供服务。那么需要引入以及考虑调度设计、任务管理、队列设计、通信模式等信息。

系统的核心是什么?是稳定,是可以持续稳定地提供服务的能力,需要考虑各种异常情况下系统的容错机制和错误处理的能力,以及在出现系统故障的情况下也能够切换到新的服务来保障客户对系统的正常使用。

感谢您的阅读。如果您想系统的学习Python语言、接口测试框架设计以及企业落地、CI&CD持可持续交付、服务端性能测试、主流性能测试工具案例实战、微服务架构、MQ中间件以及混沌工程,点击原文加入课程。

0 人点赞