协程机制
- 基于用户态模拟出来的独立的运行空间;
- 协程依附于线程的内存模型,切换开销小;
- 协程遇到阻塞就归还执行权限,协程的代码以完全同步的方式模拟异步的调用,编程很容易;
- 协程本质上是在线程中串行执行的,对任何共享变量的访问无需加锁;
Nginx 协程
- Nginx 的每一个 Worker 进程都是在 epoll 或 kqueue 这种事件模型之上,封装成协程;当一个 Socket 句柄接收到一个 HTTP Request 操作的时候,这个 epoll 对应的 Socket 就会被唤醒,此时 Nginx 会 new 出一个协程,这个协程会处理 HTTP Request 的完整生命周期,如果处理过程中没有发生阻塞,协程会返回 HTTP Response 给 Nginx 的 epoll 模型做 Socket 的write;一旦处理过程中有 Block 的操作,比如协程要将请求反向代理给后端服务器,等待后端服务器的返回,此时协程放弃自己的执行权限,并把和后端服务器的连接注册到 epoll 事件里,等待这个事件被后端服务器的 Response 唤醒,一旦该事件被唤醒之后,Nginx 的 Worker 进程又会重新 new 一个协程出来,做之后的事情;因此每个操作都不会 Block Worker 进程自身的操作,都是依赖 epoll 操作完成的;在整个的串行链路上,看上去就是几个协程串行在一起,完成一个请求的处理过程;
- 每一个请求都有一个协程进行处理;
- 即使 nginx_lua 需要执行 Lua,相对于 C 来说是有一定的开销,但依旧能保证高并发能力;
Nginx 的协程机制
- Nginx 的每个 Worker 进程内部都创建了一个 Lua 虚拟机,用来跑 Lua 的脚本文件;
- Worker 进程内的所有协程共享同一个 Lua 虚拟机;
- 每一个请求由一个 Lua 协程处理,每个请求的之间的数据是隔离的;
- Lua 代码调用 IO 等异步接口时,协程被挂起,上下文的数据保持不变;一个 HTTP 请求被一个协程处理,遇到 IO 阻塞的时候,在 epoll 模型上注册完,把自己挂起,等到 epoll 模型收到数据返回后,唤醒自己,执行下面的操作,因此,不同 HTTP 请求之间的数据是隔离的;Java 的 Servlet 是用一个线程处理一个 HTTP 请求;Nginx 的 Worker 进程是单线程模型的,其对 HTTP 请求的处理都是用协程完成的;Nginx 的协程遇到阻塞,依靠 epoll 模型解决;Java 中遇到阻塞就等待,因为本身就是多线程的模型;Nginx 的协程机制是比 Java Servlet 的多线程模型更加高效;
- 协程遇到 IO 调用,自动保存,不阻塞 Worker 进程;
- IO 异步操作完成后,还原协程上下文,代码继续执行;
- Nginx 协程的好处是,在编写代码的时候,无需考虑异步的方式,完全可以以同步的方式编写;当协程运行的时候发生了阻塞,比如发起 IO 调用,会在 Nginx 的 epoll 的模型上注册一个回调的句柄,放弃自己的执行权限,当 epoll 模型接收到了阻塞调用的返回之后,会再将对应的协程唤醒,对应的协程代码就像同步调用一样,返回的时候已经获得了 CPU 的执行权限;
Lua 的协程机制
- Lua 的协程机制和 Nginx 的是一样的,因此 Nginx 可以使用 Lua 作为插件模块,使用协程做对应的插件开发;
Nginx Lua 插载点
-
Nginx 提供了很多 Nginx Lua 的插载点,能够在 Nginx 的不同的生命阶段,完成 Lua 脚本插件式的加载,并执行对应的 Lua 程序,顺序如图:
几个重要的插载点
- init_by_lua:指定系统启动时调用的 Lua 脚本;
- init_worker_by_lua:指定 Worker 进程启动时调用的 Lua 脚本;
- set_by_lua:Nginx 变量使用复杂的 lua return;
- rewrite_by_lua:指定重写 URL 规则的 Lua 脚本;
- access_by_lua:指定权限验证阶段的 Lua 脚本;
- content_by_lua:指定内容输出节点的 Lua 脚本;
Nginx 的处理阶段
- NGX_HTTP_POST_READ_PHASE = 0, // 读取请求头部
- NGX_HTTP_SERVER_REWRITE_PHASE // 执行 uri 的 rewrite,比如访问 /resources/ 的目录 rewrite 到 static 目录;
- NGX_HTTP_FIND_CONFIG_PHASE // 根据 uri 替换掉 location
- NGX_HTTP_REWRITE_PHASE // 根据替换结果继续执行 rewrite
- NGX_HTTP_POST_REWRITE_PHASE // 执行 rewrite 的后处理过程
- NGX_HTTP_PREACCESS_PHASE // 认证预处理,比如请求限制,连接限制
- NGX_HTTP_ACCESS_PHASE // 认证阶段,比如 token 操作可以从 Java 中移到 Nginx 的这个阶段
- NGX_HTTP_POST_ACCESS_PHASE // 认证后处理,如果认证不通过,就把包丢弃掉
- NGX_HTTP_TRY_FILES_PHASE // 尝试 try catch 的标签
- NGX_HTTP_CONTENT_PHASE // 内容处理,可以是返回静态内容,可以是做反向代理,也可以自己写 Lua 脚本返回固定的内容;
- NGX_HTTP_LOG_PHASE // 日志处理
请发表评论