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
-- 单行注释
--[[
多行注释
多行注释
]]--
定义变量
同Java 一样有,全局变量和局部变量:
- 全局 整个文件可以使用!
- 局部 指定方法内可以使用!
hello.lua
-- 全局变量赋值
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( 值 ) 返回值的类型
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 语句 由一个布尔表达式作为条件判断,其后紧跟其他语句组成。
-- 条件括号,可选:加或不加
if(布尔表达式)then
--[ 在布尔表达式为 true 时执行的语句 --]
end
if…elseif…eles…end语句
- if 语句可以与 elseif 语句, else搭配使用, 在 if 条件表达式为 false 时执行 else 语句代码块。
if——else
if(布尔表达式)then
--[ 布尔表达式为 true 时执行该语句块 --]
else
--[ 布尔表达式为 false 时执行该语句块 --]
end
if——esleif——elseif——else——end
local age =15
if (age>=18) then
print('成年人')
elseif age<18 then
print('未成年')
else
print('未出生')
end
测试:
循环
while循环[满足条件就循环] do … end
- while 循环语句在判断条件为 true 时会重复执行循环体语句。
while(条件true执行)do
print(‘执行循环内容…’)
end
for循环
- for 循环语句可以重复执行指定语句,重复次数可在 for 语句中控制。
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
则结束循环!
repeat
print(‘执行循环内容…’)
until( true退出循环 )
函数
- lua中也可以定义函数,类似于java中的方法。
--[[ 函数返回两个值的最大值 --]]
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 的一种数据结构用来帮助我们创建不同的数据类型,如:数组、字典、对象等。
-- 初始化表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
-- 文件名为 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中的类要引用别的类的效果。
要引入的模块,要放在一个文件夹下,可以是子文件!不然找不到!
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
#给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
--设置编码格式!
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
-- 定义私有方法
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
--创建连接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 脚本即可! 结构清晰…