Nginx第三篇 功能扩展OpenResty简单使用

 一、OpenResty是什么

        OpenResty就是嵌入了LuaJIT VM的Nginx;LuaJIT即采用C语言写的Lua代码的解释器。通过 OpenResty,我们可以把 nginx 的各种功能进行自由拼接, 重要的是,开发门槛并不高,我们只需要熟悉简单灵巧的Lua 语言和nginx基础知识就可以上手开发简单功能了。
        OpenResty让我们可以更加从容的对nginx功能进行扩展。说到nginx功能扩展,大家可能还想到了nginx模块编程。Nginx C Module编程学习成本比较高(有兴趣可以简单了解一下,参考链接nginx模块编程),连接一些外部存储服务(比如redis、Postgres、Memcached、mysql等等)比较困难,另外模块代码改动后要重新编译;而Openresty因为可以使用lua脚本,很多的lua库都可以引入,而且每次修改脚本后直接reload即可生效,相对来说就要简单和灵活了不少。
        lua脚本这里不多说了,附上一个W3C的基础教程地址:W3C Lua 教程

二、Lua脚本与Nginx交互顺序

lua执行指令都包含在nginx的11个步骤之中了,相应的处理阶段可以做插入式处理,即可插拔式架构,不过ngx_lua并不是所有阶段都会运行的;另外指令可以在http、server、location几个范围进行配置,具体如下:

指令

所处处理阶段

使用范围

解释

init_by_lua

init_by_lua_block

init_by_lua_file

loading-config

http

nginx Master进程加载配置时执行;

通常用于初始化全局配置/预加载Lua模块

init_worker_by_lua

init_worker_by_lua_block

init_worker_by_lua_file

starting-worker

http

每个Nginx Worker进程启动时调用的计时器,如果Master进程不允许则只会在init_by_lua之后调用;

通常用于定时拉取配置/数据,或者后端服务的健康检查

set_by_lua

set_by_lua_block

set_by_lua_file

rewrite

server,server if, location,location if

设置nginx变量,可以实现复杂的赋值逻辑;此处是阻塞的,Lua代码要做到非常快;

rewrite_by_lua

rewrite_by_lua_block

rewrite_by_lua_file

rewrite tail

http,server,location,location if

rrewrite阶段处理,可以实现复杂的转发/重定向逻辑;

access_by_lua

access_by_lua_block

access_by_lua_file

access tail

http,server,location,location if

请求访问阶段处理,用于访问控制

content_by_lua

content_by_lua_block

content_by_lua_file

content

location,location if

内容处理器,接收请求处理并输出响应

header_filter_by_lua

header_filter_by_lua_block

header_filter_by_lua_file

output-header-filter

http,server,location,location if

设置header和cookie

body_filter_by_lua

body_filter_by_lua_block

body_filter_by_lua_file

output-body-filter

http,server,location,location if

对响应数据进行过滤,比如截断、替换。

log_by_lua

log_by_lua_block

log_by_lua_file

log

http,server,location,location if

log阶段处理,比如记录访问量/统计平均响应时间

这部分内容参考了:
玩转 Nginx 之:使用 Lua 扩展 Nginx 功能_阳光梦的专栏-CSDN博客_lua nginx

三、永远的hello world

(1)直接在nginx.conf内编写代码

worker_processes  1;

events {
    worker_connections  1024;
}

http {
   
	server {
        listen 80;
        location / {
            default_type text/html;
            content_by_lua_block {
                ngx.say("<p>hello world</p>")
            }
        }
    }
}

浏览器地址栏输入  localhost  查看效果如下图:

(2)引入lua文件

lua文件位置及内容如下图:

 nginx.conf配置如下(请查看 8000 端口下配置):

worker_processes  1;

events {
    worker_connections  1024;
}

http {
   
	server {
        listen 80;
        location / {
            default_type text/html;
            content_by_lua_block {
                ngx.say("<p>hello world</p>")
            }
        }
    }
	
	server {
        listen 8000;
        location / {
            default_type text/html;
            content_by_lua_file /lua/work/helloworld.lua;
        }
    }
}

 浏览器查看效果:

(3)批量引用lua脚本(引用lualib)

 如果项目比较大,有时候会编写一些自己项目的lua脚本库,那么我们可以使用如下方式进行引入
举例:

自己的lualib存放位置如下“mylib.lua”

在nginx.conf配置文件中引入   -----    lua_package_path 'lualib/mylib/?.lua;;';
这里的引入规则可以参考Lua中的模块(module)和包(package)详解
nginx.conf配置文件如下:

worker_processes  1;

events {
    worker_connections  1024;
}

http {

	lua_package_path 'lualib/mylib/?.lua;;';

	server {
        listen 80;
        location / {
            default_type text/html;
            content_by_lua_block {
                ngx.say("<p>hello world</p>")
            }
        }
    }
	
	server {
        listen 8000;
        location / {
            default_type text/html;
            content_by_lua_file /lua/work/helloworld.lua;
        }
    }
	
	server {
        listen 8080;
        location / {
            default_type text/html;
            content_by_lua_file /lua/work/my_helloworld.lua;
        }
    }
}

 mylib.lua的内容(定义了一个常量版本号,定义了一个方法say_hello_world())

local _M = { _VERSION = '1.0.0' }

function _M.say_hello_world()
	ngx.say("<p>hello world!!!</p>")
end

return _M

我们在my_helloworld.lua(参看nginx.conf中8080端口的配置)这个脚本中使用这个mylib中定义的方法,my_helloworld.lua脚本内容如下:

local mylib = require 'mylib'

ngx.say("<p>"..mylib._VERSION.."</p>")

mylib.say_hello_world()

浏览器查看结果:

 四、常用的Nginx Lua API

进行nginx lua扩展时,我们需要接收请求、处理并输出响应。接收请求后我们经常需要获取如请求参数、请求头、Body体等信息用于信息分类、信息存储、数据处理、反向代理等等,处理完成后需要进行响应状态码、响应头和响应内容体的输出。这一整个过程中,我们除了学习基础的lua语法之外,还需要熟悉nginx lua api,nginx lua api中包含了各种现成可用的快捷功能、接口,可以免去我们很多麻烦。这里我只列举几种常用的,需要学习更全面的可以查看 Lua | NGINX 

http协议内容:
ngx.req.get_headers:获取请求头,默认只获取前100,如果想要获取所以可以调用ngx.req.get_headers(0);获取带中划线的请求头时请使用如headers.user_agent这种方式;如果一个请求头有多个值,则返回的是table;
ngx.req.get_uri_args:获取url请求参数,其用法和get_headers类似;ngx.req.get_post_args:获取post请求内容体,其用法和get_headers类似,须提前调用ngx.req.read_body()来读取body体;
ngx.req.get_body_data:为解析的请求body体内容字符串。
ngx.req.get_method():获取请求方法  返回GET POST PUT等等请求方法
ngx.req.http_version():获http协议版本号

日志打印:
ngx.log(ngx.ERR,'hello world')  :ngx.ERR为日志级别,'hello world'为日志内容
ngx日志级别分为了以下9种,级别从高到低:
ngx.STDERR
ngx.EMERG
ngx.ALERT
ngx.CRIT
ngx.ERR
ngx.WARN
ngx.NOTICE
ngx.INFO
ngx.DEBUG

response响应:
ngx.header:输出响应头
ngx.print:输出响应内容体
ngx.say:通ngx.print,但是会最后输出一个换行符
ngx.exit:指定状态码退出

示例代码来一个:

local cjson = require "cjson"

-- 日志打印
ngx.log(ngx.STDERR,'1 hello world')
ngx.log(ngx.EMERG,'2 hello world')
ngx.log(ngx.ALERT,'3 hello world')
ngx.log(ngx.CRIT,'4 hello world')
ngx.log(ngx.ERR,'5 hello world')
ngx.log(ngx.WARN,'6 hello world')
ngx.log(ngx.NOTICE,'7 hello world')
ngx.log(ngx.INFO,'8 hello world')
ngx.log(ngx.DEBUG,'9 hello world')

--请求方法  
ngx.say("ngx.req.get_method : ",ngx.req.get_method(), "<br/>") 
--请求的http协议版本  
ngx.say("ngx.req.http_version : ",ngx.req.http_version(), "<br/>")
ngx.say("<br/>")
ngx.say("<br/>")
  
--get请求uri参数  
ngx.say("get uri args begin", "<br/>")
local uri_args = ngx.req.get_uri_args()  
for k, v in pairs(uri_args) do  
    ngx.say(k, ": ", v, "<br/>")  
end  
ngx.say("get uri args end", "<br/>")
ngx.say("<br/>")
ngx.say("<br/>")
  
--post请求参数  
ngx.req.read_body()
ngx.say("post args begin", "<br/>")
local post_args = ngx.req.get_post_args()  
for k, v in pairs(post_args) do  
    ngx.say(k, ": ", v, "<br/>") 
end
-- 处理post参数
local p_body_data = ngx.req.get_body_data()
local json_body = cjson.decode(p_body_data)
if json_body ~= nil then
	ngx.say("json body : ")
	for jk,jv in pairs(json_body) do 
		ngx.say(jk, ": ", jv, ",")
	end
end
ngx.say("post args end", "<br/>")
ngx.say("<br/>")
ngx.say("<br/>")

--请求头  
local headers = ngx.req.get_headers()
ngx.say("headers begin", "<br/>")
for k,v in pairs(headers) do  
    if type(v) == "table" then
        ngx.say(k, " : ", table.concat(v, ","))  
    else 
        ngx.say(k, " : ", v, "<br/>")
    end  
end  
ngx.say("headers end", "<br/>")

上面代码的日志输出 如下图:(error.log日志默认的日志级别是ngx.ERR)

网页上http get 请求后结果 如下图:

postman post 请求结果 如下图:

到这里大家会发现一个问题,通过调用ngx.req.get_post_args()获取参数时,参数被openresty处理为{"json参数":true}的格式,这不是我们想要的结果。于是我们通过ngx.req.get_body_data()拿到body的json字符串,使用cjson反序列化后,得到想要的结果。

 基本的介绍就到这里了,我们用lua可以做很多事情,上面只列举了九牛一毛。对于如何连接redis,怎么读写mysql,又怎么读写文件,怎么加缓存,等等等等 还有很多。

冰山一角,任重而道远,共勉!

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值