美文网首页
来聊聊Nginx是如何通信的

来聊聊Nginx是如何通信的

作者: OOM_Killer | 来源:发表于2019-06-15 20:38 被阅读0次

    在说Nginx是如何通信的之前,我们先聊一下线程都是如何通信的吧。


    单个进程中的多个线程

    在每个进程的内存空间中都会有一块特殊的公共区域,通常称之为堆内存。所有的线程都可以访问这块内存。这就会引发一些问题。
    假设A线程对内存中数据进行了修改,由于很累,A准备放下手上的活休息一下再工作,但是这时候B过来修改了这块的数据。等A再回来的时候,发现数据不见了。

    • 由于堆内存中的数据可以被任何线程访问到,所以没有限制的情况下会存在被修改的风险。

    为了解决这个问题,那A线程把数据放到别的线程访问不到的地方不就可以了么。所以操作系统会为每个线程分配属于它自己的一块内存空间,这就是栈内存。其他线程是无权访问的,这也是由操作系统保障的。
    最常见的就是局部变量,他们都会被分配到栈内存中。其他的线程不仅不能访问,而且也对这块内存的数据毫无感知。

    但是现实中,会有一个变量需要多个方法都能使用的情况,此时定义这个变量的位置就不能在方法里了,从(方法的)局部变量变为(类的)成员变量。这时,变量只能放到堆内存了。
    那为了解决很多线程访问同一块数据的问题。

    1. 将数据拷贝多份,每个线程认领一份。
    2. 变量设置为只读。
    3. 互斥锁,想要修改数据就要获取这把锁。

    前两种都不能做到 共享信息,就是多个线程对数据修改,第一个是修改了其他线程看不到,第二个是根本改不了。第三个似乎最友好了,可以改数据,改变的数据其他线程也可见。

    好了,费了那么多的口舌,重新回到Nginx是如何通信这个话题上吧。

    Nginx worker 进程协同工作的关键:共享内存

    Nginx 是多进程单线程程序 ,多个worker要通信,有两种方式。

    • 信号: 这个是与运行相关的
    • 共享内存: 想要数据的同步只能靠共享内存了。

    为了更好的使用共享内存,就会引入之前所提到的那些问题了。
    当然不可避免的,Nginx 也就引进了锁。(为了防止竞争关系)
    Nginx的锁为自旋锁,自旋锁的特点是,当A进程获取了这把锁,B进程没有获取到这把锁时,会不停的请求这把锁。(早期的锁,worker A已经获得锁,worker B 会休息,等待A释放锁后通知B)。
    自旋锁是要求使用共享内存必须快速,所有的模块都必须遵守。快速使用锁,快速释放锁。一旦第三方模块不遵守,就会引发死锁,或者性能下降。

    使用共享内存的模块

    1. Ngx_http_lua_api (openresty 核心)
    2. rbtree
    • stream_limit_conn_module
    • http_limit_conn_module
    • http_limit_req_module
    • http cache (file_cache proxy scgi wusgi fastcgi )
    • ssl_module
    1. 单链表
    • http_upstream_zone_module
    • http_stream_zone_module

    rbtree 十分高效。因为涉及快速的插入删除。
    lua 对共享内存的使用

    http {
          lua_shared_dict dogs 10m;
          server {
                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"))
                     }
                }
          }
    }
    

    dogs 的 k,v 数据,这里同时使用了红黑树和链表。
    共享内存中的k,v 都是由红黑树去保存的,链表是为了防止10m被写满。lua的share_dict 采用的lru淘汰,也就是当10M 满了之后,会将最早写入的优先数据淘汰(lru 淘汰)。

    共享内存在Nginx的使用场景

    一般需要多worker进程协同去配合的场景,例如upstream的负载均衡算法,limit限流等。

    共享内存的分配:Slab 分配器

    既然说到了共享内存,那来了解下内存是如何分配的。
    为了将一大块内存切割为一小块来分配给红黑树上的节点使用,就需要slab分配器。

    slab 分配器会将一整块的内存分为很多个页面(每个页面4K)。每个页面会被切分成各种大小的slot,如4 字节,8 字节,16 字节,32 字节 ,当需要放一块数据为28k的数据的话,他会将数据放到 32 字节 的slot里。
    (KB 1KB=1024B 字节 )

    slab内存管理.png

    这样就可以让申请的内存尽量避免碎片化。
    且有自动的内存对齐的功能。
    tengine 的 slab_stat 模块就可以看到,共享内存的使用情况。

    lua_shared_dict dogs 10m;
    server {
       listen 80;
    
       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"))
            }
       }
    }
    
    

    访问可以看到

    curl 127.0.0.1:80/slab_stat
    * shared memory: dogs
    total:       10240(KB) free:       10168(KB) size:           4(KB)
    pages:       10168(KB) start:00007F043FEFB000 end:00007F04408EB000
    slot:           8(Bytes) total:           0 used:           0 reqs:           0 fails:           0
    slot:          16(Bytes) total:           0 used:           0 reqs:           0 fails:           0
    slot:          32(Bytes) total:         127 used:           1 reqs:           1 fails:           0
    slot:          64(Bytes) total:           0 used:           0 reqs:           0 fails:           0
    slot:         128(Bytes) total:          32 used:           1 reqs:           1 fails:           0
    slot:         256(Bytes) total:           0 used:           0 reqs:           0 fails:           0
    slot:         512(Bytes) total:           0 used:           0 reqs:           0 fails:           0
    slot:        1024(Bytes) total:           0 used:           0 reqs:           0 fails:           0
    slot:        2048(Bytes) total:           0 used:           0 reqs:           0 fails:           0
    
    

    相关文章

      网友评论

          本文标题:来聊聊Nginx是如何通信的

          本文链接:https://www.haomeiwen.com/subject/avlyfctx.html