Nginx高级应用:lua + OpenResty

2024-08-06 13:51:21 浏览数 (1)

Nginx高级应用

应用场景:

该技术不仅仅是应用于, 广告缓存 只是常用于做广告的缓存!

广告缓存载入与读取

对于一个应用来多,每天都会有很多的用户来访问, 那么访问最多的就是首页了!

而对于首页这种,高访问,且 页面数据并不是,经常的变化! 为了减轻服务器的压力,直接将其制作成一个 静态的页面进行展示!

基本流程:

  • Nginx 都学习过了:可以通过反向代理实现,网关负载均衡的 服务器! 用户发送请求,首先通过 nginx , 通过nginx代理,负载均衡,请求对应的网关…模块。
  • 本次就是在用户登录/常用的操作, 请求时经过 nginx请求,在nginx中完成 调用redis数据操作,直接返回结果! 但,niginx并不具备 编程语言的特性 if else逻辑判断,访问数据库 redis; 它只是一个负载均衡器; 所以,需要通过lua 嵌入式语言 来完成
  • 首先访问nginx ,我们可以采用缓存的方式,先从nginx本地缓存中获取,获取到直接响应
  • 如果没有获取到,再次访问redis,我们可以从redis中获取数据,如果有 则返回,并缓存到nginx中
  • 如果没有获取到,再次访问mysql 我们从mysql中获取数据,再将数据存储到redis中。
  • 而这里面,我们都可以使用LUA脚本嵌入到程序中执行这些查询相关的业务。

Lua

简介:

  • Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放 c 作为底层语言,写的东西都快! 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。

特性

  • 轻量级: 使用 C语言编写并以源代码形式开放,编译后仅仅一百余K,可以很方便的嵌入别的程序里。
  • 可扩展: ** Lua提供了非常易于使用的扩展接口和机制** 内置了大量开发好的,脚本文件 mysql redis.... (通常是C或C )提供这些功能,Lua可以使用它们,就像是本来就内置的功能一样。
  • 其它特性: 支持面向过程(procedure-oriented)编程和函数式编程(functional programming) 自动内存管理;只提供了一种通用类型的表(table) 实现数组,哈希表,集合,对象 通过闭包和table可以很方便地支持面向对象编程所需要的一些关键机制,比如数据抽象,虚函数,继承和重载等

应用场景

游戏开发独立应用脚本Web 应用脚本扩展和数据库插件如:MySQL Proxy 和 MySQL WorkBench

安装

并不需要安装,windows 只需要配置一下系统的环境变量即可: 环境变量也只是为了可以在系统的任意对方可以使用! Path: D:WSMworklua(本人的安装路径

**lua53 或 lua53 -i **都可以启动lua

入门Lua

创建hello.lua文件
打印:print("输出内容!");
  • 创建文件,输入 print("hello");
  • 直接 cmd 运行:

执行命令:lua53 文件名.lua

LUA的基本语法(了解)

介绍:

  • lua有交互式编程和脚本式编程:
  • 交互式编程就是 启动lua直接输入语法,就能直接执行
  • 脚本式编程需要编写脚本,然后再执行命令 执行脚本才可以。 一般采用脚本式编程。(例如:编写一个hello.lua的文件,输入文件内容,并执行lua53 hell.lua即可lua53 不同版本的lua 执行要指定版本号!
  • 语法都是一样的,只是操作方式不同…下面了解lua 基本语法!

脚本式编程 创建 .lua文件, 进行执行...

注释:

hello.lua

代码语言:javascript复制
-- 单行注释
--[[
	 多行注释
	 多行注释
]]--

定义变量

同Java 一样有,全局变量和局部变量:

  • 全局 整个文件可以使用!
  • 局部 指定方法内可以使用! hello.lua
代码语言:javascript复制
-- 全局变量赋值
a=1
-- 局部变量赋值
local b=2 
-- ..拼接
print(a..b)
print(a..':'..b)

a='张三' 字符可以使用,单引号 或 双引号来表示,utf-8可能会乱码,建议文件编码 ANSl 如果变量没有初始化:则 它的值为nil 这和java中的null不同。

案例

数值运算还是通过 - * /

Lua中的数据类型

  • Lua 是动态类型语言,变量不要类型定义,只需要为变量赋值。
  • 值可以存储在变量中,作为参数传递或结果返回。
  • Lua 中有 8 个基本类型分别为: nil、boolean、number、string、userdata、function、thread 和 table。

type( 值 ) 返回值的类型

代码语言:javascript复制
print(type("Hello world"))      --> string
print(type(10.4*3))             --> number		包含double int 
print(type(print))              --> function
print(type(type))               --> function
print(type(true))               --> boolean
print(type(nil))                --> nil

流程控制

if语句 类似于Orcal的存储过程!
  • if 语句 由一个布尔表达式作为条件判断,其后紧跟其他语句组成。
代码语言:javascript复制
-- 条件括号,可选:加或不加
if(布尔表达式)then  
   --[ 在布尔表达式为 true 时执行的语句 --]
end
if…elseif…eles…end语句
  • if 语句可以与 elseif 语句, else搭配使用, 在 if 条件表达式为 false 时执行 else 语句代码块。

if——else

代码语言:javascript复制
if(布尔表达式)then
   --[ 布尔表达式为 true 时执行该语句块 --]
else
   --[ 布尔表达式为 false 时执行该语句块 --]
end

if——esleif——elseif——else——end

代码语言:javascript复制
local age =15

if (age>=18) then
	print('成年人')
elseif age<18 then
	print('未成年')
else
	print('未出生')	
end 
测试:

循环

while循环[满足条件就循环] do … end
  • while 循环语句在判断条件为 true 时会重复执行循环体语句。
代码语言:javascript复制
while(条件true执行)do
   print(‘执行循环内容…’)
end
for循环
  • for 循环语句可以重复执行指定语句,重复次数可在 for 语句中控制。
代码语言:javascript复制
for var=exp1,exp2,exp3 do  
	print(‘执行循环内容…’)
end 
--[[
	var 从 exp1 变化到 exp2
	每次变化以 exp3 为步长递增 var 并执行一次 “执行体”。
	exp3 是可选的,如果不指定,默认为1。
]]--
repeat…until语句[满足条件结束]
  • repeat…until 循环语句不同于 for 和 while循环
  • for 和 while 循环的条件语句在当前循环执行开始时判断 repeat 无论如何都会执行一次until 条件成立true 则结束循环!
代码语言:javascript复制
repeat
   print(‘执行循环内容…’)
until( true退出循环 )

函数

  • lua中也可以定义函数,类似于java中的方法。
代码语言:javascript复制
--[[ 函数返回两个值的最大值 --]]
function max(num1, num2)
   if (num1 > num2) then
      result = num1;
   else
      result = num2;
   end

   return result; 
end
-- 调用函数
print("两值比较最大值为 ",max(10,4))
print("两值比较最大值为 ",max(5,6))

执行之后的结果:

table

  • table 是 Lua 的一种数据结构用来帮助我们创建不同的数据类型,如:数组、字典、对象等。
代码语言:javascript复制
-- 初始化表mytable = {}

-- 指定值mytable[1]= "Lua"

-- 移除引用mytable = nil
测试Demo
代码语言:javascript复制
table={}

table[0]="张三1"
table[1]="张三2"
table[2]="张三3"

--table 是一个对象
print(table)

--通过下标来获取值
print(table[0])

--table = nil 来清空表/对象
table = nui
print(table)
print(table[0])

模块

模块定义

  • 模块类似于一个封装库 Lua 5.1 开始,Lua 加入了标准的 模块管理机制,可以把一些公用的代码放在一个文件里; 以 API 接口的形式在其他地方调用,有利于代码的重用和降低代码耦合度。
创建一个文件叫module.lua

别忘了中文乱码,文件格式设置ASCII码 module.lua

代码语言:javascript复制
-- 文件名为 module.lua
-- 定义一个名为 module 的模块,和文件名一样!
module = {}
 -- 定义一个常量
module.constant = "这是一个常量"
 -- 定义一个函数 module.func1
function module.func1()
	print("这是一个公有函数")
end
-- 定义一个局部(私有) 方法
-- 因此是不能从外部访问模块里的这个私有函数,必须通过模块里的公有函数来调用
local function func2()
	print("这是一个私有函数!")
end
-- 模拟调用私有方法...
function module.func3()
func2()
end
 
return module
  • 模块的结构就是一个 table 的结构,
  • 因此可以像操作调用 table 里的元素那样来操作调用模块里的常量或函数。
require 函数

require 用于引入其他的模块,类似于java中的类要引用别的类的效果。 要引入的模块,要放在一个文件夹下,可以是子文件!不然找不到!

代码语言:javascript复制
require("<模块名>")
-- 或
require "<模块名>"

OpenResty

简介

  • OpenResty® 是一个基于 Nginx 与 Lua 的高性能 Web 平台 内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。 用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。
  • 简单来说: OpenResty 就是 Niginx, 集成了Lua的Niginx 使其具备了编程特性 由 OpenResty 团队自主开发) OpenResty下载

系统配置

目录结构:

D:WSMworkopenresty-1.15.8.2-win64lualibresty 路径下包含了很多的lua 模块

配置:

配置lua库位置:修改openresty-1.15.8.2-win64confnginx.conf 文件

接下来的大部分操作都是在: 自定义的lua.conf文件中执行

lua.conf

自定义的lua.conf

代码语言:javascript复制
#给nginx 分内存 128m 兆  缓存大小!用于存放热点数据(频繁使用的数据!)
lua_shared_dict my_cache 128m;

#nginx服务
server {
	listen	9090;		#指定端口9090,默认80
	server_name	_;

	#静态模板地址设置...
	set $template_location "/templates";
	set $template_root "D:/templates";		#填写对应的地址...
	
	#nginx分流的请求路径...
	location /mylua{
		default_type text/html;
		#通过 content_by_lua_block{ lua的执行代码 }   ngx.say('是lua的浏览器输出方法!')
		content_by_lua_block{
			local age = 20
			if age>=18 then
				ngx.say('>=18');
			else 
				ngx.say('<18');
			end 
		}
	}
}
  • 这就是一个正常的 nginx的配置代码:
  • content_by_lua_block{ } lua的执行操作就写在这里 是nginx拥有了编程语言的特性! OpenResty1.9.3.2以前是 content_by_lua{ } 可通过 nginx -v 查看版本号!
  • ngx.say(‘是lua的浏览器输出方法!’) 就像Java的 Out打印流一样!

启动/刷新 nginx

启动命令

OpenResty目录下 cmd

  • nginx.exe 没有异常就是执行成功!

重启刷新命令

当修改了文件,频繁调用 nginx.exe是启动,开启进行造成堵塞!正确的方法是刷新,重启 OpenResty目录下 cmd

  • nginx.exe -s reload

测试

nginx端口占用,启动报错:bind() to 0.0.0.0:80 failed (10013: An attempt was made to access a …)

  • nginx.conf文件下还有默认的 80端口启动.. 可能会与电脑里某个端口冲突, 直接更改即可! 或者:停止进程 Windows操作! netstat -aon | findstr :80 80表示端口 tasklist|findstr "进程号"

OpenResty引入lua 文件:

在nginx 配置中写lua 代码不方便书写,可以直接在外面写好,调用即可!

代码语言:javascript复制
location /mylua2{
	default_type text/html;
	content_by_lua_file D:/WSMwork/myweb.lua;
}
#content_by_lua_file 引入外部的lua文件, 分号结尾;

content_by_lua_file

引入外部的.lua 文件

定义一个文件 myweb.lua外部文件~ 刷新nginx

openresty中发起http请求

  • 有些场景是需要nginx在进行请求转发 这样就需要nginx需要有发起http请求的能力
  • nginx服务发起http请求区分内部请求 和 外部请求

为了方便测试,启动一个微服来查看

lua 脚本

myweb.lua

代码语言:javascript复制
--设置编码格式!
ngx.header.content_type="text/html;charset=utf8"

--调用ngx 模块中的方法,返回请求url
local uri_args = ngx.req.get_uri_args()
--获取请求参数!
local zz = uri_args["zz"]
--获取http 模块,require( );
local http = require("resty.http")
--http模块 new( ) 一个httpc对象
local httpc = http.new()

--..拼接参数 wd
local resp = httpc:request_uri("http://127.0.0.1:9002/info/hello/"..zz,{
	method = "GET",	--get请求;
	keepalive=false	--无论如何都请求;
})
--返回参结果,打印结果!
local val = resp.body
ngx.say(val)

测试

注意这里的参数是在 nginx,9090端口的请求参数!

require(“resty.http”)

就是这个lua模块负载发送请求, OpenResty 中存在着很多的,lua脚本模块…

获取POST请求参数

跟get 非常类似…

代码语言:javascript复制
ngx.req.read_body()					--获取请求url
local arg = ngx.req.get_post_args()	--参数集合 Map类型!
--声明几个用于存储的空对象
id=nil		
deptname=nil
--循环参数集合!
for k,v in pairs(arg) do 
	--判断参数,key 进行参数赋值!
	if k=="id" then
		id = v
	end
	if k=="deptname" then
		deptname = v
	end 
end 
--页面输出!
ngx.say("deptname: ", deptname)
ngx.say("id: ", id)

使用postman 进行Post请求:

openresty中使用redis模块

  • 在一些高并发的场景中,我们常常会用到缓存技术
  • 现在我们常用的分布式缓存redis是最知名的 我们需要引入redis模块 require “resty.redis”

lua 脚本

myweb.lua

代码语言:javascript复制
-- 定义私有方法
local function close_redis(red)
    if not red then
        return
    end
    -- 释放连接(连接池实现),毫秒
    local pool_max_idle_time = 10000 
    -- 连接池大小
    local pool_size = 100 
    local ok, err = red:set_keepalive(pool_max_idle_time, pool_size)
    local log = ngx_log
    if not ok then
        log(ngx_ERR, "set redis keepalive error : ", err)
    end
end


-- 连接redis
local redis = require('resty.redis')	--引入模块
local red = redis.new()
red:set_timeout(1000)

local ip = "127.0.0.1"
local port = "6379" 
-- local pwd= "ok"		本人没有密码直接注释!	

-- 连接测试 ip 端口是否正确...
ok, err = red:connect(ip,port)
if not ok then
    ngx.say("failed to auth: ", err)
    return close_redis(red)
end

-- 密码是否正确 没有密码直接注释!
--[[
ok, err = red:auth(pwd)
if not ok then
    ngx.say("failed to auth: ", err)
    return close_redis(red)
end
]]--
--获取redis第一个库,redis一共16给库
red:select('0')
red:set("msg","test ngx hello")	--set存入一个msg key,值:test ngx hello
-- get根据key查找... 并处理异常
local resp, err = red:get("msg")  
if not resp then  
    ngx.say("get msg error : ", err)  
    return close_redis(red)  
end
--打印关闭资源!
ngx.say("msg : ", resp)
close_redis(red)

记得要启动redis 哦!

安装目录下:redis-server.exe

查看所以key

测试:

直接网关请求!

openresty中操作mysql

数据库

既然要数据库操作,那么就搞个数据库吧!mysql

lua 脚本

myweb.lua

代码语言:javascript复制
--创建连接mysql模块
local mysql = require "resty.mysql"
-- connect to mysql;
local db, err = mysql:new()

if not db then
	return false	--没连上关闭!
end
--超时时间
db:set_timeout(1000)	
--设置连接信息
local ok, err, errno, sqlstate = db:connect{
	host = "127.0.0.1",	--ip
	port = 3306,		--端口
	database = "book",	--连接的数据库
	user = "root",		--用户
	password = "ok",	--密码
	max_packet_size = 1024 * 1024	--字节大小 1m 
}
--验证是否连接上
if not ok then
	ngx.say("connect mysql failed")
	return false	--没连上关闭
end

if db == false then
    ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
    return
end

ngx.say("----------查询数据----------------","<br/>")

res, err, errcode, sqlstate =
    db:query("SELECT * FROM bookinfo")		--实际情况,可能还需要拼接参数....

--没有数据,返回...异常!
if not res then
    ngx.say("bad result: ", err, ": ", errcode, ": ", sqlstate, ".")
    return
end
--获取JSON 模块
local cjson = require "cjson"
--JSON翻译,页面输出
ngx.say("result: ", cjson.encode(res))

--如果是新增...
--[[
--如果没有返回结果,新增异常提示!
if not res then 
    ngx.say("insert failed")
    return 
end
--返回影响行数
ngx.say("insert rows :", res.affected_rows,", id", res.insert_id, "<br/>")
]]--

结果测试:

扩展:

  • 推荐使用 nginx 引用外部的lua使用 方便排错,如果出现错误,直接控制台运行lua 脚本即可! 结构清晰…

0 人点赞