【充电】《Nginx核心知识100讲》nginx 共享内存
所有worker进程协同工作的关键:共享内存
1.nginx 进程间的通讯方式
image.pngnginx进程间通讯有两种,信号跟共享内存,如果要做数据的同步只能用共享内存。
共享内存是nginx 跨worker通讯最有效的手段。只要我们需要一段业务逻辑在多个worker中同时生效,比如说做集群的流控上,必须使用共享内存,而不是在每一个worker的内存上操作。
2.锁
多个worker之间访问内存就会导致竞争所以我们需要加锁。
nginx早期还有关于信号量的锁。信号量是linux比较久远的进程同步方式。会导致进程进入休眠状态。现在大多数nginx版本中使用的都是自旋锁。而不会基于信号量。自旋锁,当这个锁的条件没有满足,这块内存被1号worker使用,2号worker进程获取锁的时候,只要1号进程没有释放锁,2号进程会一直在不听的请求这把锁。就好像如果是基于信号量的,早期nginx锁,假设锁住了一扇门,worker 进程1拿上了锁进入物理,worker 2发现里面有人,worker 2就会就地休息 等待woker进程1从门里出来后通知它。自旋锁不一样,woker 2会持续敲门,使用自旋锁就要求nginx模块必须快速的使用共享内存。也就是快速的取得锁,快速的释放锁。一旦出现有第三方模块不遵守这样的规则,就可能导致出现死锁或者性能下降的问题。
有了共享内存,会引入第二个问题。一个内存往往给许多对象使用的。如果在模块中手动的去编写分配,把这些内存给到不同的对象是非常繁琐的。于是使用slab内存管理器。
3.哪些官方模块使用了共享内存
image.png- 主要使用这两种数据结构:
rbtree
想做限速或者流控场景时,我们不能容忍在内存中做的。比如一个worker对某个用户触发了流控、限速而其他worker 还不知道。所以,只能在共享内存中做。比如:ngx_limit_conn_module 、ngx_stream_limit_req_module。http cache 做反向代理时用的。
红黑树特点:插入跟删除特别快,当然也可以做遍历。这些模块都有一个特点,需要做快速的插入和删除。比如:发现了一个客户端,需要对它限速,如果限速达到了需要把客户端从限速数据结构容器中移出,都需要非常的快速。
单链表
把共享的元素串起来就可以了。
ngx_http_lua_api 是Openresty的核心模块。Openresty在这个模块中定义了一个sdk,这个sdk叫做 lua_shared_dict。
image.png代码中,我们同时使用了红黑树以及链表
dogs(共享内存的名称,图中大小是10m) 使用红黑树保存每一个key value
为什么我们需要链表呢?Lua是应用代码,很容易产生限制。当我们达到10M限制的时候呢?lua_shared_dict 采用LRU淘汰。长时间不用的节点优先被淘汰掉。lua_shared_dict 对内存的使用同时满足了红黑树跟链表。
留言问题
1.共享内存使用的场景?什么时候使用,
作者回复
所有需要多worker进程协同配合的场景。例如upstream负载均衡算法,或者limit_conn、limit_req等。一般指令中有zone关键字都是共享内存。
2.老师,对于共享变量dogs,我们在配置文件中对dogs进行插入、删除、值更新的话,需要考虑锁的问题吗?
作者回复
不需要,在操作这个变量时,lua模块已经对变量使用了自旋锁