刚刚我们谈到nginx不同的worker进程间需要共享信息的时候,需要通过共享内存;我们也谈到了共享内存上可以使用链表或者红黑树这样的数据结构;但是每一个红黑树上有许多节点;每一个节点你需要分配内存去存放;那么怎么样把一整块共享内存切割成一小块给红黑树上的每一个节点使用呢?
下面我们来看下Slab内存分配管理是怎么样应用于共享内存上的;首先我们来看下Slab内存管理是怎么样的一种形式;
它首先会把整块的共享内存分为很多页面;那么每个页面例如4k;会切分为很多slot; 比如32字节是一种slot; 64字节又是一种slot; 128字节又是一种slot; 那么这些slot是以乘2的方式向上增长的; 如果现在有一个51字节需要分配的内存会放到哪里呢?会放于小于它最大的一个slot的一个环节; 比如说64字节;所以上图中slot就是指向不同大小的块;所以这样的一种数据结构呢?它有一个特点;会有内存的浪费的。 就像我们刚刚所说的: 51字节它会用64字节来存放;那么其他的13字节就浪费了;那么最多会有多少消耗呢?还有两倍: 这种使用的方式叫做Bestfit; Bestfit这种分配内存的方式有什么好处呢? 它适合小对象; 如果我们要分配的对象的内存非常小, 比如小于一个页面的大小,就非常合适;因为它很少有碎片,那么每分配一块内存,就会沿着还未分配的空白的地方继续使用就可以了;当一个页面使用满以后,我再拿一个空白的页面继续给此类slot大小的内存继续使用就可以。那么有时候我分配在某段内存上的数据结构它是固定的,甚至需要初始化;那么这样的话,原先的数据结构都还在;当我重复使用的话,也避免了初始化;Slab内存管理中,我们怎么做数据的监控和统计呢?
那么tng上有一个模块叫做slab_stat; slab_stat可以帮我们看不同的slot:
比如说: 8字节 16字节 ......等等
一共目前分配了多少,使用了多少,有多少个请求在访问,失败了多少次,这个对我们来监控Slab是非常有用处的;
下面我们来看下怎么样在openresty的场景下去使用tng上的slab_stat这个模块;
首先我们打开tengine的页面 http://tengine.taobao.org/document/ngx_slab_stat.html
但是会发现在这个模块上没有github的地址;也就是说它没有作为一个独立的模块提供出来;那这个时候该怎么办呢?
那么tengine怎么下载下来?从download里:
解压后进入目录查看多个modules目录:
modules里有个ngx_slab_stat
再进入查看
可以看到
这是一个标准的nginx第三方模块;因为每一个nginx第三方模块会通过一个c文件定义好我们之前所说的ngx_module_t这么一个结构体;
以及处理哪些配置项 ,提供哪些变量;并有一个config来帮助他编译到我们的目标nginx中 ;现在我们再回到 openresty中,让openresty编译的时候,把tengine的slab_stat模块编译进去;然后再使用openresty上的share_direct去分配共享内存;再用slab_stat查看我们共享内存的使用情况;
我们执行configure的时候,可以添加一个参数叫 --add-module=;--add-module=这个命令尼 可以将一个目录下具备config的这样一个配置项或者目录添加到我们的nginx中,它的方式尼 也就是将这个模块的nginx源码能够使我们的./configure 识别到;所以这里的configure和我们的官方configure是一样的,那么我们现在开始编译openresty;
编译完成以后开始执行make命令:
如果之前已经安装了openresty或者(nginx)
执行编译make 好以后不需要make install
(1). 备份原来的nginx 可执行文件
cd /usr/local/openresty/nginx/sbin
cp nginx nginx.old
(2) 将编译好的nginx可执行二进制文件复制到原始nginx的sbin目录
cp /home/web/openresty-1.13.6.2/build/nginx-1.13.6/objs/nginx /usr/local/openresty/nginx/sbin/ -f
(3) 验证是否成功安装ngx_slab_stat
cd /usr/local/openresty/nginx/sbin
./nginx -V
现在openresty已经安装完成了,这个openresty中,已经包含了ngx_slab_stat模块;
我们以上一节中lua_shared_dict 那段代码的例子,来讲解下slab_stat是怎么使用的?
首先我们用 lua_shared_dict_dogs 10m; 分配了一个10m的共享内存,名字叫dogs;
使用的话,我们有一个set的url下;设置了一个key-value: Jim-8;设置在我们的共享内存下,并返回给用户一个字符串叫STORED;
当收到get这个url请求时,会从dogs共享内存中取出Jim的值;也就是这里设置的8返回给用户:
然后我们又增加了一个location,这个location叫做slab_stat,它里面的内容就是tengine中slab_stat提供的slab_stat配置项,这个配置项叫做slab_stat.
它会返回我们slab的所有的统计状况:
nginx.conf 配置文件代码:
lua_shared_dict dogs 10m;
server {
listen 8080;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location = /slab_stat{
slab_stat;
}
location /set{
content_by_lua_block{
local dogs=ngx.shared.dogs
dogs:set("Jim",8)
ngx.say("STORED");
}
}
location /get {
content_by_lua_block{
local dogs=ngx.shared.dogs
ngx.say(dogs:get("Jim"))
}
}
location / {
#proxy_set_header Host host; #proxy_set_header X-Real-IPhost; #proxy_set_header X-Real-IPremote_addr;
#proxy_set_header X-Forwarded-For proxy_add_x_forwarded_for; #proxy_cache my_cache; #proxy_cache_keyproxy_add_x_forwarded_for; #proxy_cache my_cache; #proxy_cache_keyhosturiuriis_args&args;
#proxy_cache_valid 200 304 302 1d;
#proxy_pass http://local;
}
配置项写完,把ngixn启动看看它的执行效果;
每一个slot及其slot对应的大小;分配了多少个,使用了多少个,失败了多少个;
所谓分配就是10m是一个非常大的共享内存,它会划分很多个页面;对于比较小的比如32字节,一个页面可以有128个;这里127可用,已经使用了一个;
总结:以上我们介绍了Slab内存的使用方法:slab使用了Bestfit思想,它也是Linux操作系统经常使用的内存分配方式;
那么通常我们在使用共享内存时,都需要使用slab_stat去分配相应的内存给对象,再使用上层的数据结构来维护这些数据对象;