NGINX 向云原生演进,All in OpenNJet
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,分配相应的内存
- 如果
shared_slab_pool_size > 10M
,按照实际指定的大小分配相应的内存
重启时(动态共享内存只增不减)
- 如果
new_shared_slab_pool_size , 保持原来的共享内存不变,并不进行缩减内存的操作。
- 如果
new_shared_slab_pool_size > old_shared_slab_pool_size
,计算差值diff_size
,如果diff_size ,
diff_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的大小能动态增加。当前实现中还没有考虑这些需求,后续开发过程中会根据实际情况,继续对动态共享内存功能进行增强。
NJet 应用引擎通过内核重构实现了独特的运行时 动态配置加载 能力,是 新一代高性能 Web 应用引擎 。NJet 拥有高性能数据面处理能力,将集群、高可用、主动健康检查、声明式 API 等多种辅助功能,通过 NJet 独特的副驾驶 CoPilot 服务框架调度,从而方便功能扩展,隔离管理 / 控制功能对数据面的影响,NJet 应用引擎性能超过 CNCF 推荐 Envoy 应用引擎的三倍。 官网 邮件组