如何通过Lua扩展Nginx
这篇文章主要讲解了“如何通过 Lua 扩展 Nginx”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何通过 Lua 扩展 Nginx”吧!
创新互联公司是一家集网站建设,汨罗企业网站建设,汨罗品牌网站建设,网站定制,汨罗网站建设报价,网络营销,网络优化,汨罗网站推广为一体的创新建站企业,帮助传统企业提升企业形象加强企业竞争力。可充分满足这一群体相比中小企业更为丰富、高端、多元的互联网需求。同时我们时刻保持专业、时尚、前沿,时刻以成就客户成长自我,坚持不断学习、思考、沉淀、净化自己,让我们为更多的企业打造出实用型网站。
1. ngx_lua 模块
Nginx 模块需要用 C 开发,而且必须符合一系列复杂的规则,最重要的用 C 开发模块必须要熟悉 Nginx 的源代码,使得开发者对其望而生畏。
ngx_lua 模块通过将 lua 解释器集成进 Nginx,可以采用 lua 脚本实现业务逻辑。
该模块具有以下特性:
高并发、非阻塞地处理各种请求。
Lua 内建协程,这样就可以很好地将异步回调转换成顺序调用的形式。
每个协程都有一个独立的全局环境(变量空间),继承于全局共享的、只读的“comman data”。
得益于 Lua 协程的支持,ngx_lua 在处理 10000 个并发请求时只需要很少的内存。根据测试,ngx_lua 处理每个请求只需要 2KB 的内存,如果使用 LuaJIT 则会更少。
ngx_lua 非常适合用于实现可扩展的、高并发的服务。
2. 协程(Coroutine)
1. 协程类似一种多线程,与多线程的区别
协程并非 os 线程,所以创建、切换开销比线程相对较小。
协程与线程一样有自己的栈、局部变量等,但是协程的栈是在用户进程空间模拟的,所以创建、开销很小。
多线程程序是多个线程并发执行,也就是说在一瞬间有多个控制流在执行。而协程强调的是一种多个协程间协作的关系,只有当一个协程主动放弃执行权,另一个协程才能取得执行权,所以在某一瞬间,多个协程间只有一个在运行。
由于多个协程是只有一个在运行,所以对于临界区的访问不需要加锁,而多线程的情况则必须加锁。
多线程程序由于有多个控制流,所以程序的行为不可控,而多个协程的执行是由开发者定义的所以是可控的。
Nginx 的每个 Worker 进程都是在 epoll 或 kqueue 这样的事件模型之上,封装成协程,每个请求都有一个协程进行处理。这正好与 Lua 内建协程的模型是一致的,所以即使 ngx_lua 需要执行 lua,相对 C 有一定的开销,但依然能保证高并发能力。
3. Nginx 进程模型
Nginx 采用多进程模型,单Master-多Worker,Master 进程主要用来管理 Worker 进程。
Worker 进程采用单线程、非阻塞地事件模型(Event Loop,事件循环)来实现端口的监听及客户端请求的处理和响应,同时 Worker 还要处理来自 Master 的信号。Worker 进程个数一般设置为机器 CPU 核数。
1. Master 进程具体包括如下 4 个主要功能
接受来自外界的信号。
向各 Worker 进程发送信号。
监控 Worker 进程的运行状态。
当 Worker 进程退出后(异常情况下),会自动重新启动新的 Worker 进程。
2. 进程模型
4. HTTP 请求处理
阶段 | 说明 |
---|---|
post-read | 读取请求内容阶段,nginx 读取并解析完请求头之后就立即开始运行 |
server-rewrite | server 请求地址重写阶段 |
find-config | 配置查找阶段,用来完成当前请求与 location 配置块之间的配置工作 |
rewrite | location 请求地址重写阶段,当 ngx_rewrite 指令用于 location 中,就是在这个阶段运行的 |
post-rewrite | 请求地址重写阶段,当 nginx 完成 rewrite 阶段所要求的内部跳转动作,如果 rewrite 阶段有这个要求的话 |
preaccess | 访问权限检查准备阶段,ngx_limit_req 和 ngx_limit_zone 在这个阶段运行,ngx_limit_req 可以控制请求的访问频率,ngx_limit_zone 可以控制访问的并发度。 |
access | 权限检查阶段,ngx_access 在这个阶段运行,配置指令多是执行访问控制相关的任务,入检查用户的访问权限,检查用户的来源 IP 是否合法 |
post-access | 访问权限检查提交阶段 |
try-files | 配置 try_files 处理阶段 |
content | 内容产生阶段,是所有请求处理阶段中最为重要的阶段,因为这个阶段的指令通常是用来生成 HTTP 响应内容的 |
log | 日志模块处理阶段 |
5. ngx_lua 指令
ngx_lua 属于 nginx 的一部分,它的执行指令都包含在 nginx 的 11 个步骤之中了,相应的处理阶段可以做插入式处理,即可插拔式架构,不过 ngx_lua 并不是所有阶段都会运行的;另外指令可以在 http、server、server if、location、location if 几个范围进行配置。
指令 | 所处处理阶段 | 使用范围 | 解释 |
---|---|---|---|
init_by_lua init_by_lua_file | loading-config | http | nginx Master 进程加载配置时执行; 通常用于初始化全局配置/预加载 Lua 模块 |
init_worker_by_lua init_worker_by_lua_file | starting-worker | http | 每个 Nginx Worker 进程启动时调用的计时器,如果 Master 进程不允许 则只会在 init_by_\lua 之后调用;通常用于定时拉取配置/数据, 或者后端服务的健康检查。 |
set_by_lua set_by_lua_file | rewrite | server,server if,location,location if | 设置 nginx 变量,可以实现复杂的赋值逻辑;此处是阻塞的,Lua 代码要做到非常快 |
write_by_lua rewrite_by_lua_file | rewrite tail | http,server,location,location if | rewrite 阶段处理,可以实现复杂的转发/重定向逻辑。 |
access_by_lua access_by_lua_file | access tail | http,server,location,location if | 请求访问阶段处理,用于访问控制 |
content_by_lua content_by_lua_file | content | location,location if | 内容处理器,接受请求处理并输出响应 |
header_filter_by_lua header_filter_by_lua_file | output-header-first | http,server,location,location if | 设置 header 和 cookie |
body_filter_by_lua body_filter_by_lua_file | output-body-filter | http,server,location,location if | 对响应数据进行过滤,比如截断、替换 |
log_by_lua log_by_lua_file | log | http,server,location,location if | log 阶段处理,比如记录访问量/统计平均响应时间 |
6. OpenResty
1. 概念
OpenResty 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。
2. 工作原理
OpenResty 通过汇聚各种设计精良的 Nginx 模块(主要由 OpenResty 团队自主开发),从而将 Nginx 有效地变成一个强大的通用 Web 应用平台。这样,Web 开发人员和系统工程师可以使用 Lua 脚本语言调用 Nginx 支持的各种 C 以及 Lua 模块,快速构造出足以胜任 10k 乃至 1000k 以上单机并发连接的高性能 Web 应用系统。
3. 目标
OpenResty 的目标是让你的 Web 服务直接跑在 Nginx 服务内部,充分利用 Nginx 的非阻塞 I/O 模型,不仅仅对 HTTP 客户端请求,甚至于对远程后端诸如 MySQL、PostgreSQL、Memcached 以及 redis 等都进行一致的高性能响应。
7. ngx_lua 实例
content_by_lua
:内容处理器,接收请求处理并输出响应。该指令工作在 Nginx 处理流程的 content 阶段,即内容产生阶段,是所有请求处理阶段中最为重要的阶段,因为这个阶段的指令通常是用来生成 HTTP 响应内容的。
测试
输出: $ curl http://127.0.0.1/ $ Hello,world
1. 简单示例
user www-data; worker_processes auto; pid /run/nginx.pid; error_log logs/error.log; events { worker_connections 1024; } http { server { listen 8080; location / { default_type text/html; content_by_lua ' ngx.say("Hello, world!
") '; } } }
user www-data; worker_processes auto; pid /run/nginx.pid; error_log logs/error.log; events { worker_connections 1024; } http { server { listen 8080; location / { default_type text/html; content_by_lua_block { ngx.say('Hello, world!') } } } }
启动 /usr/local/openresty/nginx/sbin/nginx -p `pwd` -c conf/nginx_openresty_01.conf 测试 curl -i http://127.0.0.1/
2. 休眠示例
user www-data; worker_processes auto; pid /run/nginx.pid; error_log logs/error.log; events { worker_connections 1024; } http { server { listen 8080; location /lua_1 { default_type text/html; content_by_lua_block { ngx.say('Hello, world! @ Time 1!') ngx.sleep(3) ngx.say('Hello, world! @ Time 2!') } } } }
启动 /usr/local/openresty/nginx/sbin/nginx -p `pwd` -c conf/nginx_openresty_02.conf 测试 curl -i http://127.0.0.1/lua_1 curl -i http://127.0.0.1/lua_1
3. 带参数示例
user www-data; worker_processes auto; pid /run/nginx.pid; error_log logs/error.log; events { worker_connections 1024; } http { server { listen 8080; location /lua_1 { default_type text/html; content_by_lua_block { ngx.say(ngx.var.arg_a) } } } }
启动 /usr/local/openresty/nginx/sbin/nginx -p `pwd` -c conf/nginx_openresty_03.conf 测试 curl -i http://127.0.0.1?a=nginx_lua
8. OpenResty 连接 redis
配置文件
user www-data; worker_processes auto; pid /run/nginx.pid; error_log logs/error.log; events { worker_connections 1024; } http { server { listen 8080; location ~/redis_lua/(\d+)$ { default_type text/html; charset utf-8; lua_code_cache on; content_by_lua_file '/home/zp/openresty/lua/redis.lua'; } } }
lua 脚本
local json = require("cjson") local redis = require("resty.redis") local red = redis:new() red:set_timeout(1000) local ip = "127.0.0.1" local port = 6379 local ok, err = red:connect(ip, port) if not ok then ngx.say('connect to redis error: ', err) return ngx.exit(500) end local id = ngx.var[1] local value = "calue-"..id red:set(id, value) local resp, err = red:get(id) if not resp then ngx.say('get from redis error: ', err) return ngx.exit(500) end red:close() ngx.say(json.encode({content=resp}))
测试
启动 /usr/local/openresty/nginx/sbin/nginx -p `pwd` -c conf/nginx_openresty_04.conf 测试 curl -i http://127.0.0.1/redis_lua/1 curl -i http://127.0.0.1/redis_lua/2 curl -i http://127.0.0.1/redis_lua/3
感谢各位的阅读,以上就是“如何通过 Lua 扩展 Nginx”的内容了,经过本文的学习后,相信大家对如何通过 Lua 扩展 Nginx这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是创新互联,小编将为大家推送更多相关知识点的文章,欢迎关注!
网站名称:如何通过Lua扩展Nginx
当前网址:http://scyanting.com/article/jdgipd.html