NJet 动态共享内存功能

🙈 By 李崇民 2024-07-03

1. NJet共享内存简介

NJet中共享内存通常用于存储状态数据,以便在进程之间进行共享,这些空间通常是被频繁被申请和释放的。在NJet中,通过shm_zone结构来管理这些共享内存,每个zone维护一个slab_pool结构,使用slab算法来管理zone的共享内存。

在程序启动时,要预先在配置文件中定义好每个用途的共享内存(zone)的大小,然后使用slab算法来处理分配和释放小内存的请求。如果系统重启,master进程会检查配置文件中是否有增加新的zone,如果有就会重新申请一个共享内存给新zone。有多条指令可以配置zone,下面是一个gossip指令的示例。具体使用请参考使用手册。

stream {
        server {
                listen 238.255.253.254:5555 udp;
                gossip zone=test:1m heartbeat_timeout=20s nodeclean_timeout=30s;
        }
}

根据上面的配置文件,master进程会为gossip分配一个大小为1M,名称为test的zone来作为共享内存使用。在实际使用时,每个slab_pool有一个shmtx变量,用于对共享内存进行加锁和解锁操作。如果一个slab_pool中的内存已经用光,在没有释放足够的内存空间之前,后续请求内存的操作都只能失败,进而影响系统的性能。

2. 动态共享内存实现思路

要实现动态共享内存,可在系统中预先申请一个较大容量的共享内存,初始化成一个大的全局slab_pool, 后续可以从这个大slab_pool分配出多个较小的slab_pool,这样当一个zone的slab_pool内存用完之后,可向全局slab_pool申请一个新的和原来大小相同的slab_pool,并挂在zone的slab_pool队列尾部,这样就可以继续响应新的请求,提供足够的内存。

具体实现中,代码中主要修改了njt_init_cycle函数和njt_slab对应的文件。主要改动有

  • 增加一个新指令,标记新增共享内存的大小,在重启NJet时,可以调整这个值的大小(目前只支持增大)
  • 修改slab_pool的数据结构,使得slab_pool可以组成链表,并增加一个指向队列头的指针
  • 修改分配与释放slab_pool内存的代码,分配和释放都使用链表头的shmtx来实现加锁、解锁,并且会遍历链表查找合适的位置,来进行申请和释放内存的操作

2.1 新增指令

增加的指令shared_slab_pool_size, 可以指定预留的共享内存的大小。如果在reload时修改了这个值的大小,后面给出了判断的逻辑

Syntax shared_slab_pool_size;
Default 0
Context core

2.2 目前预留内存大小的判断

启动时

  • 如果shared_slab_pool_size 未配置或配置为0,刚不分配相应的内存
  • 如果shared_slab_pool_size < 10M,修改改为10M,分配相应的内存
  • 如果shared_slab_pool_size > 10M,按照实际指定的大小分配相应的内存

重启时(动态共享内存只增不减)

  • 如果new_shared_slab_pool_size <= old_shared_slab_pool_size, 保持原来的共享内存不变,并不进行缩减内存的操作。
  • 如果new_shared_slab_pool_size > old_shared_slab_pool_size,计算差值diff_size,如果 diff_size < 10Mdiff_size = 10M, 分配一块新的大小为diff_size的共享内存,挂在原来的预留内存队列的尾部。

当一个zone的内存出现no memory情况时,会从预留的共享中分配新的slab_pool(目前只支持大小与原zone的slab_pool.size相同),挂在zone的slab_pool的队列尾部,并尝试从新的slab_pool分配内存。如果预留内存空间不够,会同之前一样返回NULL,并在日志中增加相应记录。

3. 具体配置

# 加载其他模块

worker_processes  auto;
daemon off;
cluster_name helper;
node_name test_node1;

#shared_slab_pool_size 75M;
shared_slab_pool_size 5M;
error_log logs/error.log info;
#user root;
pid        /etc/njet/njet.pid;

events {
    worker_connections  1024;
}


4. 现存的问题及后续改进

目前对slab_pool列表申请和释放内存时,使用的是队列头所在的slab_pool的锁,这样当链表比较长时,会影响性能。另外现有实现中,没有考虑释放新分配的slab_pool的操作。最后,由于NJet中有多条命令支持使用zone,有一些zone可能不希望zone的大小能动态增加。当前实现中还没有考虑这些需求,后续开发过程中会根据实际情况,继续对动态共享内存功能进行增强。