Nginx二次开发

文章目录

  • 一、Lua语言基础
    • 1、 介绍
    • 2、IDE
    • 3、Lua基础语法
      • 3.1 保留关键字和注释
      • 3.2 变量
        • 1. 数字类型
        • 2. 字符串
        • 3. 空值
        • 4. 布尔类型
        • 5. 作用域
      • 3.3 控制语句
        • 1. while循环
        • 2. if-else
        • 3. for循环
        • 4. 函数
      • 3.4 返回值
      • 3.5 Table
      • 3.6 数组
      • 3.7 成员函数
  • 二、Openresty Nginx + Lua
    • 1、openresty简介与安装
      • 1.1 openresty简介
      • 1.2 openresty安装
    • 2、lua-nginx模块
      • 2.1 热部署与lua配置文件
      • 2.2 lua处理http请求相关操作
      • 2.3 获取Nginx uri中的所有变量
      • 2.4 获取Nginx请求头信息
      • 2.5 获取post请求参数
      • 2.6 其他信息
      • 2.7 常用参数与示例
    • 3、Nginx常用模块--数据缓存
      • 3.1 nginx全局缓存
      • 3.2 lua-resty-lrucache
      • 3.3 lua-resty-redis
      • 3.4 lua-resty-mysql
    • 4、模板引擎
      • 4.1 简介
      • 4.2 简单示例
      • 4.3 复杂示例
      • 4.4 模板管理与缓存
      • 4.5 Redis缓存+mysql+模板输出
  • 三、Lua开源项目
    • 1、WAF
    • 2、Kong 基于Openresty的流量网关
      • 2.1 介绍
      • 2.2 核心优势
    • 3、APISIX|ABTestingGateway
      • 3.1 介绍
      • 3.2 优势

一、Lua语言基础

1、 介绍

Lua 是由巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个研究小组于1993年开发的一种轻量、小巧的脚本语言,用标准 C 语言编写,其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。
lua官网:http://www.lua.org/

Lua/luajit/nginx/openresty关系,lua是脚本语言,luajit是lua的功能包与解析器(相当于java对应的jdk),nginx可以内嵌luajit(承载的容器),openresty把额外扩展包集成到nginx里(相当于spring脚手架)

2、IDE

windows版lua下载
http://joedf.ahkscript.org/LuaBuilds/
http://luabinaries.sourceforge.net/
EmmyLua插件
https://github.com/EmmyLua/IntelliJ-EmmyLua
https://emmylua.github.io/zh_CN/
LDT 基于eclipse
https://www.eclipse.org/ldt/

3、Lua基础语法

3.1 保留关键字和注释

保留关键字
and/break/do/else/elseif/end/false/for/function if/in/local/nil/not/or/repeat/return/then/true/until/while
注释

-- 两个减号是行注释

--[[
 这是块注释
 这是块注释.
 --]]

3.2 变量

1. 数字类型

Lua的数字只有double型,64bits

-- 可以以如下的方式表示数字
num = 1024
num = 3.0
num = 3.1416
num = 314.16e-2
num = 0.31416E1
num = 0xff
num = 0x56

2. 字符串

可以用单引号,也可以用双引号,也可以使用转义字符’\n’(换行),‘\r’(回车), ‘\t’ (横向制表),‘\v’(纵向制表),‘’(反斜杠),‘"’(双引号), 以及’‘’(单引号)等等
下面的四种方式定义了完全相同的字符串(其中的两个中括号可以用于定义有换行的字符串)

--[[ 输出alo
123"]]
a = 'alo\n123"'
a = "alo\n123\""
a = '\97lo\10\04923"'
a = [[alo
123"]]

3. 空值

C语言中的NULL在Lua中是nil,比如你访问一个没有声明过的变量,就是nil

4. 布尔类型

只有nil和false是 false;数字0,‘’ 空字符串(‘\0’)都是true

5. 作用域

lua中的变量如果没有特殊说明,全是全局变量,那怕是语句块或是函数里。
变量前加local关键字的是局部变量

3.3 控制语句

1. while循环

local i = 0
local max = 10
while i <= max do
print(i)
i = i +1
end

2. if-else

local function main()

local age = 140
local sex = 'Male'
  if age == 40 and sex =="Male" then
    print(" 男人四十一枝花 ")
  elseif age > 60 and sex ~="Female" then
    print("old man!!")
  elseif age < 20 then
    io.write("too young, too simple!\n")
  else
  print("Your age is "..age)
  end
end

-- 调用
main()

3. for循环

sum = 0
for i = 100, 1, -2 do
	sum = sum + i
end

4. 函数

-- ==========================第一个=============
function myPower(x,y)
    return y+x
end

power2 = myPower(2,3)
print(power2)


-- ==========================第二个=============
function newCounter()
    local i = 0
    return function()     -- anonymous function
        i = i + 1
        return i
    end
end

c1 = newCounter()
print(c1())  --> 1
print(c1())  --> 2
print(c1())

3.4 返回值

name, age,bGay = "shawn", 37, false, "[email protected]"
print(name,age,bGay)

function isMyGirl(name)
    return name == 'lucy' , name
end

local bol,name = isMyGirl('lucy')
-- lucy	true
print(name,bol)

3.5 Table

key,value的键值对 类似 map

local function main()
    dog = {name='shawn',age=18,height=165.5}
    dog.age=22
    print(dog.name,dog.age,dog.height)
    print(dog)
end
main()

3.6 数组

local function main()
    arr = {"string", 100, "dog",function() print("wangwang!") return 1 end}
    print(arr[4]())
end
main()

-- 遍历数组
for k, v in pairs(arr) do
    print(k, v)
end

3.7 成员函数

local function main()

person = {name='旺财',age = 18}
  function  person.eat(food)
    print(person.name .." eating "..food)
  end
person.eat("骨头")
end
main()

二、Openresty Nginx + Lua

1、openresty简介与安装

1.1 openresty简介

OpenResty通过汇聚各种设计精良的Nginx模块(主要由OpenResty团队自主开发)将Nginx变成一个强大的通用Web应用平台。这样,Web开发人员和系统工程师可以使用Lua脚本语言调动Nginx支持的各种C以及Lua模块,快速构造出足以胜任10KB乃至1000KB以上单机并发连接的高性能Web应用系统。
OpenResty的目标是让Web服务直接跑在Nginx服务内部,充分利用Nginx的非阻塞I/O模型,不仅对HTTP客户端请求,甚至对远程后端(如MySQL、PostgreSQL、Memcached以及Redis等)都进行一致的高性能响应。
参考:一文带你详解Nginx/OpenResty

执行原理
OpenResty中,每个Worker进程使用一个Lua VM(Lua虚拟机),当请求被分配到Worker时,将在这个Lua VM中创建一个协程,协程之间数据隔离,每个协程都具有独立的全局变量。
ngx_lua是将Lua嵌入Nginx,让Nginx执行Lua脚本,并且高并发、非阻塞地处理各种请求。Lua内置协程可以很好地将异步回调转换成顺序调用的形式。ngx_lua在Lua中进行的IO操作都会委托给Nginx的事件模型,从而实现非阻塞调用。开发者可以采用串行的方式编写程序,ngx_lua会在进行阻塞的IO操作时自动中断,保存上下文,然后将IO操作委托给Nginx事件处理机制,在IO操作完成后,ngx_lua会恢复上下文,程序继续执行,这些操作对用户程序都是透明的。
每个Nginx的Worker进程持有一个Lua解释器或LuaJIT实例,被这个Worker处理的所有请求共享这个实例。每个请求的context上下文会被Lua轻量级的协程分隔,从而保证各个请求是独立的

1.2 openresty安装

预编译安装
http://openresty.org/cn/linux-packages.html

# 服务命令
service openresty start
service openresty stop
nginx -t
service openresty reload
nginx -V

源码编译安装
http://openresty.org/cn/download.html

# 最小版本基于nginx1.21
wget https://openresty.org/download/openresty-1.21.4.1.tar.gz
# 该包包含有gcc,g ++ 和 make
apt install build-essential

tar -zxvf openresty-1.21.4.1.tar.gz
cd openresty-1.21.4.1

# Centos
yum -y install gcc zlib zlib-devel pcre-devel openssl openssl-devel
# ubuntu 
sudo apt install zlib1g
sudo apt install zlib1g-dev
sudo apt-get install libpcre3-dev
sudo apt-get install libssl-dev


# 默认, --prefix=/usr/local/openresty 程序会被安装到usr/local/openresty目录
./configure
# 可以指定各种选项
./configure --prefix=/opt/openresty \
            --with-luajit \
            --without-http_redis2_module \
            --with-http_iconv_module \
            --with-http_postgres_module
# 帮助     
./configure --help
# 编译与安装
make && make install

测试lua脚本
此时的nginx和配置文件都是openresty包提供的

# 在Nginx.conf 中写入
location /lua {

  default_type text/html;
  content_by_lua 'ngx.say("

Hello, World!

")'
; } location /lua { default_type text/html; # 创建外部脚本,脚本内容ngx.say("

Hello, World!

")
content_by_lua_file conf/lua/hello.lua; }

2、lua-nginx模块

github官方文档:https://github.com/openresty/lua-nginx-module#readme
wiki文档:https://www.nginx.com/resources/wiki/modules/lua/
文档:https://openresty-reference.readthedocs.io/en/latest/

2.1 热部署与lua配置文件

# 设置包含的lua文件块
include /etc/nginx/conf.d/lua.conf;


# lua.conf配置文件
server {
  listen       80;
  server_name  localhost;

  location /lua {
    # 热部署,hello.lua改变即可实时刷新,无需重启
    lua_code_cache off;
    default_type text/html;

    content_by_lua_file /etc/nginx/conf.d/hello.lua;
  }
}

2.2 lua处理http请求相关操作

  • set_by_lua

修改nginx变量

  • rewrite_by_lua

修改uri

  • access_by_lua

访问控制

  • header_filter_by_lua

修改响应头

  • boy_filter_by_lua

修改响应体

  • log_by_lua

日志

2.3 获取Nginx uri中的所有变量

local uri_args = ngx.req.get_uri_args()  
for k, v in pairs(uri_args) do  
    if type(v) == "table" then  
        ngx.say(k, " : ", table.concat(v, ", "), "
"
) else ngx.say(k, ": ", v, "
"
) end end

2.4 获取Nginx请求头信息

local headers = ngx.req.get_headers()                         
ngx.say("Host : ", headers["Host"], "
"
) ngx.say("user-agent : ", headers["user-agent"], "
"
) ngx.say("user-agent : ", headers.user_agent, "
"
) for k,v in pairs(headers) do if type(v) == "table" then ngx.say(k, " : ", table.concat(v, ","), "
"
) else ngx.say(k, " : ", v, "
"
) end end

2.5 获取post请求参数

ngx.req.read_body()  
ngx.say("post args begin", "
"
) local post_args = ngx.req.get_post_args() for k, v in pairs(post_args) do if type(v) == "table" then ngx.say(k, " : ", table.concat(v, ", "), "
"
) else ngx.say(k, ": ", v, "
"
) end end

2.6 其他信息

-- http协议版本
ngx.say("ngx.req.http_version : ", ngx.req.http_version(), "
"
) -- 请求方法 ngx.say("ngx.req.get_method : ", ngx.req.get_method(), "
"
) -- 原始的请求头内容 ngx.say("ngx.req.raw_header : ", ngx.req.raw_header(), "
"
) -- body内容体 ngx.say("ngx.req.get_body_data() : ", ngx.req.get_body_data(), "
"
)

2.7 常用参数与示例

ngx常用用法,例如say print可以作为content返回,redirect可以实现跳转,ngx.location.capture可以请求一个url,一般是内部url。exit可以返回状态码,shared可以作为一个所有进程共享的缓存kv池。

log
ngx.log(ngx.INFO, “日志内容”) 。级别有ngx.STDERR/ngx.EMERG /ngx.ALERT/ ngx.CRIT /ngx.ERR /ngx.WARN/ ngx.NOTICE/ ngx.INFO /ngx.DEBUG
var与ctx
ngx.var可以获取或者修改nginx上下文的$xx变量,包括内置的变量,和在location范围内set的变量。nginx上下文的变量参考
一个request上下文的ngx.ctx是一个table可以自由的赋值和获取。例如rewrite_by_lua中ngx.ctx.a=10, content_by_lua中能拿到ngx.ctx.a
location.capture

local res = ngx.location.capture('/foo?a=1&b=3&c=%3a') -- get请求, res有status body header和truncated属性

ngx.req.set_header("Content-Type", "application/json")
ngx.location.capture('/foo/bar',{ method = ngx.HTTP_POST, body = cjson.encode({a = 'hello, world' })) --post

req

  • ngx.req.get_method() 返回大写的字符串GET
  • ngx.req.get_headers() 返回 kv table
  • ngx.req.set_header(k, v)
  • ngx.req.get_body_data() 返回是string
  • ngx.req.get_uri_args() get查询参数,返回table
  • ngx.req.get_post_args() form类型的post,返回table,err

hash与编码

  • newstr = ngx.encode_base64(str, no_padding?)
  • newstr = ngx.decode_base64(str)
  • digest = ngx.md5(str)
  • str = ngx.encode_args(table)
  • table = ngx.decode_args(str, max_args?)
  • newstr = ngx.escape_uri(str)
  • newstr = ngx.unescape_uri(str)

re正则

  • local m, err = ngx.re.match(“hello, 1234”, “[0-9]{3}”) – m[0] == “123” 这个正则比lua自带的正则强一点
  • local from, to, err = ngx.re.find(s, “([0-9]+)”, “jo”)
  • local iterator, err = ngx.re.gmatch(“hello, world!”, “([a-z]+)”, “i”)
  • local newstr, n, err = ngx.re.sub(“hello, 1234”, “([0-9])[0-9]”, “[$0][$1]”)

os相关的

  • ngx.sleep(n) 单位是s
  • ngx.time() ngx.utctime()ngx.now() ngx.today()… 时间相关的
  • ngx.exec(“shell”)
# 其中#注释掉的可以在需要的时候开启并修改,没有注释掉的(除了下面location示例)不要删掉,基本都是必须的配置项。
###############################第一部分 全局配置############################
#user  nobody;                        指定启动进程的用户,默认不用指定即可。
#error_log  logs/error.log;           配置日志输出,虽然叫error_log但是可以定义输出的级别,默认不写是ERROR级别
#error_log  logs/error.log  notice;   
#error_log  logs/error.log  info;
#pid        logs/nginx.pid;           记录pid的文件,默认就是放到这个位置,可以修改。

# 只启动一个进程,nginx是多进程单线程模型,但是使用了epoll sendfile 非阻塞io
worker_processes  1;
###############################第二部分 event配置############################
#主要是网络连接相关的配置
events {
  # 每个worker能连接1024个链接
  worker_connections  1024;
  #use epoll; 事件驱动模型select|poll|kqueue|epoll|resig
}
###############################第三部分 http配置############################
http {
  include       mime.types;  #文件扩展名与文件类型映射表
  default_type  text/html;   #默认的返回类型,可以在server.location里面改
  sendfile        on;        #开启sendfile系统调用
  keepalive_timeout  65;     #连接超时时间65s
  
  server {
    listen       80;
    # 下面展示多个demo,demo之间互相没有依赖关系,可以单独配置来进行测试。
    # 其中demo1到demo6 是nginx相关的。
    
    ## demo1 展示location路径的不同写法优先级 #################
    # =最高优先级 表示路径完全等于,可以匹配/demo1/a/b的请求
    location =/demo1/a/b {
      echo "=/demo1/a/b";
    } 
    # ^~第二高  表示startsWith,可以匹配/demo1/a/b/c和/demo1/abc请求
    location ^~/demo1/a {
      echo "^~/demo1/a";
    }
    # ~等四个符号第三高  表示正则,如果要用{}是特殊符号,需要整个加引号,建议直接加双引号,防止出错,可以匹配/demo1/bcd
    # 其他三个:~*不区分大小写正则,!~正则不匹配的,!~*不分大小写的正则不匹配
    location "~/demo1/\w{3}$" {
      echo "regex";
    }
    # 最低 没有前置符号 /demo1 /demo111 /demo1/b/c 不符合上面三种,就会匹配到这
    location /demo1{
      echo "/demo1";
    }
    
    ### demo2 展示serve静态文件夹 ###############
    location / {
       root   html;                 # root就是根目录是当前html目录
       index  index.html index.htm; # index表示默认不写的时候转到的文件
    }
    
    ## demo3 指定错误文件 ###############
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
       root   html;
    }
    
    ## demo4 rewrite重写url rewrite也可以是server级别 ####################
    location /demo4 {
      # 一般放到最后一行
      rewrite ^/(.*) /$1/api permanent; # permanent301, redirect302, break不在匹配后面rewrite规则,last继续向下匹配。
    }
    ##### demo5 demo6 proxy_pass反向代理 ####################
    # /demo5 => baidu.com/demo5
    # /demo5/a/b => baidu.com/demo5/a/b
    location /demo5 {
      proxy_pass  https://www.baidu.com;
    }
    # /demo6 => baidu.com
    # /demo6/a/b => baidu.com/a/b
    location /demo6 {
      # proxy_set_header Host $http_host; 如果有请求头改动的需求可以搜索proxy_set_header去了解
      proxy_pass  https://www.baidu.com/;
    }
  }
  
  # 下面demo7到demo11是openresty lua的一些配置demo
  server {
    listen       81;
        
    ###### demo7 init_by_lua_block 用来加载经常用到的库 或者 用来对多进程shared变量赋值 ####################
    init_by_lua_block {
      cjson = require("cjson")       --后续的lua流程中可以直接使用cjson
      local myname = ngx.shared.info --可以认为是静态变量,通过info:get获取变量值
      info:set("name", "frank")
      info:set("age", 77)
    }
    
    ##### demo8 demo9 rewrite_by_lua_block 配合ngx.redirect用来替换rewrite指令 ####################
    # 注意rewrite_by_lua和因为作用阶段是nginx原生rewrite之后,所以容易和原生一起用的时候出错,最好的方式就是只用lua的不要用nginx的了。
    location /demo8 {
      set $a 1;
      set $b "";
      rewrite_by_lua_block {
        ngx.var.b = tonumber(ngx.var.a) + 1
        if tonumber(ngx.var.b) == 2 then
          return ngx.redirect("/demo9") --默认是302,如果要301或307可以再加一个第二参数即可
        end
      }
      echo "demo8"; # 注意echo是content阶段的,rewrite阶段重定向了请求,就走不到这里了
    }
    location /demo9 {
      echo "demo9";
    }
    ##### demo10 access_by_lua_block 用来做一些加载内容前的准备工作例如访问redis看看用户身份是不是合法 ip是不是合法等 ####################
    location /demo10 {
      access_by_lua_block {
        local res = ngx.location.capture("/auth") -- ngx.location.capture是作为客户端发起http请求拿到结果
        if res.status == ngx.HTTP_OK then
          return  -- 正常return就能走到content阶段
        end
        if res.status == ngx.HTTP_FORBIDDEN then
          ngx.exit(res.status) -- exit + 状态码 就直接返回状态码了
        end
        ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
      }
      echo "demo10"; # 如果合法的话就返回demo10字样
    }
    location /auth {
      return 200; # 换成403 or 500试试
    }
    
    #### demo10 content_by_lua_block 用来作为content阶段的脚本,一般测试用的多 ####################
    #不要和 echo proxy_pass等content阶段指令一起用
    location /demo10 {
      content_by_lua_block{
        ngx.say("/demo10");
        ngx.say("/demo11"); -- 和外部用俩echo效果类似。ngx.say ngx.print区别是前者会多个回车在最后
      }
      # echo "echo10";   如果外面用了echo,则只有echo的效果
      # proxy_pass http://www.baidu.com; 如果外面用了proxy_pass也是只有proxy_pass效果了,因为都是content阶段,content只能一个生效。
    }
    
    ##### demo11 rewrite_by_lua与proxy_pass配合 根据参数进行转发 ####################
    location /demo11 {
		  default_type text/html;
			set $proxy "";
      rewrite_by_lua '            # 千万别用content,因为content和proxy_pass阶段犯冲
				local h = ngx.var.host    # 这里从host中提出第一个.之前的部分看是不是a来决定转发到哪
				local dot = h:find("%.")
				local prefix = h:sub(1,dot-1)
				if prefix == "a" then
					ngx.var.proxy="127.0.0.1:3000"
				else
					ngx.var.proxy="127.0.0.1:5500"
				end
      ';
			proxy_pass http://$proxy$uri;
    }
  }
}

3、Nginx常用模块–数据缓存

3.1 nginx全局缓存

lua_shared_dict shared_data 1m;该配置文件配置在nginx.conf下的http模块,全局共享,会产生锁,是线程安全的

local shared_data = ngx.shared.shared_data  

local i = shared_data:get("i")  
if not i then  
    i = 1  
    shared_data:set("i", i)  
    ngx.say("lazy set i ", i, "
"
) end i = shared_data:incr("i", 1) ngx.say("i=", i, "
"
)

3.2 lua-resty-lrucache

Lua 实现的一个简单的 LRU 缓存,适合在 Lua 空间里直接缓存较为复杂的 Lua 数据结构:它相比 ngx_lua 共享内存字典可以省去较昂贵的序列化操作,相比 memcached 这样的外部服务又能省去较昂贵的 socket 操作
官方文档:https://github.com/openresty/lua-resty-lrucache

# 引用lua文件,会调用cache.lua
# 需要关闭热部署
content_by_lua_block {
  require("my/cache").go()
}

自定义函数,放在lualib/my/cache.lua

local _M = {}
-- 这是默认存在的工具包
lrucache = require "resty.lrucache"
-- 需要关闭热部署,这行代码只能执行一次
c, err = lrucache.new(200)  -- allow up to 200 items in the cache
ngx.say("count=init")

if not c then
    error("failed to create the cache: " .. (err or "unknown"))
end
function _M.go()
    count = c:get("count")
    c:set("count",100)
    ngx.say("count=", count, " --
"
) if not count then c:set("count",1) ngx.say("lazy set count ", c:get("count"), "
"
) else c:set("count",count+1) ngx.say("count=", count, "
"
) end end return _M

3.3 lua-resty-redis

参考文档:https://github.com/openresty/lua-resty-redis

使用和redis2-nginx-module差不多,一个是通过lua直接操作

3.4 lua-resty-mysql

参考文档:https://github.com/openresty/lua-resty-mysql

不推荐带参数

local mysql = require "resty.mysql"
local db, err = mysql:new()
if not db then
  ngx.say("failed to instantiate mysql: ", err)
  return
end

db:set_timeout(1000) -- 1 sec


local ok, err, errcode, sqlstate = db:connect{
  host = "192.168.44.211",
  port = 3306,
  database = "test",
  user = "root",
  password = "root",
  charset = "utf8",
  max_packet_size = 1024 * 1024,
}


ngx.say("connected to mysql.
"
) local res, err, errcode, sqlstate = db:query("drop table if exists cats") if not res then ngx.say("bad result: ", err, ": ", errcode, ": ", sqlstate, ".") return end res, err, errcode, sqlstate = db:query("create table cats " .. "(id serial primary key, " .. "name varchar(5))") if not res then ngx.say("bad result: ", err, ": ", errcode, ": ", sqlstate, ".") return end ngx.say("table cats created.") res, err, errcode, sqlstate = db:query("select * from t_emp") if not res then ngx.say("bad result: ", err, ": ", errcode, ": ", sqlstate, ".") return end local cjson = require "cjson" ngx.say("result: ", cjson.encode(res)) local ok, err = db:set_keepalive(10000, 100) if not ok then ngx.say("failed to set keepalive: ", err) return end

4、模板引擎

4.1 简介

参考文档:https://github.com/bungle/lua-resty-template

lua-resty-template模板引擎可以认为是JSP,其最终会被翻译成Lua代码,然后通过ngx.print输出。
安装的话首先需要下载包,然后将里面的template.luatemplate放在lualib/resty
基础语法

{(include_file)}:包含另一个模板文件;
{* var *}:变量输出;
{{ var }}:变量转义输出;
{% code %}:代码片段;
{# comment #}:注释;
{-raw-}:中间的内容不会解析,作为纯文本输出;

4.2 简单示例

nginx.conf进行配置

server {
  listen       80;
  server_name  localhost;

  location /lua {
    lua_code_cache off;
    default_type text/html;
    # 模板文件存放位置
    set $template_root /etc/nginx/conf.d/template;
    # lua文件保存位置
    content_by_lua_file /etc/nginx/conf.d/hello.lua;

  }
}

模板文件

DOCTYPE html>
<html>
<body>
  <h1>name:{{name}}h1>
  <h1>age:{{age}}h1>
body>
html>

lua文件

local template = require("resty.template")
local context = {
    name = "lucy",
    age = 50,
}
template.render("view.html", context)

4.3 复杂示例

模板


{(header.html)}  
   <body>  
      {# 不转义变量输出 #}  
      姓名:{* string.upper(name) *}<br/>  
      {# 转义变量输出 #}  
      简介:{{description}}
           简介:{* description *}<br/>  
      {# 可以做一些运算 #}  
      年龄: {* age + 10 *}<br/>  
      {# 循环输出 #}  
      爱好:  
      {% for i, v in ipairs(hobby) do %}  
         {% if v == '电影' then  %} - xxoo
            
              {%else%}  - {* v *} 
{% end %}  
         
      {% end %}<br/>  
  
      成绩:  
      {% local i = 1; %}  
      {% for k, v in pairs(score) do %}  
         {% if i > 1 then %},{% end %}  
         {* k *} = {* v *}  
         {% i = i + 1 %}  
      {% end %}<br/>  
      成绩2:  
      {% for i = 1, #score2 do local t = score2[i] %}  
         {% if i > 1 then %},{% end %}  
          {* t.name *} = {* t.score *}  
      {% end %}<br/>  
      {# 中间内容不解析 #}  
      {-raw-}{(file)}{-raw-}  
{(footer.html)}  

lua文件

local template = require("resty.template")
template.caching(false)
local context = {
    title = "测试",
    name = "lucy",
    description = "",
    age = 40,
    hobby = {"电影", "音乐", "阅读"},
    score = {语文 = 90, 数学 = 80, 英语 = 70},
    score2 = {
        {name = "语文", score = 90},
        {name = "数学", score = 80},
        {name = "英语", score = 70},
    }
}

template.render("view.html", context)

4.4 模板管理与缓存

模板缓存:默认开启,开发环境可以手动关闭template.caching(true)
模板文件需要业务系统更新与维护,当模板文件更新后,可以通过模板版本号或消息通知Openresty清空缓存重载模板到内存中template.cache = {}

4.5 Redis缓存+mysql+模板输出

模板

{(header.html)}  
   <body>  
      {# 不转义变量输出 #}  
      姓名:{* string.upper(name) *}<br/>  
      {# 转义变量输出 #}  

      年龄: {* age + 10 *}<br/>  
      {# 循环输出 #}  
      爱好:  
      {% for i, v in ipairs(hobby) do %}  
         {% if v == '电影' then  %} - xxoo
            
              {%else%}  - {* v *} 
{% end %}  
         
      {% end %}<br/>  
  
      成绩:  
      {% local i = 1; %}  
      {% for k, v in pairs(score) do %}  
         {% if i > 1 then %},{% end %}  
         {* k *} = {* v *}  
         {% i = i + 1 %}  
      {% end %}<br/>  
      成绩2:  
      {% for i = 1, #score2 do local t = score2[i] %}  
         {% if i > 1 then %},{% end %}  
          {* t.name *} = {* t.score *}  
      {% end %}<br/>  
      {# 中间内容不解析 #}  
      {-raw-}{(file)}{-raw-}  

掌门:
{* zhangmen *}

   {% for i = 1, #zhangmen do local z = zhangmen[i] %}  
         {* z.deptId *},{* z.age *},{* z.name *},{* z.empno *},<br>
      {% end %}<br/>  

{(footer.html)}  

lua文件

cjson = require "cjson"
sql="select * from t_emp"


local redis = require "resty.redis"
local red = redis:new()

red:set_timeouts(1000, 1000, 1000) -- 1 sec
local ok, err = red:connect("127.0.0.1", 6379)
if not ok then
  ngx.say("failed to connect: ", err)
  return
end



local res, err = red:get(sql)
if not res then
  ngx.say("failed to get sql: ", err)
  return
end

if res == ngx.null then
  ngx.say("sql"..sql.." not found.")




  --mysql查询
  local mysql = require "resty.mysql"
  local db, err = mysql:new()
  if not db then
    ngx.say("failed to instantiate mysql: ", err)
    return
  end

  db:set_timeout(1000) -- 1 sec


  local ok, err, errcode, sqlstate = db:connect{
    host = "192.168.44.211",
    port = 3306,
    database = "zhangmen",
    user = "root",
    password = "111111",
    charset = "utf8",
    max_packet_size = 1024 * 1024,
  }


  ngx.say("connected to mysql.
"
) res, err, errcode, sqlstate = db:query(sql) if not res then ngx.say("bad result: ", err, ": ", errcode, ": ", sqlstate, ".") return end --ngx.say("result: ", cjson.encode(res)) ok, err = red:set(sql, cjson.encode(res)) if not ok then ngx.say("failed to set sql: ", err) return end ngx.say("set result: ", ok) return end local template = require("resty.template") template.caching(false) local context = { title = "测试", name = "lucy", description = "", age = 40, hobby = {"电影", "音乐", "阅读"}, score = {语文 = 90, 数学 = 80, 英语 = 70}, score2 = { {name = "语文", score = 90}, {name = "数学", score = 80}, {name = "英语", score = 70}, }, zhangmen=cjson.decode(res) } template.render("view.html", context)

三、Lua开源项目

1、WAF

https://github.com/unixhot/waf
https://github.com/loveshell/ngx_lua_waf

优势
防止 SQL 注入,本地包含,部分溢出,fuzzing 测试,XSS/SSRF 等 Web 攻击
防止 Apache Bench 之类压力测试工具的攻击
屏蔽常见的扫描黑客工具,扫描器
屏蔽图片附件类目录执行权限、防止 webshell 上传
支持 IP 白名单和黑名单功能,直接将黑名单的 IP 访问拒绝
支持 URL 白名单,将不需要过滤的 URL 进行定义
支持 User-Agent 的过滤、支持 CC 攻击防护、限制单个 URL 指定时间的访问次数
支持支持 Cookie 过滤,URL 与 URL 参数过滤
支持日志记录,将所有拒绝的操作,记录到日志中去

2、Kong 基于Openresty的流量网关

2.1 介绍

https://konghq.com/
https://github.com/kong/kong

Kong 基于 OpenResty,是一个云原生、快速、可扩展、分布式的微服务抽象层(Microservice Abstraction Layer),也叫 API 网关(API Gateway),在 Service Mesh 里也叫 API 中间件(API Middleware)。Kong 开源于 2015 年,核心价值在于高性能和扩展性。从全球 5000 强的组织统计数据来看,Kong 是现在依然在维护的,在生产环境使用最广泛的 API 网关。Kong 宣称自己是世界上最流行的开源微服务 API 网关(The World’s Most Popular Open Source Microservice API Gateway)。

2.2 核心优势

可扩展:可以方便的通过添加节点水平扩展,这意味着可以在很低的延迟下支持很大的系统负载。
模块化:可以通过添加新的插件来扩展 Kong 的能力,这些插件可以通过 RESTful Admin API 来安装和配置。
在任何基础架构上运行:Kong 可以在任何地方都能运行,比如在云或混合环境中部署 Kong,单个或全球的数据中心。

3、APISIX|ABTestingGateway

3.1 介绍

https://github.com/CNSRE/ABTestingGateway

ABTestingGateway 是一个可以动态设置分流策略的网关,关注与灰度发布相关领域,基于 Nginx 和 ngx-lua 开发,使用 Redis 作为分流策略数据库,可以实现动态调度功能。
ABTestingGateway 是新浪微博内部的动态路由系统 dygateway 的一部分,目前已经开源。在以往的基于 Nginx 实现的灰度系统中,分流逻辑往往通过 rewrite 阶段的 if 和 rewrite 指令等实现,优点是性能较高,缺点是功能受限、容易出错,以及转发规则固定,只能静态分流。ABTestingGateway 则采用 ngx-lua,通过启用 lua-shared-dict 和 lua-resty-lock 作为系统缓存和缓存锁,系统获得了较为接近原生 Nginx 转发的性能。

3.2 优势

支持多种分流方式,目前包括 iprange、uidrange、uid 尾数和指定uid分流
支持多级分流,动态设置分流策略,即时生效,无需重启
可扩展性,提供了开发框架,开发者可以灵活添加新的分流方式,实现二次开发
高性能,压测数据接近原生 Nginx 转发
灰度系统配置写在 Nginx 配置文件中,方便管理员配置
适用于多种场景:灰度发布、AB 测试和负载均衡等

你可能感兴趣的:(#,中间件,nginx,lua)