MENU

记一次联飞服务线上OOM问题的排查

July 7, 2022 • 折腾

2018年至今,由自己主导开发的Rum联飞服务框架已经更新到了目前的3.1.3a了,最近几个版本主要都在解决一些隐性的堆内存、GC之类的问题。经典的联飞服务FSD,虽然在协议设计上沿用了上世纪八九十年代常见的PlainText类型的协议,但在内存管理上可以说是十分的精炼:平均内存占用基本在1-2MB左右,平均负载在0.01。

为什么说最近几个版本一直在解决隐性的内存管理的问题?Golang是一门自动处理垃圾回收的语言,在微小对象的回收处理上还有一些小问题,这会干扰我们对于内存泄漏的判断和跟踪。自3版本以来,Rum从原先2版本中的主服务配合PHP的webApi,转变为了服务编排、服务间广泛使用gRPC的形式。这对整个服务的复用、快速迁移、测试、服务质量跟踪等多个方面,都有十分显著的意义。

往往更多的功能会引入不确定因素。六月末、七月初的两次活动中,都出现了十分致命的OOM问题。整个Rum主服务内存占用以每秒0.5M-1M的速度增长。而负责服务发现数据交换的etcd服务也达到惊人的300+MB。

我们常见的服务器配置都是采用1:2的核心内存配比的,可以说是行业的基准选型,这次线上出问题的机器在资源规划上只需要1:1的资源即可,但由于生产环境,还是留了很大裕度。

etcd服务在大量节点接入写入时,会出现内存增长+负载上升的情况,这主要和节点选举、日志压缩这几方面有关。在Rum服务网络内,服务发现的QPS最高时段一般在服务编排启动以及部分灰度测试上下线的情况。

伴随着当时OOM的,还有多个服务的CPU竞争占用的问题,这导致在问题发生并影响用户侧时,已经是陷入十分严重的情况了,几乎不能直接介入处理。

出问题的代码如下:

func GetXClient_Etcd() client.XClient { //本写法会导致内存泄漏
        var err error
    etcdV3DiscoveryClient, err = etcd_client.NewEtcdV3Discovery(basePath,
        servicePath,
        []string{*config.RumConfig.EtcdServer},
        false,
        nil)
    if err != nil {
        panic(err)
    }
    return client.NewXClient(servicePath, client.Failtry, client.RandomSelect, etcdV3DiscoveryClient, client.DefaultOption)
}

我们可以看到etcdV3DiscoveryClient客户端出现了没有回收的情况,但这种写法并不一定在任何情况下都会导致OOM问题的发生,这往往和创建机制、引用类型、Golang垃圾回收原理有关联。

我们该如何做

写这篇文章主要是为了复盘问题。大公司是如何解决的呢?通常来说有非常庞大的质量跟踪体系。一开始只考虑到,对于Rum体系的开发,要做好代码评审、回测、压测。但在最近,回忆到了DockerComposer的资源限制功能。

我做了这样的一个配置

services:
  pergola:
    container_name: pergola
    networks:
      - rumServiceNetwork
    image: ***/rum:dev
    volumes:
      - ./pergola:/run/data
    ports:
      - 6809:6809
      - 6810:6810
    tty: true
    command: ***
    depends_on:
      - rum_plugin
      - etcd_rum
    restart: always
    deploy:
      resources:
        limits:
          cpus: '0.15'
          memory: 64M
        reservations:
          memory: 16M

可以注意到,最后对于资源有了一个很强的限制。我们可以评估好服务资源消耗量,并限定阈值。可以保证在一台机器上的多个服务群体互不干扰。

Archives QR Code
QR Code for this page
Tipping QR Code