Nginx 事件驱动模型 (秒懂+史上最全)

Posted 架构师-尼恩

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Nginx 事件驱动模型 (秒懂+史上最全)相关的知识,希望对你有一定的参考价值。

文章很长,建议收藏起来,慢慢读! Java 高并发 发烧友社群:疯狂创客圈 奉上以下珍贵的学习资源:


推荐:入大厂 、做架构、大力提升Java 内功 的 精彩博文

入大厂 、做架构、大力提升Java 内功 必备的精彩博文2021 秋招涨薪1W + 必备的精彩博文
1:Redis 分布式锁 (图解-秒懂-史上最全)2:Zookeeper 分布式锁 (图解-秒懂-史上最全)
3: Redis与MySQL双写一致性如何保证? (面试必备)4: 面试必备:秒杀超卖 解决方案 (史上最全)
5:面试必备之:Reactor模式6: 10分钟看懂, Java NIO 底层原理
7:TCP/IP(图解+秒懂+史上最全)8:Feign原理 (图解)
9:DNS图解(秒懂 + 史上最全 + 高薪必备)10:CDN图解(秒懂 + 史上最全 + 高薪必备)
11: 分布式事务( 图解 + 史上最全 + 吐血推荐 )12:seata AT模式实战(图解+秒懂+史上最全)
13:seata 源码解读(图解+秒懂+史上最全)14:seata TCC模式实战(图解+秒懂+史上最全)

Java 面试题 30个专题 , 史上最全 , 面试必刷阿里、京东、美团… 随意挑、横着走!!!
1: JVM面试题(史上最强、持续更新、吐血推荐)2:Java基础面试题(史上最全、持续更新、吐血推荐
3:架构设计面试题 (史上最全、持续更新、吐血推荐)4:设计模式面试题 (史上最全、持续更新、吐血推荐)
17、分布式事务面试题 (史上最全、持续更新、吐血推荐)一致性协议 (史上最全)
29、多线程面试题(史上最全)30、HR面经,过五关斩六将后,小心阴沟翻船!
9.网络协议面试题(史上最全、持续更新、吐血推荐)更多专题, 请参见【 疯狂创客圈 高并发 总目录

SpringCloud 精彩博文
nacos 实战(史上最全) sentinel (史上最全+入门教程)
SpringCloud gateway (史上最全)更多专题, 请参见【 疯狂创客圈 高并发 总目录

nginx 事件驱动模型

事件驱动模型是Nginx服务器保障完整功能和具有良好性能的重要机制之一。

事件驱动模型概述

实际上,事件驱动并不是计算机编程领域的专业词汇,它是一种比较古老的响应事件的模型,在计算机编程、公共关系、经济活动等领域均有很广泛的应用。顾名思义,事件驱动就是在持续事务管理过程中,由当前时间点上出现的事件引发的调动可用资源执行相关任务,解决不断出现的问题,防止事务堆积的一种策略。在计算机编程领域,事件驱动模型对应一种程序设计方式,Event-driven programming,即事件驱动程序设计。
事件驱动模型一般是由事件收集器事件发送器事件处理器三部分基本单元组成。
其中, 事件收集器专门负责收集所有的事件,包括来自用户的(如鼠标单击事件、键盘输入事件等)、来自硬件的(如时钟事件等)和来自软件的(如操作系统、应用程序本身等)。
事件发送器负责将收集器收集到的事件分发到目标对象中。目标对象就是事件处理器所处的位置。事件处理器主要负责具体事件的响应工作,它往往要到实现阶段才完全确定。
在程序设计过程中,对事件驱动机制的实现方式有多种,这里介绍batch programming,即批次程序设计。批次的程序设计是一种比较初级的程序设计方式。使用批次程序设计的软件,其流程是由程序设计师在实际编码过程中决定的,也就是说,在程序运行的过程中,事件的发生、事件的发送和事件的处理都是预先设计好的。由此可见,事件驱动程序设计更多的关注了事件产生的随机性,使得应用程序能够具备相当的柔性,可以应付种种来自用户、硬件和系统的离散随机事件,这在很大程度上增强了用户和软件的交互性和用户操作的灵活性。
事件驱动程序可以由任何编程语言来实现,只是难易程度有别。如果一个系统是以事件驱动程序模型作为编程基础的,那么,它的架构基本上是这样的:预先设计一个事件循环所形成的程序,这个事件循环程序构成了“事件收集器”,它不断地检查目前要处理的事件信息,然后使用“事件发送器”传递给“事件处理器”。“事件处理器”一般运用虚函数机制来实现。

操作系统 事件驱动处理库

Nginx服务器响应和处理Web请求的过程,就是基于事件驱动模型的,它也包含事件收集器、事件发送器和事件处理器等三部分基本单元。Nginx的“事件收集器”和“事件发送器”的实现没有太大的特点,重点介绍一下它的“事件处理器”。
通常,我们在编写服务器处理模型的程序时,基于事件驱动模型,“目标对象”中的“事件处理器”可以有以下几种实现办法:

  • “事件发送器”每传递过来一个请求,“目标对象”就创建一个新的进程,调用“事件处理器”来处理该请求。
  • “事件发送器”每传递过来一个请求,“目标对象”就创建一个新的线程,调用“事件处理器”来处理该请求。
  • “事件发送器”每传递过来一个请求,“目标对象”就将其放入一个待处理事件的列表,使用非阻塞I/O方式调用“事件处理器”来处理该请求。

以上的三种处理方式,各有特点,第一种方式,由于创建新的进程的开销比较大,会导致服务器性能比较差,但其实现相对来说比较简单。
第二种方式,由于要涉及到线程的同步,故可能会面临死锁、同步等一系列问题,编码比较复杂。
第三种方式,在编写程序代码时,逻辑比前面两种都复杂。

大多数网络服务器采用了第三种方式,逐渐形成了所谓的“事件驱动处理库”。
事件驱动处理库又被称为多路IO复用方法,最常见的包括以下三种:select模型,poll模型和epoll模型。Nginx服务器还支持rtsig模型、kqueue模型、dev/poll模型和eventport模型等。通过Nginx配置可以使得Nginx服务器支持这几种事件驱动处理模型。这里详细介绍以下它们。

select库

select库,是各个版本的Linux和Windows平台都支持的基本事件驱动模型库,并且在接口的定义上也基本相同,只是部分参数的含义略有差异。使用select库的步骤一般是:
首先,创建所关注事件的描述符集合。对于一个描述符,可以关注其上面的(Read)事件、写(Write)事件以及异常发送(Exception)事件,所以要创建三类事件描述符集合,分别用来收集读事件的描述符、写事件的描述符和异常事件的描述符。
其次,调用底层提供的select()函数,等待事件发生。这里需要注意的一点是,select的阻塞与是否设置非阻塞I/O是没有关系的。
然后,轮询所有事件描述符集合中的每一个事件描述符,检查是否有相应的事件发生,如果有,就进行处理。
Nginx服务器在编译过程中如果没有为其指定其他高性能事件驱动模型库,它将自动编译该库。我们可以使用–with-select_module和–without-select_module两个参数强制Nginx是否编译该库。

poll库

poll库,作为Linux平台上的基本事件驱动模型,实在Linux2.1.23中引入的。Windows平台不支持poll库。
poll与select的基本工作方式是相同的,都是现创建一个关注事件的描述符集合,再去等待这些事件发生,然后在轮询描述符集合,检查有没有事件发生,如果有,就进行处理。
poll库与select库的主要区别在于,select库需要为读事件、写事件和异常事件分别创建一个描述符集合,因此在最后轮询的时候,需要分别轮询这三个集合。而poll库只需要创建一个集合,在每个描述符对应的结构上分别设置读事件、写事件或者异常事件,最后轮询的时候,可以同时检查这三种事件是否发生。可以说,poll库是select库的优化实现。
Nginx服务器在编译过程中如果没有为其制定其他高性能事件驱动模型库,它将自动编译该库。我们可以使用–with-poll_module和–without-poll_module两个参数强制Nginx是否编译该库。

epoll库

epoll库是Nginx服务器支持的高性能事件驱动库之一,它是公认的非常优秀的事件驱动模型,和poll库及select库有很大的不同。epoll属于poll库的一个变种,是在Linux 2.5.44中引入的,在Linux 2.6以上的版本都可以使用它。poll库和select库在实际工作中,最大的区别在于效率。
从前面的介绍我们知道,它们的处理方式都是创建一个待处理事件列表,然后把这个列表发给内核,返回的时候,再去轮询检查这个列表,以判断事件是否发生。这样在描述符比较多的应用中,效率就显得比较低下了。一种比较好的做法是,把描述符列表的管理交给内核负责,一旦有某种事件发生,内核把发生事件的描述符列表通知给进程,这样就避免了轮询整个描述符列表。epoll库就是这样一种模型。
首先,epoll库通过相关调用通知内核创建一个由N个描述符的事件列表。然后,给这些描述符设置所关注的事件,并把它添加到内核的事件列表中去,在具体的编码过程中也可以通过相关调用对事件列表中的描述符进行修改和删除。
完成设置之后,epoll库就开始等待内核通知事件发生了。某一事件发生后,内核将发生事件的描述符列表上报给epoll库。得到事件列表的epoll库,就可以进行事件处理了。
epoll库在Linux平台上是最高效的。它支持一个进程打开大数目的事件描述符,上限是系统可以打开文件的最大数目。同时,epoll库的IO效率不随描述符数目增加而线性下降,因为它只会对内核上报的“活跃”的描述符进行操作。

rtsig模型

rtsig是Real-Time Signal的缩写,是实时信号的意思。从严格意义上说,rtsig模型并不是常用的事件驱动模型,但Nginx服务器使用了使用实时信号对事件进行响应的支持,官方文档中将rtsig模型与其他的事件驱动模型并列。
使用rtsig模型时,工作进程会通过系统内核建立一个rtsig队列用于存放标记事件发生(在Nginx服务器应用中特指客户端请求发生)的信号。每个事件发生时,系统内核就会产生一个信号存放到rtsig队列中等待工作进程的处理。
需要指出的是,rtsig队列有长度限制,超过该长度后就会发生溢出。默认情况下,Linux系统事件信号队列的最大长度设置为1024,也就是同时最多可以存放1024个发生事件的信号。在Linux 2.6.6-mm2之前的版本中,系统各个进程的事件信号队列是由内核统一管理的,用户可以通过修改内核参数/proc/sys/kernel/rtsig-max/来自定义该长度设置。在Linux 2.6.6-mm2之后的版本中,该内核参数被取消,系统各个进程分别拥有各自的事件信号队列,这个队列的大小由Linux系统的RLIMT_SIGPENGIND参数定义,在执行setrlimit()系统调用时确定该大小。Nginx提供了worker_rlimit_sigpending参数用于调节这种情况下的事件信号队列长度。
当rtsig队列发生溢出时,Nginx将暂时停止使用rtsig模型,而调用poll库处理未处理的事件,直到rgsit信号队列全部清空,然后再次启动rtsig模型,以防止新的溢出发生。
Nginx在配置文件中提供了相关参数对rtsig模型的使用配置。编译Nginx服务器时,使用–with-rtsig_module配置选项来启用rtsig模型的编译。

其他事件驱动模型

除了以上四种主要的事件驱动模型,Nginx服务器针对特定的Linux平台提供了响应的事件驱动模型支持。目前实现的主要有kqueue模型、/dev/poll模型和eventport模型等。

  • kqueue模型,是用于支持BSD系列平台的高效事件驱动模型,主要用在FreeBSD 4.1及以上版本、OpenBSD 2.9及以上版本、NetBSD 2.0及以上版本以及Mac OS X平台上。该模型也是poll库的一个变种,其和epoll库的处理方式没有本质上的区别,都是通过避免轮询操作提供效率。该模型同时支持条件触发(level-triggered,也叫水平触发,只要满足条件就触发一个事件)和边缘触发(edge-triggered,每当状态变化时,触发一个事件)。如果大家在这些平台下使用Nginx服务器,建议选在该模型用于请求处理,以提高Nginx服务器的处理性能。

  • /dev/poll模型,适用于支持Unix衍生平台的高效事件驱动模型,其主要在Solaris711/99及以上版本、HP/UX 11.22及以上版本、IRIX 6.5.15及以上版本和Tru64 UNIX 5.1A及以上版本的平台中使用。该模型是Sun公司在开发Solaris系列平台时提出的用于完成事件驱动机制的方案,它使用了虚拟的/dev/poll设备,开发人员可以将要监视的文件描述符加入这个设备,然后通过ioctl()调用来获取事件通知。在以上提到的平台中,建议使用该模型处理请求。

  • eventport模型,适用于支持Solaris 10及以上版本平台的高效事件驱动模型。该模型也是Sun公司在开发Solaris系列平台时提出的用于完成事件驱动机制的方案,它可以有效防止内核崩溃情况的发生。Nginx服务器为此提供了支持。

以上就是Nginx服务器支持的事件驱动库。可以看到,Nginx服务器针对不同的Linux或Unix衍生平台提供了多种事件驱动模型的处理,尽量发挥系统平台本身的优势,最大程度地提高处理客户端请求事件的能力。在实际工作中,我们需要根据具体情况和应用情景选择使用不同的事件驱动模型,以保证Nginx服务器的高效运行。

Nginx默认的事件处理库

Nginx 的连接处理机制在不同的操作系统会采用不同的 I/O 模型,要根据不同的系统选择不同的事件处理模型,可供选择的事件处理模型有:kqueue 、rtsig 、epoll 、/dev/poll 、select 、poll ,其中 select 和 epoll 都是标准的工作模型,kqueue 和 epoll 是高效的工作模型,不同的是 epoll 用在 Linux 平台上,而 kqueue 用在 BSD 系统中。

(1) 在 Linux 下,Nginx 使用 epoll 的 I/O 多路复用模型

(2) 在 Freebsd 下,Nginx 使用 kqueue 的 I/O 多路复用模型

(3) 在 Solaris 下,Nginx 使用 /dev/poll 方式的 I/O 多路复用模型

(4) 在 Windows 下,Nginx 使用 icop 的 I/O 多路复用模型

cat /usr/local/nginx/conf/nginx.conf

......

events {

use epoll;

}

高并发场景Nginx的建议配置


#user  nobody;
#worker_processes  1;
worker_processes  8;  #这个根据硬件有多少核CPU而定
#开发环境
#error_log  logs/error.log  debug;
#生产环境
error_log  logs/error.log;


pid     logs/nginx.pid;


events {
  use epoll;
  worker_connections  1024000;
}


http {
  default_type 'text/html';
  charset utf-8;

  log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
  '$status $body_bytes_sent "$http_referer" '
  '"$http_user_agent" "$http_x_forwarded_for"';

  #access_log  logs/access_main.log  main;
  access_log off;   #日志功能要关闭
  #sendfile: 设置为on表示启动高效传输文件的模式。
  # sendfile可以让Nginx在传输文件时直接在磁盘和tcp socket之间传输数据。
  # 开启这个参数后可以让数据不用经过用户buffer。表示开启零复制,建议生产环境使用
  sendfile        off;
  #sendfile        on;
  #tcp_nopush     on;

  #keepalive_timeout  0;
  keepalive_timeout  65;

  #gzip  on;
  gzip off;
  #gzip最小长度 一般设置成1K就行,小于1K的就不压缩了 不然会越压越大
  gzip_min_length 1024;
  #gzip压缩比,1 压缩比最小处理速度最快,9 压缩比最大但处理最慢(传输快但比较消耗cpu)。
  gzip_comp_level 1;
  #匹配MIME类型进行压缩
  gzip_types text/plain application/json application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png application/vnd.ms-fontobject font/ttf font/opentype font/x-woff image/svg+xml  font/woff;
  gzip_vary on;
  gzip_disable "MSIE [1-6]\\.";
  #设置系统获取几个单位的缓存用于存储gzip的压缩结 果数据流。 例如 4 4k 代表以4k为单位,按照原始数据大小以4k为单位的4倍申请内存。 4 8k 代表以8k为单位,按照原始数据大小以8k为单位的4倍申请内存。
  #如果没有设置,默认值是申请跟原始数据相同大小的内存空间去存储gzip压缩结果。
  gzip_buffers 4 8k;
  #gzip默认最低支持1.1现在改成最低支持1.0 近代浏览器基本不设置
  gzip_http_version 1.0;
  #Nginx作为反向代理的时候启用,开启或者关闭后端服务器返回的结果,匹配的前提是后端服务器必须要返回包含"Via"的 header头。
  #off - 关闭所有的代理结果数据的压缩
  #expired - 启用压缩,如果header头中包含 "Expires" 头信息
  #no-cache - 启用压缩,如果header头中包含 "Cache-Control:no-cache" 头信息
  #no-store - 启用压缩,如果header头中包含 "Cache-Control:no-store" 头信息
  #private - 启用压缩,如果header头中包含 "Cache-Control:private" 头信息
  #no_last_modified - 启用压缩,如果header头中不包含 "Last-Modified" 头信息
  #no_etag - 启用压缩 ,如果header头中不包含 "ETag" 头信息
  #auth - 启用压缩 , 如果header头中包含 "Authorization" 头信息
  #any - 无条件启用压缩
  gzip_proxied expired no-cache no-store private auth;


  #指定缓存信息
  lua_shared_dict ngx_cache 128m;
  #指定缓存信息
  lua_shared_dict seckill_cache 128m;
  #保证只有一个线程去访问redis或是mysql-lock for cache
  lua_shared_dict cache_lock 100k;
  #lua扩展加载

  # for linux
  # lua_package_path "./?.lua;/vagrant/LuaDemoProject/src/?.lua;/usr/local/ZeroBraneStudio-1.80/?/?.lua;/usr/local/ZeroBraneStudio-1.80/?.lua;;";
  # lua_package_cpath "/usr/local/ZeroBraneStudio-1.80/bin/clibs/?.so;;";
  lua_package_path "./?.lua;/vagrant/LuaDemoProject/src/?.lua;/vagrant/LuaDemoProject/vendor/template/?.lua;/vagrant/LuaDemoProject/src/?/?.lua;/usr/local/openresty/lualib/?/?.lua;/usr/local/openresty/lualib/?.lua;;";
  lua_package_cpath "/usr/local/openresty/lualib/?/?.so;/usr/local/openresty/lualib/?.so;;";

  # for windows
  # lua_package_path "./?.lua;C:/dev/refer/LuaDemoProject/src/vendor/jwt/?.lua;C:/dev/refer/LuaDemoProject/src/?.lua;E:/tool/ZeroBraneStudio-1.80/lualibs/?/?.lua;E:/tool/ZeroBraneStudio-1.80/lualibs/?.lua;E:/tool/openresty-1.13.6.2-win32/lualib/?.lua;;";
  # lua_package_cpath "E:/tool/ZeroBraneStudio-1.80/bin/clibs/?.dll;E:/tool/openresty-1.13.6.2-win32/lualib/?.dll;;";


  # 初始化项目
  init_by_lua_file luaScript/initial/loading_config.lua;

  #调试模式(即关闭lua脚本缓存)
  #lua_code_cache off;
  lua_code_cache on;

  #内部网关的代理,内部网关带有 token 认证
  upstream zuul {
    # idea 开发环境
    #	 server 192.168.56.121:7799;
    # centos 自验证环境
    server "cdh1:8888";
    keepalive 1000;
  }

  underscores_in_headers on;

  limit_req_zone  $arg_sku_id  zone=skuzone:10m      rate=6r/m;
  limit_req_zone  $http_user_id  zone=userzone:10m      rate=6r/m;
  limit_req_zone  $binary_remote_addr  zone=perip:10m      rate=6r/m;
  limit_req_zone  $server_name        zone=perserver:1m   rate=10r/s;
  limit_req_zone  $server_name        zone=seckill_server:1m   rate=20000r/s;

  server {
    listen       80;
    server_name  admin.nginx.server;
    default_type 'text/html';
    charset utf-8;


    limit_req  zone=perip;
    limit_req  zone=perserver;

    #管理控制台的web页面
    location  / {
      if ($request_method = 'OPTIONS') {
        add_header Access-Control-Max-Age 600000;
        add_header Access-Control-Allow-Origin *;
        add_header Access-Control-Allow-Credentials true;
        add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT, X-Mx-ReqToken, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, token';
        return 204;
      }
      # IDEA 管理控制台的web页面
      proxy_pass http://192.168.56.121:8066/ ;
      # proxy_pass http://zuul;
    }
  }


  server {
    listen       80 default;
    server_name  nginx.server *.nginx.server;
    default_type 'text/html';
    charset utf-8;

    # 转发 zuul
    location / {
      if ($request_method = 'OPTIONS') {
        add_header Access-Control-Max-Age 600000;
        add_header Access-Control-Allow-Origin *;
        add_header Access-Control-Allow-Credentials true;
        add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT, X-Mx-ReqToken, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, token';
        return 204;
      }

      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_set_header X-Nginx-Proxy true;
      proxy_pass http://zuul;
    }

    # 开发调试: 用户服务
    location  ^~ /uaa-provider/ {
      # idea 开发环境
      #  proxy_pass http://192.168.56.121:7702/;
      # centos 自验证环境
      proxy_pass http://192.168.56.121:7702/uaa-provider/ ;
    }

    # 开发调试: 秒杀服务
    location  ^~ /seckill-provider/ {
      proxy_pass http://192.168.56.121:7701/seckill-provider/ ;
    }



    # 开发调试: 管理服务
    location  ^~ /backend-provider/ {
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_set_header backend  'true'; # 给后台的请求加上自定义头,方便session区分
      proxy_set_header X-Nginx-Proxy true;
      # 指向微服務
      proxy_pass http://192.168.56.121:6600/backend-provider/ ;

      #指向gate way
      # proxy_pass http://zuul;
    }

    # 反向代理:秒杀web页面
    location  ^~ /seckill-web/ {
      proxy_pass http://192.168.56.121:6601/seckill-web/ ;
    }

    # Nginx+lua 秒杀:获取秒杀 token
    location = /seckill-provider/api/seckill/redis/token/v2 {
      default_type 'application/json';
      charset utf-8;
      # 限流的 lua 脚本
      access_by_lua_file luaScript/module/seckill/getToken_access_limit.lua;
      # 获取秒杀token lua 脚本
      content_by_lua_file luaScript/module/seckill/getToken.lua;
    }


    #  ratelimit by sku id
    location  = /ratelimit/sku {
      limit_req  zone=skuzone;
      echo "正常的响应";
    }

    #  ratelimit by user id
    location  = /ratelimit/demo {
      limit_req  zone=userzone;
      echo "正常的响应";
    }


    location = /50x.html{
      echo "限流后的降级内容";
    }

    error_page 502 503 =200 /50x.html;

    #  访问的路径 http://cdh1/ratelimit/demo2?seckillSkuId=3
    #  ratelimit by sku id
    location  = /ratelimit/demo2 {
      # 限流的 lua 脚本
      access_by_lua_file luaScript/module/seckill/getToken_access_limit.lua;

      echo "正常的响应";
    }




  }

  server {
    listen       8080 default;
    server_name  nginx.server *.nginx.server ;
    #limit_req  zone=seckill_server;


    #第一个lua脚本 hellword
    location /helloworld {
      default_type 'text/html';
      charset utf-8;
      content_by_lua_file luaScript/module/demo/helloworld.lua;
    }

    location / {        # 自动匹配到(htm|html)格式
      ## 开发阶段,配置页面不缓存html和htm结尾的文件
      add_header Cache-Control "private, no-store, no-cache, must-revalidate, proxy-revalidate";
      index index.html;
      root /vagrant/LuaDemoProject/src/www/static; #服务器路径
      default_type 'text/html';
    }

    location ~ .*\\.(htm|html)$ {        # 自动匹配到(htm|html)格式
      ## 开发阶段,配置页面不缓存html和htm结尾的文件
      add_header Cache-Control "private, no-store, no-cache, must-revalidate, proxy-revalidate";
      root /vagrant/LuaDemoProject/src/www/static; #服务器路径
      default_type 'text/html';
    }


    location ~ .*\\.(js|script)$ {        # 自动匹配到(jpg|gif|png)格式
      add_header Cache-Control "private, no-store, no-cache, must-revalidate, proxy-revalidate";
      root /vagrant/LuaDemoProject/src/www/static; #服务器路径
      default_type 'application/javascript';
    }




    location ~ .*\\.(css)$ {        # 自动匹配到(css)格式
      root /vagrant/LuaDemoProject/src/www/static; #服务器路径
      default_type 'text/css';
    }


    # 开发调试: 库存服务
    location  ^~ /stock-provider/ {
      proxy_set_header Host $host:$server_port;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_pass http://zuul/stock-provider/ ;
    }

    # 开发调试: 秒杀服务
    location  ^~ /seckill-provider/ {
      #      proxy_pass http://localhost:7701/seckill-provider/ ;
      #指向gate way
      proxy_set_header Host $host:$server_port;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_pass http://zuul/seckill-provider/;
    }



    # Nginx+lua :获取 商品信息
    location = /stock-lua/gooddetail {
      default_type 'application/json';
      charset utf-8;
      # 限流的 lua 脚本
      #access_by_lua_file luaScript/module/seckill/getToken_access_limit.lua;
      # 获取秒杀token lua 脚本
      content_by_lua_file luaScript/module/seckill/good_detail.lua;
    }


    # Nginx+lua 秒杀:获取秒杀 token
    location  ~ /seckill-lua/(.*)/getToken/v3 {
      default_type 'application/json';
      charset utf-8;
      set $skuId $1;
      limit_req  zone=userzone;
      limit_req  zone=seckill_server;

      # 限流的 lua 脚本
      # access_by_lua_file luaScript/module/seckill/getToken_access_limit.lua;
      # 获取秒杀token lua 脚本
      content_by_lua_file luaScript/module/seckill/getToken_v3.lua;
    }


    location = /echo {
      echo "echo";
    }
    location = /50x.html{
      echo "限流后的降级内容";
    }

    error_page 502 503 =200 /50x.html;
  }

}

以上是关于Nginx 事件驱动模型 (秒懂+史上最全)的主要内容,如果未能解决你的问题,请参考以下文章

事件驱动IO模式(图解+秒懂+史上最全)

红黑树(图解+秒懂+史上最全)

MappedByteBuffer 详解(图解+秒懂+史上最全)

高可用 Canal集群( 秒懂 + 史上最全)

DNS图解(秒懂 + 史上最全)

rocket高可用 (图解+秒懂+史上最全)