diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8db95dc --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# init +*.xml diff --git a/.idea/Lua-Nginx-Redis.iml b/.idea/Lua-Nginx-Redis.iml new file mode 100644 index 0000000..c956989 --- /dev/null +++ b/.idea/Lua-Nginx-Redis.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..efd78c1 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..b91b627 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,351 @@ + + + + + + + + + + + + + + +<<<<<<< Updated upstream + + +======= + + +>>>>>>> Stashed changes + + + + + + + + + + + + + + + + + + true + DEFINITION_ORDER + + + + + + + + + + + + + + + + + + +<<<<<<< Updated upstream + + + + + + + + + + + + + + +======= + +>>>>>>> Stashed changes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1491962114906 + + + 1492055386915 + + + 1492081028740 + + + 1492082235236 + + + +<<<<<<< Updated upstream + + + + + + + + + +<<<<<<< Updated upstream + + +======= + + +>>>>>>> Stashed changes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<<<<<<< Updated upstream + + +======= + + +>>>>>>> Stashed changes + + + + + + + + \ No newline at end of file diff --git a/Images/Nginx+Lua+Local_Redis+Mysql.png b/Images/Nginx+Lua+Local_Redis+Mysql.png new file mode 100644 index 0000000..8609124 Binary files /dev/null and b/Images/Nginx+Lua+Local_Redis+Mysql.png differ diff --git a/Images/Nginx-Phase.png b/Images/Nginx-Phase.png new file mode 100644 index 0000000..a01e312 Binary files /dev/null and b/Images/Nginx-Phase.png differ diff --git a/Images/Openresty_lua-resty-upstream-healthcheck.png b/Images/Openresty_lua-resty-upstream-healthcheck.png new file mode 100644 index 0000000..bdee67f Binary files /dev/null and b/Images/Openresty_lua-resty-upstream-healthcheck.png differ diff --git a/Images/URI-URL-Image.jpg b/Images/URI-URL-Image.jpg new file mode 100644 index 0000000..baee05d Binary files /dev/null and b/Images/URI-URL-Image.jpg differ diff --git a/Images/alipay.png b/Images/alipay.png new file mode 100644 index 0000000..64b635b Binary files /dev/null and b/Images/alipay.png differ diff --git a/Images/github_good1.png b/Images/github_good1.png new file mode 100644 index 0000000..d7bdd34 Binary files /dev/null and b/Images/github_good1.png differ diff --git a/Images/nginx-hls-locations.png b/Images/nginx-hls-locations.png new file mode 100644 index 0000000..baf90e7 Binary files /dev/null and b/Images/nginx-hls-locations.png differ diff --git a/Images/nginx_start_script.png b/Images/nginx_start_script.png new file mode 100644 index 0000000..3678c65 Binary files /dev/null and b/Images/nginx_start_script.png differ diff --git a/Images/screenshot_1588728211136.png b/Images/screenshot_1588728211136.png new file mode 100644 index 0000000..4179b16 Binary files /dev/null and b/Images/screenshot_1588728211136.png differ diff --git a/Images/tinywan-wechat.jpg b/Images/tinywan-wechat.jpg new file mode 100644 index 0000000..460d425 Binary files /dev/null and b/Images/tinywan-wechat.jpg differ diff --git a/Images/websocket_lua01.png b/Images/websocket_lua01.png new file mode 100644 index 0000000..93262ee Binary files /dev/null and b/Images/websocket_lua01.png differ diff --git a/Images/wechat.png b/Images/wechat.png new file mode 100644 index 0000000..a0f99da Binary files /dev/null and b/Images/wechat.png differ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..83dea21 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 ShaoBo Wan(无极) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Linux/Ubuntu-basic.md b/Linux/Ubuntu-basic.md new file mode 100644 index 0000000..84a6f29 --- /dev/null +++ b/Linux/Ubuntu-basic.md @@ -0,0 +1,25 @@ +## [Ubuntu14.04 从安装软件到卸载软件,删除安装包](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Linux/Ubuntu-basic.md) ++ 如何安装不知道一个软件的全称? + + 如果你想安装一个软件。比如:OpenCV库,你可以使用以下命令: + + ```bash + sudo apt-get install libopencv-dev + ``` + + 那么libopencv-dev这个名字是怎么来的,我只知道我想安装opencv + + 而直接使用:`sudo apt-get install opencv`是不行的 + + 正确使用:`apt-cache search opencv` + + 或者` sudo apt-get install aptitude && aptitude search opencv` ++ 如何知道Ubuntu里安装了哪些软件?列出所有安装的软件:` dpkg -l` ++ 如何确切知道自己是否安装了某个软件? + + 命令1:`dpkg -l filename` + + 命令2:`dpkg -l "*google*"`使用通配符就可以方便查找了 ++ 如何卸载某个软件? + + ```bash + sudo apt-get --purge remove --purge表示彻底删除 + ``` ++ 如果想删除apt-get下载的某个软件安装包呢? + + Ubuntu 中apt-get下载的安装包放在/var/cache/apt/archives里。所以可以在这个路径下删除 + + 或者使用命令1:`apt-get autoclean`,这个命令将已经删除了的软件包的.deb安装文件从硬盘中删除掉 + + 或者使用命令2:`apt-get clean `,这会把你已安装的软件包的安装包也删除掉 + \ No newline at end of file diff --git a/Linux/linux-basic.md b/Linux/linux-basic.md new file mode 100644 index 0000000..0fab293 --- /dev/null +++ b/Linux/linux-basic.md @@ -0,0 +1,40 @@ +## [Ubuntu14.04 从安装软件到卸载软件,删除安装包](Ubuntu-basic.md) +## Linux 基础知识 ++ 检查网卡是否正确工作 + 1. 检查系统路由表信息是否正确 + 1. [Linux route命令详解和使用示例](http://www.jb51.net/LINUXjishu/152385.html) + 1. 案例介绍: + ``` + www@ubuntu1:/var/log$ route + Kernel IP routing table + Destination Gateway Genmask Flags Metric Ref Use Iface + default 122.11.11.161 0.0.0.0 UG 0 0 0 em1 + 10.10.101.0 * 255.255.255.0 U 0 0 0 em2 + 122.11.11.160 * 255.255.255.224 U 0 0 0 em1 + ``` + + 默认路由为:`122.11.11.161`,绑定在`em1` 网卡上 + + 而`10.10.101.0` 段的IP仅供局域网主机之间共享数据,没对外连接访问权限,因而外界是没办法通过`10`段网络连接到服务器的 + + 如果需要`10.10.101.0` 段可以让外网放完的,则需要删除`122.11.11.161` 的默认路由,需要在`em2`网卡上添加`10`段的默认路由即可 + + 具体步骤: + ``` + www@ubuntu1:/var/log$ route delete defaul + www@ubuntu1:/var/log$ route add defaul gw 10.10.101.1 + ``` + + 此时外界就可以通过`ssh www@10.10.101.2`连接到服务器了 ++ find 命令 + + 查找超出7天前的flv的文件进行删除: + + 命令: + ```Bash + find ./ -mindepth 1 -maxdepth 3 -type f -name "*.flv" -mmin +10080 | xargs rm -rf + ``` + + `-type f` 按类型查找 + + `-mmin +10080` 7天之前的文件 + + xargs与-exec功能类似,` find ~ -type f | xargs ls -l ` + + -r 就是向下递归,不管有多少级目录,一并删除 + + -f 就是直接强行删除,不作任何提示的意思 + + 查找当前目录下.p文件中,最近30分钟内修改过的文件: + + `find . -name '*.p' -type f -mmin -30` + + 查找当前目录下.phtml文件中,最近30分钟内修改过的文件,的详细de情况加上ls: + + `find . -name '*.phtml' -type f -mmin -30 -ls` + + 查找当前目录下,最近1天内修改过的常规文件:`find . -type f -mtime -1` + + 查找当前目录下,最近1天前(2天内)修改过的常规文件:`find . -type f -mtime +1` \ No newline at end of file diff --git "a/Linux/\351\270\237\345\223\245\347\232\204Linux\347\247\201\346\210\277\350\217\234-\345\237\272\347\241\200\345\255\246\344\271\240\347\257\207(\347\254\254\345\233\233\347\211\210).pdf" "b/Linux/\351\270\237\345\223\245\347\232\204Linux\347\247\201\346\210\277\350\217\234-\345\237\272\347\241\200\345\255\246\344\271\240\347\257\207(\347\254\254\345\233\233\347\211\210).pdf" new file mode 100644 index 0000000..3afe548 Binary files /dev/null and "b/Linux/\351\270\237\345\223\245\347\232\204Linux\347\247\201\346\210\277\350\217\234-\345\237\272\347\241\200\345\255\246\344\271\240\347\257\207(\347\254\254\345\233\233\347\211\210).pdf" differ diff --git "a/Linux/\351\270\237\345\223\245\347\232\204Linux\347\247\201\346\210\277\350\217\234\346\234\215\345\212\241\345\231\250\346\236\266\350\256\276\347\257\207(\347\254\254\344\270\211\347\211\210).pdf" "b/Linux/\351\270\237\345\223\245\347\232\204Linux\347\247\201\346\210\277\350\217\234\346\234\215\345\212\241\345\231\250\346\236\266\350\256\276\347\257\207(\347\254\254\344\270\211\347\211\210).pdf" new file mode 100644 index 0000000..c61a8a8 Binary files /dev/null and "b/Linux/\351\270\237\345\223\245\347\232\204Linux\347\247\201\346\210\277\350\217\234\346\234\215\345\212\241\345\231\250\346\236\266\350\256\276\347\257\207(\347\254\254\344\270\211\347\211\210).pdf" differ diff --git a/Lua-Script/backage/math_function.lua b/Lua-Script/backage/math_function.lua new file mode 100644 index 0000000..d7a0e57 --- /dev/null +++ b/Lua-Script/backage/math_function.lua @@ -0,0 +1,21 @@ +MathFun = {} + +-- + +function Add(a,b) + return a+b +end + +-- - +function Minus(a,b) + return a+b +end + +-- * +function Multi(a,b) + return a+b +end +-- / +function Div(a,b) + return a+b +end + \ No newline at end of file diff --git a/Lua-Script/backage/module.lua b/Lua-Script/backage/module.lua new file mode 100644 index 0000000..d877498 --- /dev/null +++ b/Lua-Script/backage/module.lua @@ -0,0 +1,31 @@ +-- +-- Created by IntelliJ IDEA. +-- User: Tinywan +-- Date: 2017/5/22 +-- Time: 12:16 +-- To change this template use File | Settings | File Templates. +-- +-- 文件名为 module.lua +-- 定义一个名为 module 的模块 +module = {} + +-- 定义一个常量 +module.constant = "这是一个常量" + +-- 定义一个函数 +function module.func1() + io.write("这是一个公有函数!\n") +end + +-- 定义一个私有函数,只允许内部调用 ,如果外部 module.func2() 会提示错误:at 0x004044a0 +local function func2() + print("这是一个私有函数!") +end + +-- 通过定义一个普通方法使用外界调用,该方法是可以调用自己的私有函数的 +function module.func3() + func2() +end + +return module + diff --git a/Lua-Script/backage/module1.lua b/Lua-Script/backage/module1.lua new file mode 100644 index 0000000..42175b8 --- /dev/null +++ b/Lua-Script/backage/module1.lua @@ -0,0 +1,8 @@ +-- +-- Created by IntelliJ IDEA. +-- User: Administrator +-- Date: 2017/5/22 +-- Time: 12:16 +-- To change this template use File | Settings | File Templates. +-- + diff --git a/Lua-Script/backage/test_module.lua b/Lua-Script/backage/test_module.lua new file mode 100644 index 0000000..83a2925 --- /dev/null +++ b/Lua-Script/backage/test_module.lua @@ -0,0 +1,15 @@ +-- +-- Created by IntelliJ IDEA. +-- User: Administrator +-- Date: 2017/5/22 +-- Time: 12:25 +-- To change this template use File | Settings | File Templates. +-- +local module = require "module" + +print(module.constant) + +print(module.func3()) + + + diff --git a/Lua-Script/chapter-two/members.lua b/Lua-Script/chapter-two/members.lua index 2d896ce..b4a647a 100644 --- a/Lua-Script/chapter-two/members.lua +++ b/Lua-Script/chapter-two/members.lua @@ -1,4 +1,14 @@ members = {Tom = 10,Jake = 11,Dodo = 12,Jhon = 16} wan = {name='tinywan',age=24,schoole='QH'} -local wan_local = {name='tinywan_wan_local',age=24,schoole='wan_local'} \ No newline at end of file +local wan_local = {name='tinywan_wan_local',age=24,schoole='wan_local'} + + --|------------------------------------------------------------------------ + --| Github: https://github.com/Tinywan + --| Blog: http://www.cnblogs.com/Tinywan + --|-------------------------------------------------------------------------- + --| Author: Tinywan + --| Date: 2017/1/20 + --| Time: 16:25 + --| Mail: Overcome.wan@Gmail.com + --|------------------------------------------------------------------------ \ No newline at end of file diff --git a/Lua-Script/function/fun001.lua b/Lua-Script/function/fun001.lua new file mode 100644 index 0000000..67f9103 --- /dev/null +++ b/Lua-Script/function/fun001.lua @@ -0,0 +1,30 @@ +-- +-- Created by IntelliJ IDEA. +-- User: tinywan +-- Date: 2017/7/22 +-- Time: 12:02 +-- To change this template use File | Settings | File Templates. +-- [1] +local function factoricall(n) + if tonumber(n) == 0 then + return 1 + else + return n* factoricall(n-1) + end +end + +print(factoricall(5)) +fun1 = factoricall +print(fun1(5)) + +-- function 可以以匿名函数(anonymous function)的方式通过参数传递: anonymous +local function anonymous(tab,fun) + for k , v in pairs(tab) do + print(fun(k,v)) + end +end + +tab_demo = {name = "Tinywan",age=24 } +anonymous(tab_demo,function(key,value) + return key .. ' = '..value +end) diff --git a/Lua-Script/function1.lua b/Lua-Script/function1.lua index 7e88edf..c9e5a78 100644 --- a/Lua-Script/function1.lua +++ b/Lua-Script/function1.lua @@ -3,4 +3,17 @@ function add(a,b) return a+b end -print('这两个数的和是:',add(12,4)) \ No newline at end of file +print('这两个数的和是:',add(12,4)) + +local _name = "Tinywan" +local man = {} + +function man.GetName() + return _name +end + +function man.SetName(name) + _name = name +end + +return man \ No newline at end of file diff --git a/Lua-Script/lua-basic.md b/Lua-Script/lua-basic.md new file mode 100644 index 0000000..f420a19 --- /dev/null +++ b/Lua-Script/lua-basic.md @@ -0,0 +1,510 @@ +#### Lua 基础语法 ++ 删除一个全局变量,只要将变量值赋值为nil:`a = nil`,当且仅当一个变量不为nil 时,这个变量存在 ++ `Boolean`类型:在控制条件中除了`false`和`nil` 为假,其他值都为真,所以lua认为0和空字符串也是真 ++ `String`类型: + + 字符串替换:`string.gsub()` + ```lua + a = 'one HELLO' + b = string.gsub(a,'one','two') + print(a) -- one HELLO + print(b) -- two HELLO + ``` + + 字符串和数字 + ```lua + print("10" + 1) -- 11 + print("10" + "1") -- 11 + print("10 + 1") -- 10 + 1 + print("hello " .. " world") -- hello world + --print("hello" + 1) -- 错误写法 + ``` + + 字符串和数字转换 + ```lua + a = 10 + print(tostring(a)) -- 10 + b = "20" + print(tonumber(b)) -- "20" + + print(tostring(10) == "10") -- true + print(10 .. "" == "10") -- true + ``` ++ 表达式 + + 如果两个值类型不相等,Lua认为两者不同 + + nil 只和自己相等 + + 逻辑运算符 + ```lua + -- a and b -- 如果a为false,则返回a ,否则返回b + -- a or b -- 如果a为true,则返回a ,否则返回b + print(4 and 5) -- 5 + print(nil and 12 ) -- nill + print(false and 12) -- false + print(4 or 5) -- 4 + print(false or 5) -- 5 + ``` + + 注意:`and`的优先级比`or`高 + + Lua 三元运算符:`( a and b) or c` ++ 变量 + + 赋值语句 + ```lua + x = 20 + y = 30 + x,y = y,x + print(x,y) -- 30 20 + + a,b,c = 10,20 + print(a,b,c) --10 20 nil + + x,y,z = 10 + print(x,y,z) -- 10 nil nil + ``` + + 局部变量与代码块 + + 代码块:指一个控制结构内,一个函数体,或者一个chunk(变量被声明的哪个文件或者文本串) + + ```lua + a = 12 + if a>10 then + local i = 19 + print(i) -- 19 + end + print(i) -- nil + ``` ++ 控制语句 + ```lua + members = { Tom = 10, Jake = 11, Dodo = 12, Jhon = 16 } + + for k, v in pairs(members) do + if v == 10 then + print(k, 'is 10 years old') -- Tom is 10 years old + elseif v == 11 then + print(k, 'is 11 years old') -- Jake is 11 years old + elseif v == 12 then + print(k, 'is 12 years old') -- Dodo is 12 years old + else + print(k, "is not 10,11,12 years old") -- Jhon is not 10,11,12 years old + end + end + ``` ++ 函数 + + 单个返回值 + ```lua + function max(a,b) + if a > b then + return a + else + return b + end + end + print(max(10,20)) -- 20 + ``` + + 多个返回值 + ```lua + function more() + return 10 , 20 ,30 + end + a , b , c = more() + print(a,b,c) -- 10 20 30 + ``` + + 可变数目的参数 + ```lua + function more() + return 10 , 20 ,30 + end + -- 当函数位于最后一位的时候,返回全部值,否则值返回一个数值 + a , b , c ,d = 100, more() + print(a,b,c,d) -- 100 10 20 30 + ``` + + 闭合函数 + ```lua + function count() + -- i属于一个非局部变量,因为它既不是全局变量,也不是单纯的局部变量(因为另外一个函数可以访问到它) + local i = 0 + return function() + i =i +1 + return i + end + end + -- 以上 count()函数里的那个函数,加上一个非全局变量i,就构成一个闭合函数 + -- 所以每次调用闭合函数,非局部变量的值都不会被重置 + local func = count() + print(func()) -- 1 + print(func()) -- 2 + ``` + + 非全局函数,在定义函数的使用要注意定义函数的顺序 + ```lua + local eat + local drink + eat = function() + print("eat") + return drink() -- 这里的drink()属于尾调用 + end + drink = function() + print("drink") + end + eat() + ``` ++ table 使用 + + Lua table 第一个索引为1 + + 简单 + ```lua + a = {} + a.x = 100 + a.y = 200 + a["z"] = 300 -- a.z = 300 + print(a.x) -- 100 + print(a.y) -- 200 + print(a.z) -- 300 + ``` ++ 泛型迭代器 + + 标准库迭代器包括: + + 迭代文件每行:`io.lines` + + 迭代table元素:`pairs` + - [x] 可以遍历表中的所有key + - [x] 并且除了迭代器本身以及遍历表本身,还可以返回nil + + 迭代数组元素:`ipairs` + - [x] ipairs不能返回nil,只能返回数字0,如果遇到nil则退出 + - [x] 只能遍历表中出现的第一个不是整数的key + + 泛型迭代器 + ```lua + config = {host = '127.0.0.1',port = '3306', dbname = 'LuaDB' } + config.redis_host = "192.168.1.1" + config.redis_port = "6379" + config.redis_db = "12" + print(config['redis_host']) -- 192.168.1.1 + print(config.redis_port) -- 6379 + print(config.dbname) -- LuaDB + + for k, v in pairs(config) do + print(k,v) + end + + --[[ + host 127.0.0.1 + dbname LuaDB + redis_host 192.168.1.1 + redis_db 12 + redis_port 6379 + port 3306 + -- ]] + ``` + + 迭代table元素 + ```lua + arr = {} + for var = 1,100 do -- for 循环 + table.insert(arr,1,var) + end + + for k, v in pairs(arr) do -- 遍历表 + print(k,v) + end + --[[ 打印结果 + 1 100 + 2 99 + ... ... + 99 2 + 100 1 + + -- ]] + print(table.maxn(arr)) -- table长度 100 + print(#arr) -- table长度(快捷方式) 100 + ``` + + 迭代数组元素:`ipairs` + ```lua + arr = {host = '127.0.0.1',port = '3306','Tinywan'} + -- 如果没有找到下标为整数的则直接退出,是整数的则直接输出,如上面的'Tinywan' + for k, v in ipairs(arr) do -- 只能遍历key 为整数的下标 + print(k,v) -- 1 Tinywan + end + ``` + + 循环迭代table元素(如:lua-resty-mysql 扩展查询的数据) + + 查询 :`res, err, errcode, sqlstate = db:query("select * from tb_ngx_test order by id asc", 10)` + + 转换成JSON结果集输出2条记录: + ```lua + ngx.say("result: ", cjson.encode(res)) + result: [{"age":"24123","name":"tinywan123","address":"China","id":"1"},{"age":"24","name":"tinywan","address":"China","id":"2"}] + ``` + + 遍历该结果集: + ```lua + res, err, errcode, sqlstate = db:query("select * from tb_ngx_test order by id asc", 10) + if not res then + ngx.say("bad result: ", err, ": ", errcode, ": ", sqlstate, ".") + return + end + + for k, v in pairs(res) do + if type(v) == "table" then + for new_table_index, new_table_value in pairs(v) do + ngx.say(new_table_index.." = "..new_table_value) + end + else + ngx.say(k,v) + end + end + + --[[ 打印结果 + age = 24123 + name = tinywan123 + address = China + id = 1 + age = 24 + name = tinywan + address = China + id = 2 + ]] + ``` + + json 和 lua table 转换 + + [1] 将 json 转换成 lua table + ```lua + local json_str = '{"is_male":"nan","name":"zhangsan","id":1}' + local t = json.decode(json_str) + ngx.say(format_table(t)) + ``` + + [2] 将 lua table 转换成 json 字符串 + ```lua + local t = [[{key="table key",value="table value"}]] + local json_str = json.encode(t) + ngx.say(json_str) -- "{key=\"table key\",value=\"table value\"}" + ``` + + [3] 将lua table转换成 json 数组 (lua 两个大括号表示一个数组) + ```lua + local t = {keys={"list1","list2","list3"},num=1} + local str = json.encode(t) + ngx.say(str) -- {"keys":["list1","list2","list3"],"num":1} + ``` ++ 编译执行与错误 + + error 错误 + ```lua + local name = "Lua1" + if name ~= "Lua" + then + error("this is not Lua "); + end + ``` + + assert 错误:`assert(name~="Lua"," this is not Lua")` + + pcall 捕获错误代码 + ```lua + function test() + print(a[1]) + end + -- pcall 除了会返回true或者false外,还能返回函数的错误信息。 + -- 如果没有错误信息,err 会返回一个nil + local status,err = pcall(test) + if status then + print('success') + else + print('函数执行出错了') + print('错误信息:',err) + end + ``` ++ Lua面向对象(重点) + + [博客详细地址描述](http://www.cnblogs.com/tinywan/p/6940784.html) + + :white_check_mark: `__add` 元方法 #demo1 + ```lua + local mt = {} + mt.__add = function(t1, t2) + print("两个Table 相加的时候会调用我") + end + local t1 = {} + local t2 = {} + -- 给两个table 设置新的元表,一个元表就是一个table的值 + setmetatable(t1, mt) -- meta:元素 + setmetatable(t2, mt) + -- 进行相加操作 + local t = t1 + t2 + print(t) + + --[[输出结果 + 两个Table 相加的时候会调用我 + nil + --]] + ``` + + :white_check_mark: `__add` 元方法 #demo2 + ```lua + -- 创建一个元表 (是创建一个类吗?) + local mt = {} + mt.__add = function(s1, s2) + local result = "" + if s1.sex == "boy" and s2.sex == "girl" then + result = "一个男孩和一个女孩的家庭" + elseif s1.sex == "girl" and s2.sex == "girl" then + result = "两个女孩的家庭" + else + result = "未知孩子的家庭" + end + return result + end + -- 创建两个table,可以想象成是两个类的对象(实例化两个类) + local s1 = { name = "Per1", sex = "boy" } + local s2 = { name = "Per2", sex = "girl" } + -- 给两个table 设置新的元表,一个元表就是一个table的值 + setmetatable(s1, mt) + setmetatable(s2, mt) + -- 进行加法操作 + local result = s1 + s2 + print(result) + + -- 输出结果 一个男孩和一个女孩的家庭 + ``` + + :white_check_mark: `__index` 元方法 #demo1 + ```lua + local t = { + name = "Tinywan" + } + local mt = { + __index = function(table, key) + print("虽然你调用了我不存在的字段和方法,不过没关系,我能检测出来" .. key) + end + } + setmetatable(t, mt) + print(t.name) + print(t.age) + + --[[输出结果 + -- Tinywan + -- 虽然你调用了我不存在的字段和方法,不过没关系,我能检测出来age + -- nil + ---- ]] + ``` + + :white_check_mark: `__index` 元方法 #demo2 + ```lua + local t = { + name = "Tinywan" + } + local mt = { + money = 808080 + } + + mt.__index = mt + setmetatable(t, mt) + print(t.money) + -- 输出结果 808080 + ``` + + :white_check_mark: `__index` 元方法 #demo3 + ```lua + local t = { + name = "Tinywan" + } + local mt = { + __index = { + money = 909090 + } + } + setmetatable(t, mt) + print(t.money) + -- 输出结果 909090 + ``` + + :white_check_mark: `__index` 元方法 #demo4 + ```lua + local smartMan = { + name = "Tinywan", + age = 26, + money = 800000, + say_fun = function() + print("Tinywan say 大家好") + end + } + + local t1 = {} + local t2 = {} + local mt = { __index = smartMan } -- __index 可以是一个表,也可以是一个函数 + setmetatable(t1, mt) + setmetatable(t2, mt) + print(t1.money) + t2.say_fun() + --- 输出结果 + -- 800000 + -- Tinywan say 大家好 + ``` + + Lua面向对象1 + + Lua面向对象1 + + Lua面向对象3 更新中... ++ Lua 排序算法 + + [Lua 排序算法 - 选择排序](https://www.openresty.com.cn/ms2008-select-sort.html#section-1) + + 选择排序 + ```lua + local function selectionSort(arr) + for i = 1,#arr-1 do + local idx = i + -- 迭代剩下的元素,寻找最小的元素 + for j = i+1,#arr do + if arr[j] < arr[idx] then + idx = j + end + end + -- + arr[i],arr[idx]= arr[idx],arr[i] + end + end + + local list = { + -81, -93, -36.85, -53, -31, 79, 45.94, 36, 94, -95.03, 11, 56, 23, -39, + 14, 1, -20.1, -21, 91, 31, 91, -23, 36.5, 44, 82, -30, 51, 96, 64, -41 + } + + selectionSort(list) + print(table.concat( list, ", ")) + ``` +#### 控制结构 ++ [if-elseif-end 语句](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Lua-Script/chapter-one/if-else-example.lua) ++ [for 语句](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Lua-Script/chapter-one/for-example.lua) ++ [Lua 只有一个容器,那就是table](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Lua-Script/chapter-one/container-table.lua) +#### [Lua 实现简单封装](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Lua-Script/function1.lua) +#### Table 操作常用的方法 ++ table.concat (table [, sep [, start [, end]]]) + + concat是concatenate(连锁, 连接)的缩写. table.concat()函数列出参数中指定table的数组部分从start位置到end位置的所有元素, 元素间以指定的分隔符(sep)隔开 + + demo + ```lua + fruits = {"banana","orange","apple"} + -- 返回 table 连接后的字符串 value = banana orange apple + print("连接后的字符串 ",table.concat(fruits)) + -- 指定连接字符 value = banana, orange, apple + print("指定连接字符连接后的字符串 ",table.concat(fruits,", ")) + ``` ++ table.insert (table, [pos,] value): + + 在table的数组部分指定位置(pos)插入值为value的一个元素. pos参数可选, 默认为数组部分末尾 + + demo + ```lua + fruits = {"banana","orange","apple"} + + -- 在末尾插入 + table.insert(fruits,"Tinywan4") + print("索引为 4 的元素为 ",fruits[4]) -- 索引为 4 的元素为 Tinywan + + -- 在索引为 2 的键处插入 + table.insert(fruits,2,'Tinywan2') + print("索引为 2 的元素为 ",fruits[2]) -- 索引为 2 的元素为 Tinywan + + print("最后一个元素为 ",fruits[5]) -- 最后一个元素为 Tinywan4 + table.remove(fruits) + print("移除后最后一个元素为 ",fruits[5]) -- 移除后最后一个元素为 nil + ``` ++ table.sort (table [, comp]) + + 对给定的table进行升序排序 +#### Lua 模块与包 ++ 定义:Lua 的模块是由变量、函数等已知元素组成的 table,因此创建一个模块很简单,就是创建一个 table,然后把需要导出的常量、函数放入其中,最后返回这个 table 就行. ++ Table 操作常用的方法 + - [x] table.concat() + - [x] table.insert() + - [x] table.maxn() + - [x] table.concat() ++ Lua 实现简单封装 + + man.lua + ```Lua + local _name = "Tinywan" + local man = {} + + function man.GetName() + return _name + end + + function man.SetName(name) + _name = name + end + + return man + ``` + + 测试封装,test.lua + + ```Lua + local man = require('man') + print("The man name is "..man.GetName()) + man.SetName("Phalcon") + print("The man name is "..man.GetName()) + ``` \ No newline at end of file diff --git a/Lua-Script/metatable/__add.lua b/Lua-Script/metatable/__add.lua new file mode 100644 index 0000000..3e8270e --- /dev/null +++ b/Lua-Script/metatable/__add.lua @@ -0,0 +1,40 @@ +<<<<<<< HEAD +======= +-- +-- Created by IntelliJ IDEA. +-- User: Administrator +-- Date: 2017/6/6 +-- Time: 8:57 +-- To change this template use File | Settings | File Templates. +-- +>>>>>>> 05e5321a6a0cc4d269c9c15fb03540a680c2a801 +--在 Lua 5.1 语言中,元表 (metatable) 的表现行为类似于 C++ 语言中的操作符重载,例如我们可以重载 "__add" 元方法 (metamethod),来计算两个 Lua 数组的并集;或者重载 "__index" 方法,来定义我们自己的 Hash 函数。Lua 提供了两个十分重要的用来处理元表的方法,如下: +--setmetatable(table, metatable):此方法用于为一个表设置元表。 +--getmetatable(table):此方法用于获取表的元表对象 + +------------------------------------------------------ 通过重载 "__add" 元方法来计算集合的并集实例: +local set1 = {10,20,30} +local set2 = {40,50,60} +-- 将用于重载__add的函数,注意第一个参数是self +local union = function (self, another) + local set = {} + local result = {} + + -- 利用数组来确保集合的互异性 + for i, j in pairs(self) do set[j] = true end + for i, j in pairs(another) do set[j] = true end + + -- 加入结果集合 + for i, j in pairs(set) do table.insert(result, i) end + return result +end +setmetatable(set1, {__add = union}) -- 重载 set1 表的 __add 元方法 + +local set3 = set1 + set2 +for _, j in pairs(set3) do + io.write(j.." ") -->output:30 50 20 40 10 +<<<<<<< HEAD +end +======= +end +>>>>>>> 05e5321a6a0cc4d269c9c15fb03540a680c2a801 diff --git a/Lua-Script/metatable/__index.lua b/Lua-Script/metatable/__index.lua new file mode 100644 index 0000000..99daf48 --- /dev/null +++ b/Lua-Script/metatable/__index.lua @@ -0,0 +1,28 @@ +-- +-- Created by IntelliJ IDEA. +-- User: Administrator +-- Date: 2017/5/23 +-- Time: 16:51 +-- To change this template use File | Settings | File Templates. +--[1] +--local other = { foo = 3 } +--t = setmetatable({}, { __index = other }) +--print(t.foo) -- 3 +--print(t.far) -- nil + +--[2] 如果__index包含一个函数的话,Lua就会调用那个函数,table和键会作为参数传递给函数 +--mytable = setmetatable({key1 = "value1"}, { +-- __index = function(mytable, key) +-- if key == "key2" then +-- return "metatablevalue" +-- else +-- return nil +-- end +-- end +--}) +-- +--print(mytable.key1,mytable.key2) +mytable = setmetatable({key1 = "value1"}, { __index = { key2 = "metatablevalue123" } }) +print(mytable.key1,mytable.key2) -- value1 metatablevalue123 + + diff --git a/Lua-Script/metatable/__newindex.lua b/Lua-Script/metatable/__newindex.lua new file mode 100644 index 0000000..8ef4e96 --- /dev/null +++ b/Lua-Script/metatable/__newindex.lua @@ -0,0 +1,35 @@ +-- +-- Created by IntelliJ IDEA. +-- User: Administrator +-- Date: 2017/5/23 +-- Time: 16:57 +-- To change this template use File | Settings | File Templates. + +--__newindex 元方法用来对表更新,__index则用来对表访问 +--当你给表的一个缺少的索引赋值,解释器就会查找__newindex 元方法:如果存在则调用这个函数而不进行赋值操作 +--mymetatable = {} +--mytable = setmetatable({key1 = "Value1"}, {__newindex = mymetatable}) +-- +--print(mytable.key1) -- Value1 +-- +--mytable.newkey = 'New Value2' +--print(mytable.newkey,mymetatable.newkey) -- nil New Value2 +-- +--mytable.key1 = "新值1" +--print(mytable.key1,mymetatable.key1) -- 新值1 nil + +--以上实例中表设置了元方法 __newindex +--[1] 在对新索引键(newkey)赋值时(mytable.newkey = "新值2"),会调用元方法,而不进行赋值。 +--[2] 而如果对已存在的索引键(key1),则会进行赋值,而不调用元方法 __newindex + +-- 以下实例使用了 rawset 函数来更新表 +mytable = setmetatable({key1 = "value1"}, { __newindex = + function(mytable, key, value) + rawset(mytable, key, "\""..value.."\"") + end +}) + +mytable.key1 = "new value" +mytable.key2 = 4 + +print(mytable.key1,mytable.key2) diff --git a/Lua-Script/metatable/test.lua b/Lua-Script/metatable/test.lua new file mode 100644 index 0000000..f640c8b --- /dev/null +++ b/Lua-Script/metatable/test.lua @@ -0,0 +1,17 @@ +-- +-- Created by IntelliJ IDEA. +-- User: Administrator +-- Date: 2017/5/22 +-- Time: 12:53 +-- To change this template use File | Settings | File Templates. +--- 普通表 +mytable = {} + +-- 元表 +mymetatable = {} + +-- 把 mymetatable 设为 mytable 的元表 以上代码也可以直接写成一行 mytable = setmetatable({},{}) +setmetatable(mytable,mymetatable) + +-- 这回返回mymetatable +getmetatable(mytable) \ No newline at end of file diff --git a/Lua-Script/oop/account.lua b/Lua-Script/oop/account.lua new file mode 100644 index 0000000..312c243 --- /dev/null +++ b/Lua-Script/oop/account.lua @@ -0,0 +1,32 @@ +-- +-- Created by IntelliJ IDEA. +-- User: Administrator +-- Date: 2017/5/24 +-- Time: 9:10 +-- To change this template use File | Settings | File Templates. +-- Lua 面向对象编程 那么LUA中的类可以通过table + function模拟出来 +local _M = {} +-- setmetatable(mytable,mymetatable) -- 把 mymetatable 设为 mytable 的元表 +local mt = { __index = _M } + +function _M.deposit (self, v) + self.balance = self.balance + v +end + +function _M.withdraw (self, v) + if self.balance > v then + self.balance = self.balance - v + else + error("insufficient funds") + end +end + +-- 那么LUA中的类可以通过table + function模拟出来 +function _M.new (self, balance) + balance = balance or 0 + return setmetatable({balance = balance}, mt) -- 把 mt 设为 balance 的元表,其中 mt 代表 { __index = _M } +end +--setmetatable 将 _M 作为新建表的原型,所以在自己的表内找不到 'deposit'、'withdraw' 这些方法和变量的时候,便会到 __index 所指定的 _M 类型中去寻找 + +return _M + diff --git a/Lua-Script/oop/account/account.lua b/Lua-Script/oop/account/account.lua new file mode 100644 index 0000000..59b51e6 --- /dev/null +++ b/Lua-Script/oop/account/account.lua @@ -0,0 +1,28 @@ +-- +-- Created by IntelliJ IDEA. +-- User: Administrator +-- Date: 2017/6/6 +-- Time: 17:14 +-- To change this template use File | Settings | File Templates. +-- +local _M = {} + +local mt = { __index = _M } + +function _M.deposit (self, v) + self.balance = self.balance + v +end + +function _M.withdraw (self, v) + if self.balance > v then + self.balance = self.balance - v + else + error("insufficient funds") + end +end + +function _M.new (self, balance) + balance = balance or 0 + return setmetatable({balance = balance}, mt) +end + diff --git a/Lua-Script/oop/account_require.lua b/Lua-Script/oop/account_require.lua new file mode 100644 index 0000000..024dbab --- /dev/null +++ b/Lua-Script/oop/account_require.lua @@ -0,0 +1,18 @@ +-- +-- Created by IntelliJ IDEA. +-- User: Administrator +-- Date: 2017/5/24 +-- Time: 9:18 +-- To change this template use File | Settings | File Templates. +-- +local account = require "account" + +local a = account:new() +a:deposit(100) + +local b = account:new() +b:deposit(50) + +print(a.balance) --> output: 100 +print(b.balance) --> output: 50 + diff --git a/Lua-Script/oop/class01.lua b/Lua-Script/oop/class01.lua new file mode 100644 index 0000000..8e39d13 --- /dev/null +++ b/Lua-Script/oop/class01.lua @@ -0,0 +1,29 @@ +-- +local Sharp = { _val = 1 } --① 父类 + +function Sharp:new() + local new_sharp = {} + self.__index = self --②,self == Sharp Sharp.__index = Sharp 等价于 Sharp.__index = function(key) return Sharp[key] end + setmetatable(new_sharp, self) + return new_sharp +end + +-- define fun1 +function Sharp:sharp_func() + print("Sharp call sharp_func") +end + +-- define fun2 +function Sharp:name() + print("Sharp call name") +end + +-- define fun3 +function Sharp:val() + print(string.format("Sharp call val %d", self._val)) +end + +local sharp = Sharp:new() +sharp:sharp_func() +sharp:name() +sharp:val() \ No newline at end of file diff --git a/Lua-Script/oop/class02.lua b/Lua-Script/oop/class02.lua new file mode 100644 index 0000000..b5dc5ff --- /dev/null +++ b/Lua-Script/oop/class02.lua @@ -0,0 +1,61 @@ +-- 类的继承 +local Sharp = { _val = 1 } --① 父类 + +function Sharp:new() + local new_sharp = {} + self.__index = self --②,self == Sharp Sharp.__index = Sharp 等价于 Sharp.__index = function(key) return Sharp[key] end + setmetatable(new_sharp, self) + return new_sharp +end + +-- define fun1 +function Sharp:sharp_func() + print("Sharp call sharp_func") +end + +-- define fun2 +function Sharp:name() + print("Sharp call name") +end + +-- define fun3 +function Sharp:val() + print(string.format("Sharp call val %d", self._val)) +end + +Circle = Sharp:new() --① 子类 +function Circle:new() + local new_circle = {} + self.__index = self --②,self == Circle + setmetatable(new_circle, self) --③ + + return new_circle +end + +--新函数 +function Circle:circle_func() + print("Circle call circle_func") +end + +--覆盖函数name +function Circle:name() + print("Circle call name") +end + +--覆盖函数val +function Circle:val() + print(string.format("Circle call val %d", self._val)) +end + +local circle = Circle:new() +circle._val = 2 --覆盖赋值 +circle:sharp_func() --调用父类函数 +circle:circle_func() --调用新函数 +circle:name() --调用覆盖函数 +circle:val() --调用覆盖函数 + +--输出结果 +--Sharp call sharp_func +--Circle call circle_func +--Circle call name +--Circle call val 2 \ No newline at end of file diff --git a/Lua-Script/oop/class03.lua b/Lua-Script/oop/class03.lua new file mode 100644 index 0000000..61d6499 --- /dev/null +++ b/Lua-Script/oop/class03.lua @@ -0,0 +1,71 @@ +-- 类的继承 +local Sharp = { _val = 1 } --① 父类 + +function Sharp:new() + local new_sharp = {} + self.__index = self --②,self == Sharp Sharp.__index = Sharp 等价于 Sharp.__index = function(key) return Sharp[key] end + setmetatable(new_sharp, self) + return new_sharp +end + +-- define fun1 +function Sharp:sharp_func() + print("Sharp call sharp_func") +end + +-- define fun2 +function Sharp:name() + print("Sharp call name") +end + +-- define fun3 +function Sharp:val() + print(string.format("Sharp call val %d", self._val)) +end + +Circle = Sharp:new() --① 子类 +function Circle:new() + local new_circle = {} + self.__index = self --②,self == Circle + setmetatable(new_circle, self) --③ + + return new_circle +end + +--新函数 +function Circle:circle_func() + print("Circle call circle_func") +end + +--覆盖函数name +function Circle:name() + print("Circle call name") +end + +--覆盖函数val +function Circle:val() + print(string.format("Circle call val %d", self._val)) +end + +--输出结果 +--Sharp call sharp_func +--Circle call circle_func +--Circle call name +--Circle call val 2 + +--调用父类被子类覆盖了的name方法 +function Circle:sharp_name() + local circle_metatable = getmetatable(self) +-- local sharp_metatable = getmetatable(circle_metatable) +-- sharp_metatable["name"]() + print(circle_metatable) +end + +local circle1 = Circle:new() +circle1.name() +circle1.sharp_name() +-- circle._val = 2 --覆盖赋值 +-- circle:sharp_func() --调用父类函数 +-- circle:circle_func() --调用新函数 +-- circle:name() --调用覆盖函数 +-- circle:val() --调用覆盖函数 \ No newline at end of file diff --git a/Lua-Script/oop/oop1.lua b/Lua-Script/oop/oop1.lua new file mode 100644 index 0000000..b5311c4 --- /dev/null +++ b/Lua-Script/oop/oop1.lua @@ -0,0 +1,41 @@ +-- +-- Created by IntelliJ IDEA. +-- User: Administrator +-- Date: 2017/5/23 +-- Time: 17:12 +-- To change this template use File | Settings | File Templates. +-- 那么LUA中的类可以通过table + function模拟出来 + +-- Meta class +Shape = { area = 0 } + +-- 基础类方法 new +function Shape:new(o, side) + o = o or {} + setmetatable(o, self) -- self其实就相当于Java,C++中的this对象 + self.__index = self + side = side or 0 + self.area = side * side; + return o +end + +-- 基础类方法 printArea +function Shape:printArea() + print("面积为 ", self.area) +end + +-- 创建对象 +myshape = Shape:new(nil, 10) + +-- 对象实例去访问该类的方法 +myshape:printArea() + +--setmetatable(table,metatable): 对指定table设置元表(metatable) + +function _M.new(self) + local sock, err = tcp() + if not sock then + return nil, err + end + return setmetatable({ sock = sock }, mt) +end \ No newline at end of file diff --git a/Lua-Script/oop/oop3.lua b/Lua-Script/oop/oop3.lua new file mode 100644 index 0000000..27e58fd --- /dev/null +++ b/Lua-Script/oop/oop3.lua @@ -0,0 +1,127 @@ +-- +-- Created by IntelliJ IDEA. +-- User: Administrator +-- Date: 2017/5/25 +-- Time: 17:02 +-- To change this template use File | Settings | File Templates. +-- +----------------------------------------------------------- Lua面向对象1 +-- local mt = {} +-- mt.__add = function(t1, t2) +-- print("两个Table 相加的时候会调用我") +-- end +-- local t1 = {} +-- local t2 = {} +--- - 给两个table 设置新的元表,一个元表就是一个table的值 +-- setmetatable(t1, mt) -- meta:元素 +-- setmetatable(t2, mt) +---- 进行相加操作 +-- local t = t1 + t2 +-- print(t) + +--[[输出结果 +两个Table 相加的时候会调用我 +nil +--]] + +----------------------------------------------------------- Lua面向对象2 +-- 创建一个元表 (是创建一个类吗?) +local mt = {} +mt.__add = function(s1, s2) + local result = "" + if s1.sex == "boy" and s2.sex == "girl" then + result = "一个男孩和一个女孩的家庭" + elseif s1.sex == "girl" and s2.sex == "girl" then + result = "两个女孩的家庭" + else + result = "未知孩子的家庭" + end + return result +end +-- 创建两个table,可以想象成是两个类的对象(实例化两个类) +local s1 = { name = "Per1", sex = "boy" } +local s2 = { name = "Per2", sex = "girl" } +-- 给两个table 设置新的元表,一个元表就是一个table的值 +setmetatable(s1, mt) +setmetatable(s2, mt) +-- 进行加法操作 +local result = s1 + s2 +print(result) -- 一个男孩和一个女孩的家庭 + +----------------------------------------------------------- Lua面向对象2 +local t = { + name = "Tinywan" +} +local mt = { + __index = function(table, key) + print("虽然你调用了我不存在的字段和方法,不过没关系,我能检测出来" .. key) + end +} +setmetatable(t, mt) +print(t.name) +print(t.age) +--[[输出结果 +-- Tinywan +-- 虽然你调用了我不存在的字段和方法,不过没关系,我能检测出来age +-- nil +---- ]] + +----------------------------------------------------------- Lua面向对象2 +local t = { + name = "Tinywan" +} +local mt = { + money = 808080 +} + +mt.__index = mt +setmetatable(t, mt) +print(t.money) +-- 输出结果 808080 + +------------------------------------------------------------- Lua面向对象2 +local t = { + name = "Tinywan" +} +local mt = { + __index = { + money = 909090 + } +} +setmetatable(t, mt) +print(t.money) +-- 输出结果 909090 + +----------------------------------------------------------- Lua面向对象2 +local smartMan = { + name = "Tinywan", + age = 26, + money = 800000, + say_fun = function() + print("Tinywan say 大家好") + end +} + +local t1 = {} +local t2 = {} +local mt = { __index = smartMan } -- __index 可以是一个函数,也可以是一个函数 +setmetatable(t1, mt) +setmetatable(t2, mt) +print(t1.money) +t2.say_fun() +--- 输出结果 +-- 800000 +-- Tinywan say 大家好 + + + + + + + + + + + + + diff --git a/Lua-Script/oop/oop4.lua b/Lua-Script/oop/oop4.lua new file mode 100644 index 0000000..2ca29ee --- /dev/null +++ b/Lua-Script/oop/oop4.lua @@ -0,0 +1,115 @@ +-- +-- Created by IntelliJ IDEA. +-- User: Administrator +-- Date: 2017/5/25 +-- Time: 17:02 +-- To change this template use File | Settings | File Templates. +----------------------------------------------------------- Lua面向对象2 +-- local smartMan = { +-- name = "Tinywan", +-- age = 26, +-- money = 800000, +-- sayHello = function() +-- print("Tinywan say 大家好") +-- end +-- } +-- local t1 = {} +-- local mt = { +-- __index = smartMan, +-- __newindex = function(table, key, value) +-- print(key .. "字段不存在不要试图给他赋值") +-- end +-- } +-- setmetatable(t1, mt) +-- t1.sayHello = function() +-- print("HAHA") +-- end +-- t1.sayHello() +----- 输出结果 +---- sayHello字段不存在不要试图给他赋值 +---- Tinywan say 大家好 + +----------------------------------------------------------- Lua面向对象3 +-- local smartMan = { +-- name = "none" +-- } +-- local other = { +-- name = "大家好,我是无赖的table" +-- } +-- local t1 = {} +-- local mt = { +-- __index = smartMan, +-- __newindex = other +-- } +-- setmetatable(t1, mt) +-- print("other的名字,赋值前:" .. other.name) +-- t1.name = "峨眉大侠" +-- print("other的名字,赋值后:" .. other.name) +-- print("t1 的名字:" .. t1.name) +----- 输出结果 +---- other的名字,赋值前:大家好,我是无赖的table +---- other的名字,赋值后:峨眉大侠 +---- t1 的名字:none + +----------------------------------------------------------- Lua面向对象3 +a = 'fdfd' +for n in pairs(_G) do + print(n) -- 打印全局的函数和变量 +end + +--输出结果 +--coroutine +--assert +--tostring +--tonumber +--io +--rawget +--xpcall +--arg +--ipairs +--print +--pcall +--gcinfo +--module +--setfenv +--getfenv +--pairs +--jit +--bit +--package +--error +--debug +--loadfile +--rawequal +--loadstring +--rawset +--unpack +--table +--require +--_VERSION +--newproxy +--collectgarbage +--dofile +--next +--math +--load +--os +--_G +--select +--string +--type +--getmetatable +--a (自己定义的也出来了) +--setmetatable + + + + + + + + + + + + diff --git a/Lua-Script/oop/opp2.lua b/Lua-Script/oop/opp2.lua new file mode 100644 index 0000000..ee91c01 --- /dev/null +++ b/Lua-Script/oop/opp2.lua @@ -0,0 +1,27 @@ +-- +-- Created by IntelliJ IDEA. +-- User: Administrator +-- Date: 2017/5/23 +-- Time: 17:29 +-- To change this template use File | Settings | File Templates. +-- +--local a = {} -- 普通表 +--local b = {k = 11} -- 元表 +--setmetatable(a,b) -- 把 b 设为 a 的元表 +--print(a.k) -- nil +--b.__index = b --设置元方法 +--b.v = 22 --给b表增加一个属性 +--a.aa = 33 --给a表增加一个属性 +--print(a.k) --11,因为a有元表b,且存在元方法,可以索引到b表中对应的值 +--print(a.v) --22,设定关系后,b增加的属性,a也可以索引到 +--print(b.aa) --nil,相反,b并不可以索引到a的值 + +local a = {} -- 普通表 +local b = {k = 11} -- 元表 +setmetatable(a,{__index = b } ) +print(a.k) +b.v = 1020 +a.aa = 3030 +print(a.v) --22,设定关系后,b增加的属性,a也可以索引到 +print(a.aa) --nil,相反,b并不可以索引到a的值 + diff --git a/Lua-Script/oop/self__index.md b/Lua-Script/oop/self__index.md new file mode 100644 index 0000000..ff35403 --- /dev/null +++ b/Lua-Script/oop/self__index.md @@ -0,0 +1,16 @@ +## lua中self.__index = self是什么意思? ++ 设置原表语法:`setmetatable(table,metatable): 对指定table设置元表(metatable)` ++ [http://www.cnblogs.com/mentalidade/p/6561418.html](http://www.cnblogs.com/mentalidade/p/6561418.html) ++ 当一个表存在元表的时候可能会发生改变。lua当发现一个不存在的key时,如果这个table存在元表,则会尝试在元表中寻找是否有匹配key对应的value + ```lua + local a = {} -- 普通表 + local b = {k = 11} -- 元表 + setmetatable(a,b) -- 把 b 设为 a 的元表 + b.__index = b --设置元方法 + b.v = 22 --给b表增加一个属性 + a.aa = 33 --给a表增加一个属性 + print(a.k) --11,因为a有元表b,且存在元方法,可以索引到b表中对应的值 + print(a.v) --22,设定关系后,b增加的属性,a也可以索引到 + print(b.aa) --nil,相反,b并不可以索引到a的值 + + ``` \ No newline at end of file diff --git a/Lua-Script/oop/self_class01.lua b/Lua-Script/oop/self_class01.lua new file mode 100644 index 0000000..ecd1372 --- /dev/null +++ b/Lua-Script/oop/self_class01.lua @@ -0,0 +1,32 @@ +-- +-- Created by IntelliJ IDEA. +-- User: Administrator +-- Date: 2017/6/6 +-- Time: 9:47 +-- To change this template use File | Settings | File Templates. +-- 自己尝试着写一个类哦 + +-- +local ok, new_tab = pcall(require, "table.new") +if not ok or type(new_tab) ~= "function" then + new_tab = function (narr, nrec) return {} end +end + +local _M = new_tab(0, 54) --① 父类 + +_M._VERSION = '0.01' + +function _M:new() + local new_obj = {} + self.__index = self --②,self == _M + setmetatable(new_obj, self) + return new_obj +end + +-- 定义个简单的方法 +function _M:func() + print("Function func ") +end + +return _M + diff --git a/Lua-Script/oop/self_class02.lua b/Lua-Script/oop/self_class02.lua new file mode 100644 index 0000000..79b86bb --- /dev/null +++ b/Lua-Script/oop/self_class02.lua @@ -0,0 +1,12 @@ +-- +-- Created by IntelliJ IDEA. +-- User: Administrator +-- Date: 2017/6/6 +-- Time: 9:49 +-- To change this template use File | Settings | File Templates. +-- +local class01 = require "self_class01" +print("_VERSION == "..class01._VERSION) +local func = class01:func() +print(func) + diff --git a/Lua-Script/sort/selectionSort.lua b/Lua-Script/sort/selectionSort.lua new file mode 100644 index 0000000..da5eac4 --- /dev/null +++ b/Lua-Script/sort/selectionSort.lua @@ -0,0 +1,34 @@ +-- +-- Created by IntelliJ IDEA. +-- User: Administrator +-- Date: 2017/6/14 +-- Time: 10:08 +-- To change this template use File | Settings | File Templates. +--首先在未排序序列中找到最小元素,存放到排序序列的起始位置 再从剩余未排序元素中继续寻找最小元素,然后放到已排序序列的末尾。 重复第二步,直到所有元素均排序完毕。 +local function selectionSort(arr) + for i = 1,#arr-1 do + local idx = i + -- 迭代剩下的元素,寻找最小的元素 + for j = i+1,#arr do + if arr[j] < arr[idx] then + idx = j + end + end + -- + arr[i],arr[idx]= arr[idx],arr[i] + end +end + +local list = { + -81, -93, -36.85, -53, -31, 79, 45.94, 36, 94, -95.03, 11, 56, 23, -39, + 14, 1, -20.1, -21, 91, 31, 91, -23, 36.5, 44, 82, -30, 51, 96, 64, -41 +} + +selectionSort(list) +print(table.concat( list, ", ")) +print(#list) +for i = 1 ,#list do + print(i) +end + + diff --git a/Lua-Script/string/lua04.lua b/Lua-Script/string/lua04.lua new file mode 100644 index 0000000..71b8913 --- /dev/null +++ b/Lua-Script/string/lua04.lua @@ -0,0 +1,103 @@ +-- +-- Created by IntelliJ IDEA. +-- User: tinywan +-- Date: 2017/5/31 +-- Time: 20:42 +-- To change this template use File | Settings | File Templates. +-- +--config = {host = '127.0.0.1',port = '3306', dbname = 'LuaDB' } +--config.redis_host = "192.168.1.1" +--config.redis_port = "6379" +--config.redis_db = "12" +--print(config['redis_host']) -- 192.168.1.1 +--print(config.redis_port) -- 6379 +--print(config.dbname) -- LuaDB +-- +--for k, v in pairs(config) do +-- print(k,v) +--end +-- +----[[ +--host 127.0.0.1 +--dbname LuaDB +--redis_host 192.168.1.1 +--redis_db 12 +--redis_port 6379 +--port 3306 +---- ]] +--arr = {} +--for var = 1,100 do -- for 循环 +-- table.insert(arr,1,var) +--end +-- +--for k, v in pairs(arr) do -- 遍历表 +-- print(k,v) +--end +----[[ +--1 100 +--2 99 +--... ... +--99 2 +--100 1 +-- +---- ]] +--print(table.maxn(arr)) -- table长度 100 +--print(#arr) -- table长度(快捷方式) 100 + +--arr = {host = '127.0.0.1',port = '3306','Tinywan'} +---- 如果没有找到下标为整数的则直接退出,是整数的则直接输出,如上面的'Tinywan' +--for k, v in ipairs(arr) do -- 只能遍历key 为整数的下标 +-- print(k,v) -- 1 Tinywan +-- end + +--function count() +-- -- i属于一个非局部变量,因为它既不是全局变量,也不是单纯的局部变量(因为另外一个函数可以访问到它) +-- local i = 0 +-- return function() +-- i =i +1 +-- return i +-- end +--end +---- 以上 count()函数里的那个函数,加上一个非全局变量i,就构成一个闭合函数 +---- 所以每次调用闭合函数,非局部变量的值都不会被重置 +--local func = count() +--print(func()) -- 1 +--print(func()) -- 2 + +--local eat +--local drink +--eat = function() +-- print("eat") +-- return drink() -- 这里的drink()属于尾调用,游戏角色的状态切换 +--end +--drink = function() +-- print("drink") +--end +--eat() +--local name = "Lua1" +--if name ~= "Lua" +--then +-- error("[error] this is not Lua"); +--end +--assert(name~="Lua"," this is not Lua") + +function test() + print(a[1]) +end + +-- pcall 除了会返回true或者false外,还能返回函数的错误信息。如果没有错误信息,err 会返回一个nil +local status,err = pcall(test) +if status then + print('success') +else + print('函数执行出错了') + print('错误信息:',err) +end + + + + + + + + diff --git a/Lua-Script/string/string1.lua b/Lua-Script/string/string1.lua new file mode 100644 index 0000000..82a43ec --- /dev/null +++ b/Lua-Script/string/string1.lua @@ -0,0 +1,83 @@ +-- +-- Created by IntelliJ IDEA. +-- User: tinywan +-- Date: 2017/5/27 +-- Time: 22:33 +-- To change this template use File | Settings | File Templates. +-- +--a = 'one HELLO' +--b = string.gsub(a,'one','two') +--print(a) -- one HELLO +--print(b) -- two HELLO + +--print("10" + 1) -- 11 +--print("10" + "1") -- 11 +--print("10 + 1") -- 10 + 1 +--print("hello " .. " world") -- hello world +--- -print("hello" + 1) -- 错误写法 + +--a = 10 +--print(tostring(a)) -- 10 +--b = "20" +--print(tonumber(b)) -- "20" +-- +--print(tostring(10) == "10") -- true +--print(10 .. "" == "10") -- true + +-- a and b -- 如果a为false,则返回a ,否则返回b +-- a or b -- 如果a为true,则返回a ,否则返回b +--print(4 and 5) -- 5 +--print(nil and 12 ) -- nill +--print(false and 12) -- false +--print(4 or 5) -- 4 +--print(false or 5) -- 5 +--x = 20 +--y = 30 +--x,y = y,x +--print(x,y) -- 30 20 + +--a,b,c = 10,20 +--print(a,b,c) --10 20 nil +-- +--x,y,z = 10 +--print(x,y,z) -- 10 nil nil +--a = 12 +--if a>10 then +-- local i = 19 +-- print(i) -- 19 +--end +--print(i) -- nil + +--members = { Tom = 10, Jake = 11, Dodo = 12, Jhon = 16 } +-- +--for k, v in pairs(members) do +-- if v == 10 then +-- print(k, 'is 10 years old') +-- elseif v == 11 then +-- print(k, 'is 11 years old') +-- elseif v == 12 then +-- print(k, 'is 12 years old') +-- else +-- print(k, "is not 10,11,12 years old") +-- end +--end + +--function more() +-- return 10 , 20 ,30 +--end +---- 当函数位于最后一位的时候,返回全部值,否则值返回一个数值 +--a , b , c ,d = 100, more() +--print(a,b,c,d) -- 100 10 20 30 +a = {} +a.x = 100 +a.y = 200 +a["z"] = 300 -- a.z = 300 +print(a.x) -- 100 +print(a.y) -- 200 +print(a.z) -- 300 + + + + + + diff --git a/Nginx-Develop/command-order-01.md b/Nginx-Develop/command-order-01.md index 6241dae..d2d876a 100644 --- a/Nginx-Develop/command-order-01.md +++ b/Nginx-Develop/command-order-01.md @@ -23,4 +23,129 @@ echo $a; echo $a; ``` - + 两个命令 设置在执行阶段`rewrite`,两个命令 回声在随后阶段执行`content`。不同阶段的命令无法执行。 \ No newline at end of file + + 两个命令 设置在执行阶段`rewrite`,两个命令 回声在随后阶段执行`content`。不同阶段的命令无法执行。 + ++ Lua 脚本变量 ` ngx.var.remote_addr` = Nginx的的内建变量 ` $REMOTE_ADDR` +## Nginx指令执行命令(05) ++ 多个命令的执行顺序 + + 顺序: ` rewrite phase `=> `access phase` => `content phase` + ``` + location /test_rewrite_access_content { + # rewrite phase + set $age 1; + rewrite_by_lua_block { + ngx.var.age = ngx.var.age + 1 + } + + # access phase + deny 10.32.168.49; + access_by_lua_block { + ngx.var.age = ngx.var.age * 3 + } + + #content phase + echo "age = $age"; + } + ``` + + 测试结果 + ``` + # curl http://127.0.0.2:8008/test_rewrite_access_content + age = 6 + + ``` + + 执行顺序结论 + > [1] 由ngx_rewrite模块 实现的命令 集同步执行rewrite。模块 ngx_lua的命令 rewrite_by_lua在阶段结束时执行 + + > [2] 模块 ngx_access的命令被 拒绝同步执行。模块 ngx_lua的命令 access_by_lua在阶段结束时执行。 + + > [3] 最后,我们最喜欢的命令 echo,由ngx_echo模块实现 ,执行阶段。 + + > [4] 最后的执行顺序:rewrite access access content + + + 命令` set `集和命令 `rewrite_by_lua ` 都属于阶段 `rewrite` + + 命令` deny `集和命令 `access_by_lua ` 都属于阶段 `access` + + 命令` set `集和命令 `rewrite_by_lua ` 都属于阶段 `rewrite` ++ 下面的这样写是错误的 XXXXXXXXXX + + 错误写法????? + ``` + ? location /test { + ? content_by_lua 'ngx.say("hello")'; + ? content_by_lua 'ngx.say("world")'; + ? } + ``` + + 不是每个模块都支持在一个内部执行多次命令location。命令 content_by_lua为一个实例,只能使用一次 + + 正确写法 + ``` + location /test { + content_by_lua 'ngx.say("hello") ngx.say("world")'; + } + ``` + + 而不是使用content_by_lua两次 命令location,方法是在Lua代码中调用函数 ngx.say两次,该代码由命令content_by_lua执行 ++ 模块 ngx_proxy的命令 proxy_pass不能与一个中的命令echo共存, 因为它们都在同步执行 + + 错误写法!!!!!!!!! + ``` + ? location /test { + ? echo "before..."; + ? proxy_pass http://127.0.0.1:8080/foo; + ? echo "after..."; + ? } + ? + ? location /foo { + ? echo "contents to be proxied"; + ? } + ``` + + 测试结果 + ``` + $ curl 'http://localhost:8080/test' + contents to be proxied + ``` + + 结论:该示例尝试在模块ngx_proxy返回其内容之前和之后 输出字符串"before..."和"after..."命令 echo。但是只有一个模块可以执行。测试表明模块 ngx_proxy获胜并且命令 echo来自模块 ngx_echo永远不会运行 content + + 为了实现这个例子想要的,我们将使用模块ngx_echo, echo_before_body和 echo_after_body提供的另外两个命令 + ``` + location /test { + echo_before_body "before..."; + proxy_pass http://127.0.0.1:8080/foo; + echo_after_body "after..."; + } + + location /foo { + echo "contents to be proxied"; + } + ``` ++ 反向代理:可以看出反向代理前和反向代理后的结果 + + 案例 + ``` + location /echo_test { + echo_before_body "before..."; + proxy_pass http://127.0.0.1:8008/tinywan; + echo_after_body "after..."; + } + location /tinywan { + echo "contents to be proxied"; + } + + ``` + + 测试结果 + ``` + root@tinywan:/opt/openresty/nginx/conf# curl http://127.0.0.2:8008/echo_test + before... + contents to be proxied + after... + ``` + + 命令echo_before_body和 echo_after_body可以与其他模块同步共存的原因 content是,它们不是Nginx的“内容处理程序”,而是“输出过滤器” +## Nginx指令执行命令(06) ++ 当一个命令在 ` content ` 一个特定的阶段执行时 ` location `,通常意味着它的Nginx模块注册了一个 ` 内容处理程序 (content handler)` ++ 没有模块将其命令注册为“内容处理程序”时候,将获得产生内容和产出回应是静态资源模块,它将请求URI映射到文件系统。只有当没有“内容处理程序”时,静态资源模块才会发挥作用,否则它将“责任”移交给“内容处理程序” ++ Nginx有三个静态资源模块用于该content阶段( 按照执行顺序排列 ) + + (1) `ngx_index` 模块 + + (2) `ngx_autoindex` 模块 + + (3) ` ngx_static ` 模块 ++ ngx_index和 ngx_autoindex模块 仅适用于那些以URI为结尾的请求URI /。对于不以其他方式结束的其他请求URI /,两个模块都将忽略它们,并让以下content阶段模块处理。ngx_static然而,模块具有完全相反的策略。它忽略结束的请求URI /并处理其余的 ++ 模块 ngx_index主要查找特定的主页文件,如文件系统index.html或index.htm文件系统 + ``` + location / { + root /var/www/; + index index.htm index.html; + } + ``` + + 当地址 `/ ` 请求,Nginx的查找文件 ` index.htm `,并 `index.html` 在文件系统中的路径(按照这个顺序)。路径由命令 根目录指定。如果文件 `index.htm` 存在,Nginx内部跳转到位置 `index.htm`; 如果不存在并且文件 ` index.html` 存在,Nginx将内部跳转到位置`index.html`。如果文件 `index.html` 不存在,并且处理转移到同步执行命令的其他模块 `content` \ No newline at end of file diff --git a/Nginx-Rtmp/HLS-live-vod-locatiuon-config.md b/Nginx-Rtmp/HLS-live-vod-locatiuon-config.md new file mode 100644 index 0000000..c04992c --- /dev/null +++ b/Nginx-Rtmp/HLS-live-vod-locatiuon-config.md @@ -0,0 +1,61 @@ +### HLS视频直播和点播的Nginx的Location的配置信息 + ++ 配置信息 + ``` + http { + include mime.types; + default_type application/octet-stream; + server { + listen 80; + server_name 127.0.0.1; + root /home/tinywan; + error_log logs/rewrite_error.log notice; + + # HLS Live + location ^~ /live/ { + add_header Cache-Control no-cache; + add_header 'Access-Control-Allow-Origin' '*' always; + add_header 'Access-Control-Allow-Credentials' 'true'; + add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range'; + add_header 'Access-Control-Allow-Headers' 'Range'; + + types{ + application/dash+xml mpd; + application/vnd.apple.mpegurl m3u8; + video/mp2t ts; + } + root /home/tinywan/HLS; + } + + # History Video m3u8&mp4 + location ^~ /video_recordings/ { + add_header Cache-Control no-cache; + add_header 'Access-Control-Allow-Origin' '*' always; + add_header 'Access-Control-Allow-Credentials' 'true'; + add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range'; + add_header 'Access-Control-Allow-Headers' 'Range'; + + types{ + application/dash+xml mpd; + application/vnd.apple.mpegurl m3u8; + video/mp2t ts; + } + alias /home/tinywan/video_recordings/; + } + + } + } + ``` ++ 直播 + + OBS推流地址:`rtmp://192.168.18.143/live/tinywan123` + + RTMP直播地址:`rtmp://192.168.18.143/live/tinywan123` + + HLS直播:`http://192.168.18.143/live/tinywan123/index.m3u8` + + OBS推流地址:`rtmp://192.168.18.143/live/tinywan123` ++ 点播 + + MP4点播: `rtmp://192.168.18.143/vod/tinywan123-149190255220170411172232.mp4` ++ 回顾 + + HLS回顾地址: `http://192.168.18.143/video_recordings/tinywan123-149179459020170410112310/index.m3u8` + + MP4回顾地址: `http://192.168.18.143/video_recordings/tinywan123-149179459020170410112310.mp4` ++ 流媒体信息 + + 在线人数:`http://192.168.18.143/nclients?app=live&name=stream123456` + + RTMP流基本信息:`http://192.168.18.143/stat` \ No newline at end of file diff --git a/Nginx-Rtmp/HLS-live-vod.md b/Nginx-Rtmp/HLS-live-vod.md new file mode 100644 index 0000000..d0b2270 --- /dev/null +++ b/Nginx-Rtmp/HLS-live-vod.md @@ -0,0 +1,220 @@ +### Nginx配置Rtmp支持Hls的直播和点播功能 +>[参考网址:https://www.vultr.com/docs/setup-nginx-on-ubuntu-to-stream-live-hls-video](https://www.vultr.com/docs/setup-nginx-on-ubuntu-to-stream-live-hls-video) + +>配置信息 +``` +worker_processes 1; +error_log logs/error.log debug; +events { + worker_connections 1024; +} +rtmp { + server { + listen 1935; + allow play all; + notify_method get; + + #creates our "live" full-resolution HLS videostream from our incoming encoder stream and tells where to put the HLS video manifest and video fragments + application live { + allow play all; + live on; + on_publish http://example.com/openapi/on_publish_done; # 设置开始推流的回调 + record all; + record_path /home/tinywan/video_recordings; + record_unique on; + on_record_done http://example.com/recorded; # 设置录像结束的回调 + hls on; + hls_nested on; + hls_path /HLS/live; + hls_fragment 10s; + on_publish_done http://example.com/on_publish_done; # 设置开始推流的回调 + + #creates the downsampled or "trans-rated" mobile video stream as a 400kbps, 480x360 sized video + exec ffmpeg -i rtmp://192.168.254.178:1935/$app/$name -acodec copy -c:v libx264 -preset veryfast -profile:v baseline -vsync cfr -s 480x360 -b:v 400k maxrate 400k -bufsize 400k -threads 0 -r 30 -f flv rtmp://192.168.254.178:1935/mobile/$; + } + + #creates our "mobile" lower-resolution HLS videostream from the ffmpeg-created stream and tells where to put the HLS video manifest and video fragments + application mobile { + allow play all; + live on; + hls on; + hls_nested on; + hls_path /home/tinywan/HLS/mobile; + hls_fragment 10s; + } + + #allows you to play your recordings of your live streams using a URL like "rtmp://my-ip:1935/vod/filename.flv" + application vod { + play /home/tinywan/video_recordings; + } + } +} + + +http { + include mime.types; + default_type application/octet-stream; + + server { + listen 80; + server_name 192.168.254.178; + + #creates the http-location for our full-resolution (desktop) HLS stream - "http://my-ip/live/my-stream-key/index.m3u8" + location /live { + types { + application/vnd.apple.mpegurl m3u8; + } + alias /home/tinywan/HLS/live; + add_header Cache-Control no-cache; + } + + #creates the http-location for our mobile-device HLS stream - "http://my-ip/mobile/my-stream-key/index.m3u8" + location /mobile { + types { + application/vnd.apple.mpegurl m3u8; + } + alias /home/tinywan/HLS/mobile; + add_header Cache-Control no-cache; + } + + #allows us to see how stats on viewers on our Nginx site using a URL like: "http://my-ip/stats" + location /stats { + stub_status; + } + + #allows us to host some webpages which can show our videos: "http://my-ip/my-page.html" + location / { + root html; + index index.html index.htm; + } + } +} +``` +## 播放测试结果 ++ M3U8播放 + ``` + http://192.168.18.151/live/123456/index.m3u8 + ``` ++ RTMP播放 + ``` + rtmp://192.168.18.151/live/123456 + ``` ++ 点播播放 + ``` + rtmp://192.168.18.151/vod/123456-1490768694.flv + ``` + # m3u8: +## 统计 ++ 使用Nginx统计信息(使用Nginx stub_status)。要查看访客/观众统计资料 + + 浏览器测试 + ``` + http://192.168.18.151/stats + ``` + + 结果信息 + ``` + Active connections: 1 + server accepts handled requests + 274 274 108 + Reading: 0 Writing: 1 Waiting: 0 + ``` +## 回调信息 ++ on_publish + + 上下文:rtmp, server, application + + 返回参数参考 + ``` + $action = $_GET['call']; -- publish + $appName = $_GET['app']; -- live + $swfurl = $_GET['swfurl']; -- http://123213.com/lib/jwplayer/jwplayer.flash.swf + $tcurl = $_GET['tcurl']; -- rtmp://11.26.11.11/live/ + $ip = $_GET['addr']; -- 客户端IP地址 + $clientid = $_GET['clientid']; -- 871696 + $streamName = $_GET['name']; -- stream123 + ``` ++ on_publish_done + + 上下文:rtmp, server, application + + 返回参数参考 + ``` + $action = $_GET['call']; -- on_publish_done + $appName = $_GET['app']; + $swfurl = $_GET['swfurl']; + $tcurl = $_GET['tcurl']; + $ip = $_GET['addr']; + $clientid = $_GET['clientid']; + $streamName = $_GET['name']; + ``` ++ on_record_done (on_record_done http://my-ip/api/recordDone;) + + 上下文:rtmp, server, application, recorder + + 返回参数参考 + ``` + 'app' => string 'live' (length=4) + 'flashver' => string 'FMLE/3.0 (compatible; Lavf56.3.' (length=31) + 'swfurl' => string '' (length=0) + 'tcurl' => string 'rtmp://10.117.19.148:1935/live' (length=30) + 'pageurl' => string '' (length=0) + 'addr' => string '192.168.18.151/' (length=13) + 'clientid' => string '1' (length=1) + 'call' => string 'record_done' (length=11) -- 录像事件状态 + 'recorder' => string 'rec1' (length=4) -- 录像模块名称 + 'name' => string '123456' (length=13) -- 推流名称 + 'path' => string '/home/tinywan/video_recordings/123456-1482801335.flv' -- 录像路径 + ``` + + RTMP配置信息,在这里,定义了一个录像模块,这样的话不会自动录像 + ``` + rtmp { + server { + listen 1935; + ping 30s; + notify_method get; + application live { + live on; + on_record_done http://my-ip/api/recordDone; -- 以上配置在这里个回调里面的地址 + recorder rec1 { + record all manual; + record_unique on; + record_notify on; + record_path /data/recorded_flvs; + exec_record_done /home/www/bin/rtmpRecordedNotify.sh $name $path $filename $basename $dirname; + } + } + } + ``` + ++ on_connect + + 上下文:rtmp, server + + 返回参数参考 + ``` + 'app' => string 'live' (length=4) + 'flashver' => string '' (length=0) + 'swfurl' => string '' (length=0) + 'tcurl' => string 'rtmp://192.168.18.151/live' (length=25) + 'pageurl' => string '' (length=0) + 'addr' => string '10.117.64.209' (length=13) + 'epoch' => string '582057759' (length=9) + 'call' => string 'connect' (length=7) + ``` + ++ 实际案例 (Web页面和Mobile手机流转换) + ``` + rtmp { + server { + listen 1935; + application live { + live on; + exec ffmpeg -i rtmp://localhost/live/$name -acodec copy -c:v libx264 -preset veryfast -profile:v baseline -vsync cfr -s 480x360 -b:v 400k -bufsize 400k -threads 0 -r 30 -f flv rtmp://localhost/mobile/$name; + } + + application mobile { + allow play all; + live on; + hls on; + hls_nested on; + hls_path /home/tinywan/HLS/mobile; + hls_fragment 10s; + } + } + } + ``` + +>实际案例 2 + +>实际案例 3 \ No newline at end of file diff --git a/Nginx-Rtmp/Openresty_rtmp_obs_push.md b/Nginx-Rtmp/Openresty_rtmp_obs_push.md new file mode 100644 index 0000000..70ba3d3 --- /dev/null +++ b/Nginx-Rtmp/Openresty_rtmp_obs_push.md @@ -0,0 +1,85 @@ +## Openresty_rtmp_obs_push.md + +#### nginx.conf +```lua + # rtmp auth_key + location /rtmp_redirect_lua { + lua_code_cache off; + access_by_lua_file /opt/openresty/nginx/conf/lua/rtmp_auth_key.lua; + } +``` + +#### rtmp_auth_key.lua 文件 +```lua +local cjson = require "cjson" +local redis = require("resty.redis_iresty") +local var = ngx.var +local client_ip = var.remote_addr +local client_port = var.remote_port + +ngx.req.read_body() +local post_var = ngx.req.get_post_args() +local stream_name = post_var.name +local app = post_var.app +local action = post_var.call + +local red = redis:new() +-- 权限验证 +local res,err = red:auth('=123123') +if not res then + ngx.say("failed to authenticate: ", err) + return +end +red:select(1) + +local res, err = red:sismember('StreamNameValidityCheck',stream_name) +if not res then + ngx.log(ngx.WARN, "warn: failed to sismember: ", res) + return +end +-- auth flag +if tonumber(res) ~= 1 then + local ok, err = red:multi() + if not ok then + ngx.log(ngx.ERR, "error: failed to run multi: ", err) + return + end + + local ok, err = red:zrem('NonInterfaceStreamName',stream_name) + if not ok then + ngx.log(ngx.ERR, "error: zrem failed : ", err) + return + end + + --local ok, err = red:zadd('NonInterfaceStreamName',ngx.time(),client_ip..':'..client_port..'=:'..stream_name) + local ok, err = red:zadd('NonInterfaceStreamName',ngx.time(),stream_name) + if not ok then + ngx.log(ngx.ERR, "error: failed to run multi: ", err) + return + end + + local ans, err = red:exec() + if not ans then + ngx.log(ngx.WARN, "warn: failed to sismember: ", res) + return + end + --ngx.log(ngx.ERR, "warn: RTMP to sismember: ",ngx.HTTP_BAD_REQUEST) + ngx.exit(ngx.HTTP_BAD_REQUEST) +end +ngx.exit(ngx.HTTP_OK) +``` +#### post 请求方式 +```lua +curl -d "name=CY0000768621&app=live123&call=publish_done_12" http://localhost:8081/rtmp_redirect_lua +``` + +#### 状态码查看 +```javascript +www@ubuntu4:/opt/openresty/nginx/conf/lua$ curl -I http://localhost:8081/rtmp_redirect_lua +HTTP/1.1 400 Bad Request +Server: openresty/1.11.2.1 +Date: Tue, 02 May 2017 09:59:42 GMT +Content-Type: text/html +Content-Length: 179 +Connection: close +``` diff --git a/Nginx-Rtmp/Openresty_rtmp_obs_push_auth.md b/Nginx-Rtmp/Openresty_rtmp_obs_push_auth.md new file mode 100644 index 0000000..aa715d1 --- /dev/null +++ b/Nginx-Rtmp/Openresty_rtmp_obs_push_auth.md @@ -0,0 +1,226 @@ +## Openresty_rtmp_obs_push.md + +#### nginx.conf +```lua +location /rtmp_redirect_lua { + access_by_lua_file /opt/openresty/nginx/conf/lua/rtmp_auth_key.lua; +} + +application live { + live on; + on_publish http://localhost:8081/rtmp_redirect_lua; +} +``` + +#### rtmp_auth_key.lua 文件 +```lua +--[[----------------------------------------------------------------------- +* | Copyright (C) Shaobo Wan (Tinywan) +* | Github: https://github.com/Tinywan +* | Blog: http://www.cnblogs.com/Tinywan +* |------------------------------------------------------------------------ +* | Author: Tinywan +* | Date: 2017/5/4 +* | Time: 16:25 +* | Mail: Overcome.wan@Gmail.com +* |------------------------------------------------------------------------ +--]] +local cjson = require "cjson" +local redis = require("resty.redis_iresty") +local var = ngx.var + +-- post var +ngx.req.read_body() +local post_var = ngx.req.get_post_args() +local stream_name = post_var.name +local app = post_var.app +local action = post_var.call + +-- redis auth +local red = redis:new() +local res,err = red:auth('+=') +if not res then + ngx.say("failed to authenticate: ", err) + ngx.exit(ngx.HTTP_BAD_REQUEST) +end +-- select redis db +red:select(1) + +--get push auth switch +local res, err = red:get('RTMPPushAuthSwitch') +if not res then + ngx.log(ngx.WARN, "warn: RTMPPushAuthSwitch is not set: ", err) + ngx.exit(ngx.HTTP_BAD_REQUEST) +end + +--ngx.log(ngx.ERR, "error: TEST============: ", res) + +if tonumber(res) == 0 then -- 0 all allowed + ngx.exit(ngx.HTTP_OK) +elseif tonumber(res) == 1 then -- 1 web allowed + -- whitelist validation + local res, err = red:sismember('StreamNameValidityCheck',stream_name) + if not res then + ngx.log(ngx.WARN, "warn: failed to sismember: ", res) + ngx.exit(ngx.HTTP_BAD_REQUEST) + end + -- if set + if tonumber(res) ~= 1 then + local ok, err = red:multi() + if not ok then + ngx.log(ngx.ERR, "error: failed to run multi: ", err) + return + end + -- zrem member + local ok, err = red:zrem('NonInterfaceStreamName',stream_name) + if not ok then + ngx.log(ngx.ERR, "error: zrem failed : ", err) + return + end + + local ok, err = red:zadd('NonInterfaceStreamName',ngx.time(),stream_name) + if not ok then + ngx.log(ngx.ERR, "error: failed to run multi: ", err) + return + end + -- exec + local ans, err = red:exec() + if not ans then + ngx.log(ngx.WARN, "warn: failed to sismember: ", res) + return + end + ngx.exit(ngx.HTTP_BAD_REQUEST) + end + --if it is set + ngx.exit(ngx.HTTP_OK) +elseif tonumber(res) == 2 then -- 2 all push is required to be auth_key + --get var + --local uri = ngx.var.uri (uri = rtmp_redirect_lua) 这里有时间在继续研究 + local var = ngx.req.get_post_args() + local app = var.app + local name = var.name + local uri = '/'..app..'/'..name + local auth_key = var.auth_key + + --ngx.log(ngx.ERR, "error: auth_key::uri====::",uri) + --expire_time vlidation + local expire_time = string.sub(auth_key,0,10) + --get current Unixtimestamp + local res_time = ngx.time() + if tonumber(expire_time) < tonumber(res_time) then + ngx.log(ngx.ERR, "error : the push flow address has expired ") + ngx.exit(ngx.HTTP_BAD_REQUEST) + end + + --get private_key + local private_key, err = red:get('private_key') + if not private_key then + ngx.log(ngx.ERR, "error: redis failed to get private_key ", err) + ngx.exit(ngx.HTTP_BAD_REQUEST) + end + + --create res_hash_value + local rand = 0 + local uid = 0 + local seq_hash_value = string.sub(auth_key,-32) + local sstring = uri.."-"..expire_time.."-"..rand.."-"..uid.."-"..private_key + local res_hash_value = ngx.md5(sstring) + + --seq_hash_value vlidation + if tostring(seq_hash_value) ~= tostring(res_hash_value) then + ngx.log(ngx.ERR, "error: client hash_value is error value== ",seq_hash_value," server_hash_value== ",res_hash_value) + ngx.exit(ngx.HTTP_BAD_REQUEST) + end + + -- set name expire_time 7d = 604800 + local expire_timestamp = tonumber(expire_time)+tonumber(604800) + local ok, err = red:hset("STREAM_GLOBAL:"..name,'expireTime',expire_timestamp) + if not ok then + ngx.log(ngx.ERR, "error: failed to set expire_time ", err) + end + ngx.exit(ngx.HTTP_OK) +else -- unknown error + ngx.log(ngx.ERR, "unknown Error 500 ") + ngx.exit(ngx.HTTP_BAD_REQUEST) +end +ngx.exit(ngx.HTTP_OK) + +``` +#### Lua Api 接口修改参数 +```lua +--[[----------------------------------------------------------------------- +* | Copyright (C) Shaobo Wan (Tinywan) +* | Github: https://github.com/Tinywan +* | Blog: http://www.cnblogs.com/Tinywan +* |------------------------------------------------------------------------ +* | Author: Tinywan +* | Date: 2017/4/20 +* | Time: 16:25 +* | Mail: Overcome.wan@Gmail.com +* |------------------------------------------------------------------------ +--]] +local cjson = require "cjson" +local redis = require("resty.redis_iresty") + +local uri = ngx.var.uri +ngx.req.read_body() +local var = ngx.req.get_uri_args() + +local switch_type = var.switch_type +local switch_status = var.switch_status +local auth_key = var.auth_key + +-- expire_time vlidation +local expire_time = string.sub(auth_key,0,10) +local res_time = ngx.time() +if tonumber(expire_time) < tonumber(res_time) then + ngx.say(cjson.encode("The request url has expired")) + ngx.exit(ngx.HTTP_OK) +end + +local red = redis:new() +-- auth vlidation +local res,err = red:auth('+=') +if not res then + ngx.say("failed to authenticate: ", err) + ngx.exit(ngx.HTTP_OK) +end +red:select(1) + +-- get private_key +local private_key, err = red:get('private_key') +if not private_key then + ngx.say("error: failed to get private_key ") + ngx.exit(ngx.HTTP_OK) +end + +-- create res_hash_value +--local expire_time = string.sub(auth_key,0,10) +local rand = "0" +local uid = "0" +local seq_hash_value = string.sub(auth_key,-32) +local sstring = uri.."-"..expire_time.."-"..rand.."-"..uid.."-"..private_key +local res_hash_value = ngx.md5(sstring) + +--seq_hash_value vlidation +if tostring(seq_hash_value) ~= tostring(res_hash_value) then + ngx.say("req_hash_value is error") + ngx.exit(ngx.HTTP_OK) +end + +-- set address switch status +local ok, err = red:getset(switch_type,switch_status) +if not ok then + ngx.log(ngx.ERR, "error: failed to set StreamAddressSwitch ", err) + ngx.say("failed to set info:",cjson.encode(err)) + ngx.exit(ngx.HTTP_OK) +end +ngx.say("set result: ", cjson.encode(ok)) +ngx.exit(ngx.HTTP_OK) + +``` +#### get 浏览器请求方式 +```lua +http://127.0.0.1/stream_address_switch?switch_type=StreamAddressSwitch&switch_status=2&auth_key=1493863980-0-0-64f1a882229888aeae8db32b48271c84 +set result: "1" +``` diff --git a/Nginx-Rtmp/Shell_Log.sh b/Nginx-Rtmp/Shell_Log.sh new file mode 100644 index 0000000..2742603 --- /dev/null +++ b/Nginx-Rtmp/Shell_Log.sh @@ -0,0 +1,159 @@ +#!/bin/bash +####################################################### +# $Name: Shell_Log.sh +# $Version: v1.0 +# $Function: Log Script +# $Author: ShaoBo Wan (Tinywan) +# $organization: https://github.com/Tinywan +# $Create Date: 2017-06-29 +# $Description: Mysql 自动备份脚本安全加锁机制 +####################################################### +:<>log_file.log + [1] error===>红色(31m):错误日志信息 + [2] info===>绿色(32m):命令成功执行、URL回调成功、打印正确数据信息 + [3] warn===>黄色(33m):参数不存在、文件不存在、命令拼写错误 + [3] debug===>Blue色(34m): debug + [3]date:2016-11-04 10:23:10 [date '+%Y-%m-%d %H:%M:%S'] +tinywan +PATH=/usr/local/bin:/usr/bin:/bin +YM=`date +%Y%m` +FLOG=/home/tinywan/bin/recorded_${YM}.log +#设置日志级别 +loglevel=0 #debug:0; info:1; warn:2; error:3 +TIME=`date '+%Y-%m-%d %H:%M:%S'` +function LOG(){ + local log_type=$1 + local LOG_CONTENT=$2 + # 这里的写入日志时间修改掉,经过一段时间的测试${TIME} 每次都是一个固定的时间,所以在这里修改为每次写入是自动获取当前时间写入日志 + logformat="`date '+%Y-%m-%d %H:%M:%S'` \t[${log_type}]\tFunction: ${FUNCNAME[@]}\t[line:`caller 0 | awk '{print$1}'`]\t [log_info: ${LOG_CONTENT}]" + { + case $log_type in + debug) + [[ $loglevel -le 0 ]] && echo -e "\033[34m${logformat}\033[0m" ;; + info) + [[ $loglevel -le 1 ]] && echo -e "\033[32m${logformat}\033[0m" ;; + warn) + [[ $loglevel -le 2 ]] && echo -e "\033[33m${logformat}\033[0m" ;; + error) + [[ $loglevel -le 3 ]] && echo -e "\033[31m${logformat}\033[0m" ;; + esac + } | tee -a $FLOG +} + +echo -e "\r\n \033[34m------------------------------------------------------Shell Script Start -------------------------------------------- \033[0m " >> $FLOG +# 最小视频长度 +MIN_DURATION=20 + +# Redis Var +REDIS_HOST='127.0.0.1' +REDIS_PORT='6379' +REDIS_AUTH='tinywanredis' +REDIS_DB=12 + +# 推流名称 mystream +STREAM_NAME=$1 + +# 录制文件全路径 /tmp/rec/mystream-1389499351.flv +FULL_NAME=$2 + +# 录制文件名 mystream-1389499351.flv +FILE_NAME=$3 + +# 录制文件名,不带后缀(mystream-1389499351) +BASE_NAME=$4 + +# 录制文件路径 /tmp/rec +DIR_NAME=$5 + +# -z 检测字符串长度是否为0 # 检测 STREAM_NAME 变量值是否为空 +if [ -z "${STREAM_NAME}" ];then + LOG error "STREAM_NAME is null" + exit 1 +fi + +# $@ 是传给脚本的所有参数的列表 +#echo -e "\033[32m [SUCCESS][$TIME]: $@ \033[0m" >>$FLOG +LOG debug $@ + +# 检测 FULL_NAME 变量值是否为空 +if [ -z "${FULL_NAME}" ]; then + LOG error "FULL_NAME is null" + exit 1 +fi + +# 检测文件是否存在并且大小不为0 +if [ ! -s "${FULL_NAME}" ]; then + LOG error "File not exists or zero size " + # 文件为空文件删除掉该文件 + rm -f ${FULL_NAME} + exit 1 +fi + +# 检测视频时长,小于 MIN_DURATION 的文件将被丢弃 +DURATION=`/usr/bin/ffmpeg -i ${FULL_NAME} 2>&1 | awk '/Duration/ {split($2,a,":");print a[1]*3600+a[2]*60+a[3]}'` +if [ $(echo "${DURATION} < $MIN_DURATION"|bc) = 1 ]; then + LOG error " Duration too short, FULL_NAME=${FULL_NAME}, DURATION==${DURATION}" + rm -f ${FULL_NAME} + exit 1 +fi + +#echo "[DEBUG1][$TIME] Video Record : FULL_NAME=$FULL_NAME, FULL_NAME=${FULL_NAME}, DURATION=${DURATION}" >> $FLOG + +# 自动截取封面图片 +#/usr/bin/ffmpeg -y -ss 00:00:10 -i ${FULL_NAME} -vframes 1 ${DIR_NAME}/${BASE_NAME}.jpg +FFMPEG_JPG=$(/usr/bin/ffmpeg -y -i ${FULL_NAME} -vcodec copy -acodec copy ${DIR_NAME}/${BASE_NAME}.mp4 && echo "success" || echo "fail") +LOG info "Screenshot JPG: ${FFMPEG_JPG} " + +# 转码成MP4 +#/usr/bin/ffmpeg -y -i ${FULL_NAME} -vcodec copy -acodec copy ${DIR_NAME}/${BASE_NAME}.mp4 +FFMPEG_MP4=$(/usr/bin/ffmpeg -y -i ${FULL_NAME} -vcodec copy -acodec copy ${DIR_NAME}/${BASE_NAME}.mp4 && echo "success" || echo "fail") +LOG info "Transcoding MP4: ${FFMPEG_MP4} " + +# 获取文件大小 +FILE_SIZE=`stat -c "%s" ${DIR_NAME}/${BASE_NAME}.mp4` + +# 获取视频录制时间 +FILE_TIME=`stat -c "%Y" ${FULL_NAME}` + +LOG debug "Video: FILE_NAME=${FILE_NAME}, DURATION=${DURATION}, FILESIZE=${FILE_SIZE},FILETIME=${FILE_TIME}" + +#recorded done rallback +URL="http://localhost/recordDone?streamName=${STREAM_NAME}&baseName=${BASE_NAME}&duration=${DURATION}&fileSize=${FILE_SIZE}&fileTime=${FILE_TIME}" +RESULT=$(curl ${URL} 2>/dev/null) + +RES_STATUS=${RESULT:0:3} +RES_RESULT=${RESULT:4} +#RESULT 返回值必须为字符串 +if [ "${RES_STATUS}" == "200" ]; then + LOG info "[$(date '+%Y-%m-%d %H:%M:%S')] recorded rallBack OK :${RES_RESULT}" +elif [ "${RES_STATUS}" == "500" ] +then + LOG error "recorded rallBack Fail :${RES_RESULT}" +else + LOG error "recorded rallBack Unknown error" +fi + +# auto slice mp4 to m3u8 +mkdir -p ${DIR_NAME}/${BASE_NAME} +# 添加如果ffmpeg 命令执行错误,则提示切片错误,负责输出正确结果 +FFMPEG_RUN=$(/usr/bin/ffmpeg -i ${FULL_NAME} -flags +global_header -f segment -segment_time 3 -segment_format mpegts -segment_list ${DIR_NAME}/${BASE_NAME}/index.m3u8 -c:a copy -c:v copy -bsf:v h264_mp4toannexb -map 0 ${DIR_NAME}/${BASE_NAME}/%5d.ts && echo "slice success" || echo "slice fail") +# $? = 0 success, other fail +if [[ $? -eq 0 ]]; then + LOG info "ffmpeg run success :"$(echo $?) +else + LOG error "ffmpeg run error : "$(echo $?) +fi + +LOG info "FFMPEG_SLICE: ${FFMPEG_RUN} [$(date '+%Y-%m-%d %H:%M:%S')] " + +# 查找超出7天前的flv的文件进行删除 +cd ${DIR_NAME} +find ./ -mindepth 1 -maxdepth 3 -type f -name "*.flv" -mmin +10080 | xargs rm -rf + +exit 1 + + + diff --git a/Nginx-Rtmp/Shell_Nginx_Log_cut.sh b/Nginx-Rtmp/Shell_Nginx_Log_cut.sh new file mode 100644 index 0000000..1363fea --- /dev/null +++ b/Nginx-Rtmp/Shell_Nginx_Log_cut.sh @@ -0,0 +1,56 @@ +#!/bin/bash +#function: Nginx log timing backup and delete + +# get nginx PID +# pid = $(cat /var/run/nginx.pid) +pid=$(ps -aef | grep nginx | grep -v grep | grep master |awk '{print $2}') + +## if nginx running else exit 1 +#if [[ $? == 0 && -n $pid ]] +#then +# exit 1; +#fi + +#set the path save nginx log files +base_path="/home/tinywan/logs" + +# get data eg:201611 +log_dir_name=$(date -d yesterday +"%Y%m") + +# get days eg:03 +DAY=$(date -d yesterday +"%d") + +# create log directory +mkdir -p $base_path/$log_dir_name + +# set the path to nginx log +log_files_path="/usr/local/nginx/logs/" + +#set nginx log files you want to cut eg: array +log_files_names=(access error) + +#set the path to nginx. +nginx_sbin="/usr/local/nginx/sbin/nginx" + +#Set how long you want to save eg: 7 days +save_mins=1 + +#log file num eg: 2 +log_files_num=${#log_files_names[@]} + +#loop cut nginx log files +for log_name in ${log_files_names[*]} +do + sudo mv ${log_files_path}${log_name}.log ${base_path}/${log_dir_name}/${log_name}_${DAY}.log +done + +#向 Nginx 主进程发送 USR1 信号,USR1 信号是重新打开日志文件 +sudo kill -USR1 $pid + +#delete 7 days ago nginx log files +find $base_path -mindepth 1 -maxdepth 3 -type f -name "*.log" -mmin +$save_mins | xargs rm -rf + +#################### crontab edit 每天凌晨 1:55 执行该脚本 ##################### +# 55 1 * * * bash /home/tinywan/shell/nginx_log_cut.sh >/dev/null 2>&1 +############################################################################## + diff --git a/Nginx-Rtmp/Shell_script.md b/Nginx-Rtmp/Shell_script.md new file mode 100644 index 0000000..73896ed --- /dev/null +++ b/Nginx-Rtmp/Shell_script.md @@ -0,0 +1,70 @@ +## SHELL脚本 ++ [如何不耍流氓的做运维之-SHELL脚本](http://mp.weixin.qq.com/s/aI3ha5BvzIvqpn2ddAK5zQ) ++ [已经测试成功自动备份和自动压缩备份数据库文件](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx-Rtmp/slimp_backup_db.sh) +## SHELL脚本小技巧 +```bash +#!/bin/bash +####################################################### +# $Name: shell_template.sh +# $Version: v1.0 +# $Function: Backup MySQL Databases Template Script +# $Author: Jason Zhao +# $organization: https://www.unixhot.com/ +# $Create Date: 2016-08-27 +# $Description: You know what i mean,hehe +####################################################### + +# Shell Env +SHELL_NAME="shell_template.sh" +SHELL_DIR="/opt/shell" +SHELL_LOG="${SHELL_DIR}/${SHELL_NAME}.log" +LOCK_FILE="/tmp/${SHELL_NAME}.lock" + +#Write Log +shell_log(){ + LOG_INFO=$1 + echo "$(date "+%Y-%m-%d") $(date "+%H-%M-%S") : ${SHELL_NAME} : ${LOG_INFO}" >> ${SHELL_LOG} +} + +# Shell Usage shell_usage函数,用来告诉用户,这个脚本的使用方法 +shell_usage(){ + echo $"Usage: $0 {backup}" +} + +# 函数shell_lock和shell_unlock非常简单,就是创建一个锁文件 +shell_lock(){ + touch ${LOCK_FILE} +} + +shell_unlock(){ + rm -f ${LOCK_FILE} +} + +# Backup MySQL All Database with mysqldump or innobackupex +mysql_backup(){ + if [ -f "$LOCK_FILE" ];then + shell_log "${SHELL_NAME} is running" + echo "${SHELL_NAME}" is running && exit + fi + shell_log "mysql backup start" + shell_lock + sleep 10 + shell_log "mysql backup stop" + shell_unlock +} + +# Main Function +main(){ + case $1 in + backup) + mysql_backup + ;; + *) + shell_usage; + esac +} + +#Exec +main $1 +``` + diff --git a/Nginx-Rtmp/backup_mysql.sh b/Nginx-Rtmp/backup_mysql.sh new file mode 100644 index 0000000..dc25450 --- /dev/null +++ b/Nginx-Rtmp/backup_mysql.sh @@ -0,0 +1,99 @@ +#!/bin/bash +####################################################### +# $Name: mysql_auto_backup.sh +# $Version: v1.0 +# $Function: Backup MySQL Databases Script +# $Author: ShaoBo Wan (Tinywan) +# $organization: https://github.com/Tinywan +# $Create Date: 2017-06-27 +# $Description: 定期备份MySQL数据库 +# $crontab: 55 23 * * * bash $PATH/mysql_auto_backup.sh backup >/dev/null 2>&1 +####################################################### + +# Shell Env +SHELL_NAME="mysql_auto_backup.sh" +SHELL_TIME=$(date '+%Y-%m-%d-%H:%M:%S') +SHELL_DAY=$(date '+%Y-%m-%d') +SHELL_DIR="/home/www/data-backup" +SHELL_LOG="${SHELL_DIR}/logs/${SHELL_NAME}-${SHELL_DAY}.log" +LOCK_FILE="/tmp/${SHELL_NAME}.lock" +MYSQL_DUMP="/usr/bin/mysqldump" +MYSQL_BACKUP_DB_NAME="resty" +BACKUP_NAME=${MYSQL_BACKUP_DB_NAME}"-${SHELL_TIME}.sql" + +# Write Log +loglevel=0 #debug:0; info:1; warn:2; error:3 +TIME=`date '+%Y-%m-%d %H:%M:%S'` +shell_log(){ + local log_type=$1 + local LOG_CONTENT=$2 + # 这里的写入日志时间修改掉,经过一段时间的测试${TIME} 每次都是一个固定的时间,所以在这里修改为每次写入是自动获取当前时间写入日志 + logformat="`date '+%Y-%m-%d %H:%M:%S'` \t[${log_type}]\t [${SHELL_NAME}] Function: ${FUNCNAME[@]}\t[line:`caller 0 | awk '{print$1}'`]\t [log_info: ${LOG_CONTENT}]" + { + case $log_type in + debug) + [[ $loglevel -le 0 ]] && echo -e "\033[34m${logformat}\033[0m" ;; + info) + [[ $loglevel -le 1 ]] && echo -e "\033[32m${logformat}\033[0m" ;; + warn) + [[ $loglevel -le 2 ]] && echo -e "\033[33m${logformat}\033[0m" ;; + error) + [[ $loglevel -le 3 ]] && echo -e "\033[31m${logformat}\033[0m" ;; + esac + } | tee -a $SHELL_LOG +} + +# Shell Usage shell_usage函数,用来告诉用户,这个脚本的使用方法 +shell_usage(){ + echo $"Usage: $0 {backup}" +} + +shell_lock(){ + touch ${LOCK_FILE} +} + +shell_unlock(){ + rm -f ${LOCK_FILE} +} + +mysql_zip(){ + cd $SHELL_DIR + /bin/bzip2 $BACKUP_NAME + find ./ -mindepth 1 -maxdepth 3 -type f -name '*.bz2' -mmin +43200 | xargs rm -rf + find ./ -mindepth 1 -maxdepth 3 -type f -name *.sql -mmin +10080 | xargs rm -rf + find ./ -mindepth 1 -maxdepth 3 -type f -name *.log -mmin +10080 | xargs rm -rf +} + +# Backup MySQL weblive Database with mysqldump or innobackupex +mysql_backup(){ + if [ -f "$LOCK_FILE" ];then + shell_log warn "${SHELL_NAME} is running" + exit 1 + fi + shell_log info "mysql backup start" + shell_lock + #sleep 10 + #$qMYSQL_DUMP $MYSQL_BACKUP_DB_NAME > $SHELL_DIR/$BACKUP_NAME + BACKUP_RES=$($MYSQL_DUMP $MYSQL_BACKUP_DB_NAME > $SHELL_DIR/$BACKUP_NAME && echo "success" || echo "fail") + if [ "${BACKUP_RES}" == "fail" ];then + shell_log error "MYSQL_BACKUP_DB error : ${BACKUP_RES}" + shell_unlock + exit 1 + fi + mysql_zip + shell_log info "mysql backup stop" + shell_unlock +} + +# Main Function +main(){ + case $1 in + backup) mysql_backup + ;; + *) shell_usage + ;; + esac +} + +#Exec +main $1 diff --git a/Nginx-Rtmp/nginx_status.md b/Nginx-Rtmp/nginx_status.md new file mode 100644 index 0000000..d1f0fee --- /dev/null +++ b/Nginx-Rtmp/nginx_status.md @@ -0,0 +1,27 @@ ++ Nginx查看并发连接数 + + 编译添加选项:`--with-http_stub_status_module` + + Openresty编译: + ```javascript + ./configure --prefix=/opt/openresty + --with-http_iconv_module + --pid-path=/var/run/nginx.pid + --with-http_realip_module + --with-http_ssl_module + --with-http_stub_status_module + ``` + + 配置文件 + ```javascript + location /nginx_status { + stub_status on; + access_log off; + allow 127.0.0.1; + deny all; + } + ``` + + 观看地址:`http://127.0.0.1/nginx_status` + + nginx status详解 + - `active connections` – 活跃的连接数量 + - `server accepts handled requests` — 总共处理了11989个连接 , 成功创建11989次握手, 总共处理了11991个请求 + - `reading ` — 读取客户端的连接数. + - `writing ` — 响应数据到客户端的数量. + - `waiting ` — 开启 keep-alive 的情况下,这个值等于 active – (reading+writing), 意思就是 Nginx 已经处理完正在等候下一次请求指令的驻留连接. \ No newline at end of file diff --git a/Nginx-Rtmp/shell_usage.sh b/Nginx-Rtmp/shell_usage.sh new file mode 100644 index 0000000..76a218f --- /dev/null +++ b/Nginx-Rtmp/shell_usage.sh @@ -0,0 +1,125 @@ +#!/bin/bash +####################################################### +# $Name: shell_template.sh +# $Version: v1.0 +# $Function: Backup MySQL Databases Script +# $Author: ShaoBo Wan +# $organization: https://github.com/Tinywan +# $Create Date: 2017-06-27 +# $Description: 定期备份MySQL数据库 +####################################################### + +# Shell Env +SHELL_NAME="test.sh" +SHELL_TIME=$(date "+%Y-%m-%d") +SHELL_DIR="/home/www/database_back" +SHELL_LOG="${SHELL_DIR}/${SHELL_NAME}-${SHELL_TIME}.log" +LOCK_FILE="/tmp/${SHELL_NAME}.lock" +MYSQL_DUMP="/usr/bin/mysqldump" +MYSQL_BACKUP_DB_NAME="weblive" +BACKUP_NAME=${MYSQL_BACKUP_DB_NAME}"-${SHELL_TIME}.sql" + +#设置日志级别 +loglevel=0 #debug:0; info:1; warn:2; error:3 +TIME=`date '+%Y-%m-%d %H:%M:%S'` +shell_log(){ + local log_type=$1 + local LOG_CONTENT=$2 + # 这里的写入日志时间修改掉,经过一段时间的测试${TIME} 每次都是一个固定的时间,所以在这里修改为每次写入是自动获取当前时间写入日志 + logformat="`date '+%Y-%m-%d %H:%M:%S'` \t[${log_type}]\t [${SHELL_NAME}] Function: ${FUNCNAME[@]}\t[line:`caller 0 | awk '{print$1}'`]\t [log_info: ${LOG_CONTENT}]" + { + case $log_type in + debug) + [[ $loglevel -le 0 ]] && echo -e "\033[34m${logformat}\033[0m" ;; + info) + [[ $loglevel -le 1 ]] && echo -e "\033[32m${logformat}\033[0m" ;; + warn) + [[ $loglevel -le 2 ]] && echo -e "\033[33m${logformat}\033[0m" ;; + error) + [[ $loglevel -le 3 ]] && echo -e "\033[31m${logformat}\033[0m" ;; + esac + } | tee -a $SHELL_LOG +} + +# Shell Usage shell_usage函数,用来告诉用户,这个脚本的使用方法 +shell_usage(){ + #echo $"Usage: $0 {backup}" + echo '你没有输入 1 到 4 之间的数字' +} + +# 函数shell_lock和shell_unlock非常简单,就是创建一个锁文件 +shell_lock(){ + touch ${LOCK_FILE} +} + +shell_unlock(){ + rm -f ${LOCK_FILE} +} + +# Backup MySQL weblive Database with mysqldump or innobackupex +mysql_backup(){ + if [ -f "$LOCK_FILE" ];then + shell_log "${SHELL_NAME} is running" + echo "${SHELL_NAME}" is running && exit + fi + shell_log "mysql backup start" + shell_lock + sleep 10 + $MYSQL_DUMP $MYSQL_BACKUP_DB_NAME > $SHELL_DIR/$BACKUP_NAME + cd $SHELL_DIR + /bin/bzip2 $BACKUP_NAME + shell_log "mysql backup stop" + shell_unlock +} + +# Main Function +main(){ + case $1 in + 1) echo '你选择了 1' + ;; + 2) echo '你选择了 2' + ;; + 3) echo '你选择了 3' + ;; + 4) echo '你选择了 4' + ;; + *) shell_usage + ;; + esac +} + +main2(){ + case $1 in + backup) mysql_backup + ;; + *) shell_usage + ;; + esac +} + + +#Exec +main $1 + +database=weblive +databak_dir=/home/www/database_back +logs_dir=/home/www/database_back/logs +dumpbin=/usr/bin/mysqldump + +DATE=$(date +%Y%m%d) +date_time=$(date +"%y-%m-%d %H:%M:%S") +backup_name=sansan_bak1_${DATE}.sql +logFile=$logs_dir/sansan_${DATE}.log + +echo " " > $logFile +echo "-------------$(date +"%y-%m-%d %H:%M:%S") backup start ----------------------" >> $logFile + +$MYSQL_DUMP $MYSQL_BACKUP_NAME >$databak_dir/$backup_name +cd $databak_dir +/bin/bzip2 $backup_name +# 1 mouth = 44640 min +find ./ -mindepth 1 -maxdepth 3 -type f -name *.bz2 -mmin +43200 | xargs rm -rf +find ./ -mindepth 1 -maxdepth 3 -type f -name *.sql -mmin +1440 | xargs rm -rf +echo "-------------$(date +"%y-%m-%d %H:%M:%S") backup end ----------------------" >> $logFile + +FFMPEG_JPG=$(/usr/bin/ffmpeg -y -ss 00:00:10 -i ${FULL_NAME} -vframes 1 ${DIR_NAME}/${BASE_NAME}.jpg && echo "success" || echo "fail") \ No newline at end of file diff --git a/Nginx-Rtmp/slimp_backup_db.sh b/Nginx-Rtmp/slimp_backup_db.sh new file mode 100644 index 0000000..ef0b05d --- /dev/null +++ b/Nginx-Rtmp/slimp_backup_db.sh @@ -0,0 +1,31 @@ +#!/bin/bash +####################################################### +# $Name: mysql_backup.sh +# $Version: v1.0 +# $Function: Backup MySQL Databases Script +# $Author: ShaoBo Wan (Tinywan) +# $organization: https://github.com/Tinywan +# $Create Date: 2017-06-29 +# $Description: Mysql 自动备份脚本安全加锁机制 +####################################################### + +database=weblive +databak_dir=/home/www/database_back +logs_dir=/home/www/database_back/logs +dumpbin=/usr/bin/mysqldump + +DATE=$(date +%Y%m%d) +date_time=$(date +"%y-%m-%d %H:%M:%S") +backup_name=sansan_bak1_${DATE}.sql +logFile=$logs_dir/sansan_${DATE}.log + +echo " " > $logFile +echo "-------------$(date +"%y-%m-%d %H:%M:%S") backup start ----------------------" >> $logFile + +$dumpbin $database >$databak_dir/$backup_name +/bin/bzip2 $backup_name +cd $databak_dir +# 1 mouth = 44640 min +find ./ -mindepth 1 -maxdepth 3 -type f -name *.bz2 -mmin +43200 | xargs rm -rf +find ./ -mindepth 1 -maxdepth 3 -type f -name *.sql -mmin +1440 | xargs rm -rf +echo "-------------$(date +"%y-%m-%d %H:%M:%S") backup end ----------------------" >> $logFile diff --git a/Nginx/Nginx-Web/Nginx-2-4-all-config.md b/Nginx/Nginx-Web/Nginx-2-4-all-config.md new file mode 100644 index 0000000..6ed123e --- /dev/null +++ b/Nginx/Nginx-Web/Nginx-2-4-all-config.md @@ -0,0 +1,75 @@ + +#### Nginx服务器基础配置命令 +--- ++ 基于域名的虚拟主机配置 + + 语法格式: + ``` + server_name name name2 name3 ... # 可以有一个或者多个名称并列,之间用空格隔开 + ``` + + 普通案例: + ``` + server_name www.tinywan.com www.redis.com # 第一个名称作为虚拟主机的主要名称 + ``` + + 通配符案例: + ``` + server_name *.tinywan.com www.redis.* # 通配符只能用在域名字符串名称的手段或尾端 + ``` + + 正则匹配案例: + ``` + server_name ~^www\d+\.tinywan.com$ + ``` + > 波浪号 ` ~ ` 作为正则表达式字符串的开始标记 + > 正则表达式含义: + >>` ^www ` 以 www 开头 + >>` \d+ ` ,`\d` 代表 0 ~ 9 的某一个数字,`+` 表示以前的字符出现一次或者多次 + >>`\.` ,由于`.`在正则表达式有特殊含义,因此需要转义字符`\`进行转义 + >> ` m$` 表示一个m结束 + + + 访问服务器域名 + > 可以访问 + ``` + www1.tinywan.com + ``` + + > 不可以访问 + ``` + www.tinywan.com + ``` ++ 基于IP的虚拟主机配置 + + 本机IP地址为: `192.168.127.129` + + 添加 IP 别名 (`192.168.127.131` 和 `192.168.127.141`) + ``` + sudo ifconfig ens33:0 192.168.127.131 netmask 255.255.255.0 up + + sudo ifconfig ens33:1 192.168.127.141 netmask 255.255.255.0 up + ``` + + 将以上两条命令添加到Linux 系统的启动脚本rc.local 中,系统重启后,ens33 的别名就自动设置好了(注意:sudo 权限) + ``` + echo "ifconfig ens33:1 192.168.127.131 netmask 255.255.255.0 up" >> /etc/rc.local + + echo "ifconfig ens33:1 192.168.127.131 netmask 255.255.255.0 up" >> /etc/rc.local + ``` + + /etc/rc.local 解释 + >在Linux启动的最后阶段,系统会执行存于rc.local中的命令 + + + 配置虚拟主机 + ``` + server { + listen 80; + server_name 192.168.127.131; + location / { + root /usr/local/nginx/html2; + } + } + + server { + listen 80; + server_name 192.168.127.141; + location / { + root /usr/local/nginx/html3; + } + } + ``` + + + diff --git a/Nginx/Nginx-Web/Nginx-2-4-basic-config.md b/Nginx/Nginx-Web/Nginx-2-4-basic-config.md new file mode 100644 index 0000000..d6dc13a --- /dev/null +++ b/Nginx/Nginx-Web/Nginx-2-4-basic-config.md @@ -0,0 +1,302 @@ + +#### 基础配置文件 +--- ++ 完整基础配置nginx.conf +``` +user www www; ## Default: nobody +worker_processes 5; ## Default: 1 +error_log logs/error.log; +pid logs/nginx.pid; +worker_rlimit_nofile 8192; + +events { + worker_connections 4096; ## Default: 1024 +} + +http { + include conf/mime.types; + include /etc/nginx/proxy.conf; + include /etc/nginx/fastcgi.conf; + index index.html index.htm index.php; + + default_type application/octet-stream; + log_format main '$remote_addr - $remote_user [$time_local] $status ' + '"$request" $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + access_log logs/access.log main; + sendfile on; + tcp_nopush on; + server_names_hash_bucket_size 128; # this seems to be required for some vhosts + + server { # php/fastcgi + listen 80; + server_name domain1.com www.domain1.com; + access_log logs/domain1.access.log main; + root html; + + location ~ \.php$ { + fastcgi_pass 127.0.0.1:1025; + } + } + + server { # simple reverse-proxy + listen 80; + server_name domain2.com www.domain2.com; + access_log logs/domain2.access.log main; + + # serve static files + location ~ ^/(images|javascript|js|css|flash|media|static)/ { + root /var/www/virtual/big.server.com/htdocs; + expires 30d; + } + + # pass requests for dynamic content to rails/turbogears/zope, et al + location / { + proxy_pass http://127.0.0.1:8080; + } + } + + upstream big_server_com { + server 127.0.0.3:8000 weight=5; + server 127.0.0.3:8001 weight=5; + server 192.168.0.1:8000; + server 192.168.0.1:8001; + } + + server { # simple load balancing + listen 80; + server_name big.server.com; + access_log logs/big.server.access.log main; + + location / { + proxy_pass http://big_server_com; + } + } +} +``` ++ proxy_conf 扩展参数 +``` +proxy_redirect off; +proxy_set_header Host $host; +proxy_set_header X-Real-IP $remote_addr; +proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; +client_max_body_size 10m; +client_body_buffer_size 128k; +proxy_connect_timeout 90; +proxy_send_timeout 90; +proxy_read_timeout 90; +proxy_buffers 32 4k; +``` ++ fastcgi_conf 扩展参数 +``` +fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; +fastcgi_param QUERY_STRING $query_string; +fastcgi_param REQUEST_METHOD $request_method; +fastcgi_param CONTENT_TYPE $content_type; +fastcgi_param CONTENT_LENGTH $content_length; +fastcgi_param SCRIPT_NAME $fastcgi_script_name; +fastcgi_param REQUEST_URI $request_uri; +fastcgi_param DOCUMENT_URI $document_uri; +fastcgi_param DOCUMENT_ROOT $document_root; +fastcgi_param SERVER_PROTOCOL $server_protocol; +fastcgi_param GATEWAY_INTERFACE CGI/1.1; +fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; +fastcgi_param REMOTE_ADDR $remote_addr; +fastcgi_param REMOTE_PORT $remote_port; +fastcgi_param SERVER_ADDR $server_addr; +fastcgi_param SERVER_PORT $server_port; +fastcgi_param SERVER_NAME $server_name; + +fastcgi_index index.php; + +fastcgi_param REDIRECT_STATUS 200; 32 4k; +``` ++ mime_types 扩展参数 +``` +types { + text/html html htm shtml; + text/css css; + text/xml xml rss; + image/gif gif; + image/jpeg jpeg jpg; + application/x-javascript js; + text/plain txt; + text/x-component htc; + text/mathml mml; + image/png png; + image/x-icon ico; + image/x-jng jng; + image/vnd.wap.wbmp wbmp; + application/java-archive jar war ear; + application/mac-binhex40 hqx; + application/pdf pdf; + application/x-cocoa cco; + application/x-java-archive-diff jardiff; + application/x-java-jnlp-file jnlp; + application/x-makeself run; + application/x-perl pl pm; + application/x-pilot prc pdb; + application/x-rar-compressed rar; + application/x-redhat-package-manager rpm; + application/x-sea sea; + application/x-shockwave-flash swf; + application/x-stuffit sit; + application/x-tcl tcl tk; + application/x-x509-ca-cert der pem crt; + application/x-xpinstall xpi; + application/zip zip; + application/octet-stream deb; + application/octet-stream bin exe dll; + application/octet-stream dmg; + application/octet-stream eot; + application/octet-stream iso img; + application/octet-stream msi msp msm; + audio/mpeg mp3; + audio/x-realaudio ra; + video/mpeg mpeg mpg; + video/quicktime mov; + video/x-flv flv; + video/x-msvideo avi; + video/x-ms-wmv wmv; + video/x-ms-asf asx asf; + video/x-mng mng; +} +``` ++ 生产环境的完整配置nginx.conf +``` +user www www; +worker_processes 2; +pid /var/run/nginx.pid; + +# [ debug | info | notice | warn | error | crit ] +error_log /var/log/nginx.error_log info; + +events { + worker_connections 2000; + # use [ kqueue | rtsig | epoll | /dev/poll | select | poll ] ; + use kqueue; +} + +http { + include conf/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] ' + '"$request" $status $bytes_sent ' + '"$http_referer" "$http_user_agent" ' + '"$gzip_ratio"'; + + log_format download '$remote_addr - $remote_user [$time_local] ' + '"$request" $status $bytes_sent ' + '"$http_referer" "$http_user_agent" ' + '"$http_range" "$sent_http_content_range"'; + + client_header_timeout 3m; + client_body_timeout 3m; + send_timeout 3m; + + client_header_buffer_size 1k; + large_client_header_buffers 4 4k; + + gzip on; + gzip_min_length 1100; + gzip_buffers 4 8k; + gzip_types text/plain; + + output_buffers 1 32k; + postpone_output 1460; + + sendfile on; + tcp_nopush on; + + tcp_nodelay on; + send_lowat 12000; + + keepalive_timeout 75 20; + + # lingering_time 30; + # lingering_timeout 10; + # reset_timedout_connection on; + + + server { + listen one.example.com; + server_name one.example.com www.one.example.com; + + access_log /var/log/nginx.access_log main; + + location / { + proxy_pass http://127.0.0.1/; + proxy_redirect off; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + client_max_body_size 10m; + client_body_buffer_size 128k; + + client_body_temp_path /var/nginx/client_body_temp; + + proxy_connect_timeout 90; + proxy_send_timeout 90; + proxy_read_timeout 90; + proxy_send_lowat 12000; + + proxy_buffer_size 4k; + proxy_buffers 4 32k; + proxy_busy_buffers_size 64k; + proxy_temp_file_write_size 64k; + + proxy_temp_path /var/nginx/proxy_temp; + + charset koi8-r; + } + + error_page 404 /404.html; + + location /404.html { + root /spool/www; + + charset on; + source_charset koi8-r; + } + + location /old_stuff/ { + rewrite ^/old_stuff/(.*)$ /new_stuff/$1 permanent; + } + + location /download/ { + valid_referers none blocked server_names *.example.com; + + if ($invalid_referer) { + #rewrite ^/ http://www.example.com/; + return 403; + } + + # rewrite_log on; + # rewrite /download/*/mp3/*.any_ext to /download/*/mp3/*.mp3 + rewrite ^/(download/.*)/mp3/(.*)\..*$ /$1/mp3/$2.mp3 break; + + root /spool/www; + # autoindex on; + access_log /var/log/nginx-download.access_log download; + } + + location ~* ^.+\.(jpg|jpeg|gif)$ { + root /spool/www; + access_log off; + expires 30d; + } + + location ~ \.php$ { + fastcgi_pass unix:/var/run/php7.0.9-fpm.sock; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; + } + + } +} +``` + diff --git a/Nginx/Nginx-Web/Nginx-2-4-log-cut.md b/Nginx/Nginx-Web/Nginx-2-4-log-cut.md new file mode 100644 index 0000000..2450164 --- /dev/null +++ b/Nginx/Nginx-Web/Nginx-2-4-log-cut.md @@ -0,0 +1,172 @@ +#### 脚本思路 ++ 第一步就是重命名日志文件,不用担心重命名后nginx找不到日志文件而丢失日志。在你未重新打开原名字的日志文件前,nginx还是会向你重命名的文件写日志,linux是靠文件描述符而不是文件名定位文件。 ++ 第二步向nginx主进程发送USR1信号。nginx主进程接到信号后会从配置文件中读取日志文件名称,重新打开日志文件(以配置文件中的日志名称命名),并以工作进程的用户作为日志文件的所有者。重新打开日志文件后,nginx主进程会关闭重名的日志文件并通知工作进程使用新打开的日志文件。工作进程立刻打开新的日志文件并关闭重名名的日志文件。 ++ 然后你就可以处理旧的日志文件了。 ++ [Nginx日志切割shell脚本](http://www.jb51.net/article/47884.htm) +#### 日志 +--- ++ 日志格式允许包含的变量注释 + ``` + $remote_addr, $http_x_forwarded_for(反向) --记录客户端IP地址 + $remote_user --记录客户端用户名称 + $request --记录请求的URL和HTTP协议 + $status --记录请求状态 + $body_bytes_sent --发送给客户端的字节数,不包括响应头的大小; 该变量与Apache模块mod_log_config里的“%B”参数兼容。 + $bytes_sent --发送给客户端的总字节数。 + $connection --连接的序列号。 + $connection_requests --当前通过一个连接获得的请求数量。 + $msec --日志写入时间。单位为秒,精度是毫秒。 + $pipe --如果请求是通过HTTP流水线(pipelined)发送,pipe值为“p”,否则为“.”。 + $http_referer --记录从哪个页面链接访问过来的 + $http_user_agent --记录客户端浏览器相关信息(注意:个别浏览器是空的) + $request_length --请求的长度(包括请求行,请求头和请求正文)。 + $request_time --请求处理时间,单位为秒,精度毫秒; 从读入客户端的第一个字节开始,直到把最后一个字符发送给客户端后进行日志写入为止。 + $time_iso8601 --ISO8601标准格式下的本地时间。 + $time_local --通用日志格式下的本地时间。 + ``` ++ Nginx位于负载均衡器,squid,nginx反向代理之后,web服务器无法直接获取到客户端真实的IP地址 + + $remote_addr获取反向代理的IP地址。反向代理服务器在转发请求的http头信息中,可以增加X-Forwarded-For信息,用来记录 客户端IP地址和客户端请求的服务器地址 ++ 日志文件的切割 + + 查看主进程号( master process ):`cat /var/run/nginx.pid` + + 停止 + + 配置了pid文件存放路径则,QUIT向NGINX主进程发送(优雅关机)信号的方法:`kill -QUIT $(cat /usr/local/nginx/logs/nginx.pid )` + + 从容停止: `kill -QUIT 主进程号` + + 快速停止:`kill -TERM 主进程号` + + 强制停止:`kill -9 主进程号` + + 重启 + + 验证配置文件是否正确: `/usr/local/nginx/sbin/nginx -t` + + 重启方式一:`kill -HUP 主进程号` + >[1] 配置重新加载 + >[2] 使用新配置启动新的工作进程 + >[3] 正常关闭旧的工作进程 + + + 重启方式二:`/usr/local/nginx/sbin/nginx -s reload` + > `-s ` 参数包含四个命令分别是 `stop/quit/reopen/reload` (发送信号到主进程:停止,退出,重新打开,重新加载) + + + 发送 kill -USR1 信号给Nginx 主进程号,让Nginx 生成一个新的日志文件 `/usr/local/nginx/logs/access.log` + + 单日志备份Shell 脚本 `cut_nginx_log.sh` + ``` + #!/bin/bash + # ====================================================================================== + # chmod u+x /opt/nginx/cut_nginx_log.sh + # crontab -e + # 0 0 * * * /home/tinywan/bin/cut_nginx_log.sh > /home/tinywan/bin/cut_nginx_log.log 2>&1 + # ======================================================================================= + + LOGS_PATH="/usr/local/nginx/logs" + YEAR=$(date -d "yesterday" "+%Y") + MONTH=$(date -d "yesterday" "+%m") + # 获取昨天的日期 + DATE=$(date -d "yesterday" "+%Y%m%d_%H%M%S") + echo "YEAR : ${YEAR} MONTH : ${MONTH} DATE :${DATE}" + # Nginx的master 主进程号 + NGINX_PID="/var/run/nginx.pid" + # -r 检测文件是否可读,如果是,则返回 true + if [ -r ${NGINX_PID} ]; then + mkdir -p "${LOGS_PATH}/${YEAR}/${MONTH}" + mv "${LOGS_PATH}/access.log" "${LOGS_PATH}/${YEAR}/${MONTH}/access_${DATE}.log" + kill -USR1 $(cat "/var/run/nginx.pid") + sleep 1 + gzip "${LOGS_PATH}/${YEAR}/${MONTH}/access_${DATE}.log" + echo 'Nginx Cut Log Success' + else + echo "Nginx might be down" + fi + # ============================================================================== + # Clean up log files older than 100 days + # ============================================================================== + # Change HOUSEKEEPING=1 to enable clean up + HOUSEKEEPING=0 + KEEP_DAYS=100 + if [ $HOUSEKEEPING == 1 ]; then # 删除日志开关,开关为1的时候才会去根据设置的天数删除压缩日志文件 + if [ -d "${LOGS_PATH}" ]; then + find "${LOGS_PATH}" -type f -name "*.log.gz" -mtime +${KEEP_DAYS} -exec rm -f {} \; + fi + fi + ``` + + Nginx 报错误日志,由于php-fpm没有启动,就会报错以下错误信息error.log中 + ``` + 11 connect() to unix:/var/run/php7.0.9-fpm.sock failed (2: No such file or directory) while connecting to upstream + ``` + + 多日志备份Shell 脚本 `cut_ multiple_nginx_log.sh` + ``` + #!/bin/bash + # ====================================================================================== + # chmod u+x /opt/nginx/cut_multiple_nginx_log.sh + # crontab -e + # 0 0 * * * /home/tinywan/bin/cut_multiple_nginx_log.sh > /home/tinywan/bin/cut_nginx_log.log 2>&1 + # ======================================================================================= + + LOGS_PATH="/usr/local/nginx/logs" # 注意这里在路径末尾多个"/" + YEAR=$(date -d "yesterday" "+%Y") + MONTH=$(date -d "yesterday" "+%m") + # 获取昨天的日期 + DATE=$(date -d "yesterday" "+%Y%m%d_%H%M%S") + echo "YEAR : ${YEAR} MONTH : ${MONTH} DATE :${DATE}" + # Nginx的master 主进程号 + NGINX_PID="/var/run/nginx.pid" + # -r 检测文件是否可读,如果是,则返回 true + CUT_LOG(){ + if [ -r ${NGINX_PID} ]; then + mkdir -p "${LOGS_PATH}/${YEAR}/${MONTH}" + cd ${LOGS_PATH} + for i in $(ls *.log) # i = access.log/error.log/...等等 + do + FILE_NAME=$(echo ${i} | sed 's/\.log//') # FILE_NAME=access/error/...等等 + echo ${FILE_NAME} + mv "${LOGS_PATH}/${i}" "${LOGS_PATH}/${YEAR}/${MONTH}/${FILE_NAME}_${DATE}.log" + sleep 1 + gzip "${LOGS_PATH}/${YEAR}/${MONTH}/${FILE_NAME}_${DATE}.log" + done + kill -USR1 $(cat "/var/run/nginx.pid") + echo 'Nginx Cut Log Success' + else + echo "Nginx might be down" + exit 1 + fi + } + CUT_LOG + # ============================================================================== + # Clean up log files older than 100 days + # ============================================================================== + # Change HOUSEKEEPING=1 to enable clean up + HOUSEKEEPING=1 + KEEP_DAYS=100 + if [ $HOUSEKEEPING == 1 ]; then + if [ -d "${LOGS_PATH}" ]; then + find "${LOGS_PATH}" -type f -name "*.log.gz" -mtime +${KEEP_DAYS} -exec rm -f {} \; + fi + fi + ``` ++ Linux命令之 ` find ` 命令中的 `-mtime` 参数 + + mtime参数的理解应该如下: + + -mtime n 按照文件的更改时间来找文件,n为整数。 + + n表示文件更改时间距离为n天, -n表示文件更改时间距离在n天以内,+n表示文件更改时间距离在n天以前。 + + 例如: + > -mtime 0 表示文件修改时间距离当前为0天的文件,即距离当前时间不到1天(24小时)以内的文件。 + > -mtime 1 表示文件修改时间距离当前为1天的文件,即距离当前时间1天(24小时-48小时)的文件。 + > -mtime+1 表示文件修改时间为大于1天的文件,即距离当前时间2天(48小时)之外的文件 + > -mtime -1 表示文件修改时间为小于1天的文件,即距离当前时间1天(24小时)之内的文件 + + + ` find "/usr/local/nginx/logs" -type f -name "*.log.gz" -mtime 0 ` + > 查找距离当前时间不到1天(24小时)以内的文件的日志文件 + >> 查找结果: + ``` + root@tinywan:/usr/local/nginx/logs/2017/04# find "/usr/local/nginx/logs" -type f -name "*.log.gz" -mtime 0 + /usr/local/nginx/logs/2017/04/error_20170401_224602.log.gz + /usr/local/nginx/logs/2017/04/access_20170401_224602.log.gz + /usr/local/nginx/logs/2017/04/host.access_20170401_224602.log.gz + ``` + + + 为什么-mtime+1 表示文件修改时间为大于1天的文件,即距离当前时间48小时之外的文件,而不是24小时之外的呢? + + 因为n值只能是整数,即比1大的最近的整数是2,所有-mtime+1不是比当前时间大于1天(24小时),而是比当前时间大于2天(48小时)。 + + ` find . -name "*ab*" -exec rm -f {}\; ` + > 整句命令表示:在当前目录下查找以ab结尾的文件,并删除 + > ` -name “*ab” ` 表示查找以ab结尾的文件或文件名 + > `-exec` 表示执行什么命令。后面跟要执行的命令。此处是 `rm -f`,表示不确认删除 + > `{} \;` 表示把查找到的结果发送到此来 + + + + + diff --git a/Nginx/Nginx-Web/Nginx-6-ReWrite-1.md b/Nginx/Nginx-Web/Nginx-6-ReWrite-1.md new file mode 100644 index 0000000..03872a8 --- /dev/null +++ b/Nginx/Nginx-Web/Nginx-6-ReWrite-1.md @@ -0,0 +1,78 @@ + +#### rewrite 重写 +--- + ++ 重写中用到的指令 + + if (条件) {} 设定条件,再进行重写 + + If 语法格式 + ``` + If 空格 (条件) { + 重写模式 + } + ``` + + 配置案例一:禁止某一个IP地址访问 + ``` + location / { + if ( $remote_addr = 192.168.127.129 ){ # 注意:这里的if和()之间是有个空格的 + return 403; + } + root html; + } + ``` + + 配置案例二:正则表达式的用法 + ``` + # 这个没有添加break 则会一直循环重定向,服务器会相应 500 + rewrite_log on; + if ($http_user_agent ~ Mozilla){ + rewrite ^.*$ /ie.html; + } + # nginx 日志记录():rewrite or internal redirection cycle while processing "/404.html", + # 这里要开启重写日志:rewrite_log on + + # 正确配置信息 ,服务器会输出ie.html 中的内容 + rewrite_log on; + if ($http_user_agent ~ Mozilla){ + rewrite ^.*$ /ie.html; + break; + } + ``` + + + set #设置变量 + + return #返回状态码 + + break #跳出rewrite + + rewrite #重写 + ++ Nginx 全局应用的变量文件路径:root@tinywan:/usr/local/nginx/conf# cat fastcgi.conf + ``` + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; -- 脚本文件请求的路径 + fastcgi_param QUERY_STRING $query_string; -- 请求的参数;如?app=123 + fastcgi_param REQUEST_METHOD $request_method; -- 请求的方法(GET,POST) + fastcgi_param CONTENT_TYPE $content_type; -- 请求头中的Content-Type字段 + fastcgi_param CONTENT_LENGTH $content_length; -- 请求头中的Content-length字段 + + fastcgi_param SCRIPT_NAME $fastcgi_script_name; -- 脚本名称 + fastcgi_param REQUEST_URI $request_uri; -- 请求的地址不带参数 + fastcgi_param DOCUMENT_URI $document_uri; -- 与$uri相同 + fastcgi_param DOCUMENT_ROOT $document_root; -- 网站的根目录。在server配置中root指令中指定的值 + fastcgi_param SERVER_PROTOCOL $server_protocol; -- 请求使用的协议,通常是HTTP/1.0或HTTP/1.1 + fastcgi_param REQUEST_SCHEME $scheme; -- + fastcgi_param HTTPS $https if_not_empty; -- + + fastcgi_param GATEWAY_INTERFACE CGI/1.1; + fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; + + fastcgi_param REMOTE_ADDR $remote_addr; -- 客户端IP + fastcgi_param REMOTE_PORT $remote_port; -- 客户端端口 + fastcgi_param SERVER_ADDR $server_addr; -- 服务器IP地址 + fastcgi_param SERVER_PORT $server_port; -- 服务器端口 + fastcgi_param SERVER_NAME $server_name; -- 服务器名,域名在server配置中指定的server_name + + PHP : fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + ``` + +**二、错误日志** + +* 错误日志主要记录客户端访问Nginx出错时的日志,格式不支持自定义。通过错误日志,你可以得到系统某个服务或server的性能瓶颈等。因此,将日志好好利用,你可以得到很多有价值的信息。错误日志由指令error_log来指定` + + + diff --git a/Nginx/Nginx-Web/Nginx-7-Proxy-1.md b/Nginx/Nginx-Web/Nginx-7-Proxy-1.md new file mode 100644 index 0000000..6099aee --- /dev/null +++ b/Nginx/Nginx-Web/Nginx-7-Proxy-1.md @@ -0,0 +1,20 @@ + +#### 简单的负载平衡 +``` +http { +upstream myproject { + server 127.0.0.1:8000 weight=3; + server 127.0.0.1:8001; + server 127.0.0.1:8002; + server 127.0.0.1:8003; +} + +server { + listen 80; + server_name www.domain.com; + location / { + proxy_pass http://myproject; + } +} +} +``` diff --git a/Nginx/Nginx-Web/Nginx-7-Proxy.md b/Nginx/Nginx-Web/Nginx-7-Proxy.md index c7e25d5..69a0964 100644 --- a/Nginx/Nginx-Web/Nginx-7-Proxy.md +++ b/Nginx/Nginx-Web/Nginx-7-Proxy.md @@ -1,12 +1,24 @@ -#### Nginx服务器的代理服务 +#### Nginx服务器的HTTP代理服务 --- ++ 网络初始化之listen常见配置 + ```Lua + listen 127.0.0.1:8000; + listen 127.0.0.1; + listen 8000; + listen *:8000; + listen localhost:8000; + listen [::]:8000; + listen [fe80::1]; + listen unix:/var/run/nginx.sock; + ``` * **配置实例一:对所有请求实现一般轮询规则的负载均衡** ``` http { upstream live_node { # 配置后端服务器组 - server 127.0.0.1:8089; + #max_fails默认值为1,fail_timeout默认值为10s,max_fails=0表示不做检查 + server 127.0.0.1:8089 weight=1 max_fails=1 fail_timeout=10s; server 127.0.0.1:8088; keepalive 32; hash $request_uri consistent; @@ -18,6 +30,15 @@ location / { proxy_pass http://live_node; # 注意:proxy_pass后面的路径不带uri时,其会将location的uri传递给后端主机 proxy_set_header Host $host; # 保留客户端的真实信息 + proxy_set_header Host $host:$server_port; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Real-PORT $remote_port; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + proxy_redirect off; + proxy_buffer_size 128k; + proxy_buffers 32 32k; + proxy_busy_buffers_size 128k; } } @@ -40,6 +61,12 @@ } } ``` + + Nginx-proxy 详解文章链接 + + 查看错误日志 + `upstream sent too big header while reading response header from upstream` + + [Nginx-proxy_buffer_size and fastcgi_buffer](http://blog.csdn.net/u010391029/article/details/50850210) + + [http://wiki.nginx.org/NginxHttpProxyModule](http://wiki.nginx.org/NginxHttpProxyModule) + + [http://blog.sina.com.cn/s/blog_5dc960cd0100i4mt.html](http://blog.sina.com.cn/s/blog_5dc960cd0100i4mt.html) > 参数:`keepalive connections;` >>补充:`由于短连接消耗前端代理服务器的资源现象严重,因此会将一部分连接定义为长连接以节省资源` >>FUN:`#为每个worker进程保留的空闲的长连接数量` diff --git a/Nginx/Nginx-Web/Nginx-8-proxy_cache.md b/Nginx/Nginx-Web/Nginx-8-proxy_cache.md new file mode 100644 index 0000000..c106db4 --- /dev/null +++ b/Nginx/Nginx-Web/Nginx-8-proxy_cache.md @@ -0,0 +1,187 @@ +## 如何配置proxy_cache模块 ++ [官方:ngx_http_proxy_module](http://nginx.org/en/docs/http/ngx_http_proxy_module.html) ++ `Http`配置文件 + ```bash + user www; + worker_processes 1; + + error_log logs/error.log error; + + pid /run/nginx.pid; + + worker_rlimit_nofile 204800; + + events { + worker_connections 65535; + multi_accept on; + use epoll; + } + + http { + lua_package_path '/usr/local/openresty/lualib/?.lua;/usr/local/openresty/nginx/conf/waf/?.lua;'; + lua_package_cpath '/usr/local/openresty/lualib/?.so;;'; + + init_by_lua_file "/usr/local/openresty/nginx/conf/waf/init.lua"; + access_by_lua_file "/usr/local/openresty/nginx/conf/waf/waf.lua"; + + include mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"' + '"$upstream_cache_status"'; # nginx cache命中率统计 + + charset UTF-8; + client_header_buffer_size 32k; + large_client_header_buffers 4 32k; + + client_header_timeout 100; + client_body_timeout 100; + client_max_body_size 800m; + client_body_buffer_size 512k; + reset_timedout_connection on; + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + + keepalive_timeout 75 20; + + proxy_connect_timeout 5; + proxy_send_timeout 5; + proxy_read_timeout 60; + # 是否启用或者关闭 proxy_buffer,默认为 on + proxy_buffering on; + # 设置缓存大小,默认4KB、8KB 保持与 proxy_buffers 指令中size变量相同或者更小 + proxy_buffer_size 16k; + # proxy_buffer个数和Buffer大小(一般设置为内存页大小) + proxy_buffers 4 64k; + # 限制处于 BUSY 状态的 proxy_buffer 的总大小 + proxy_busy_buffers_size 128k; + # 所有临时文件总体积大小,磁盘上的临时文件不能超过该配置 + proxy_max_temp_file_size 500MB; + # 配置同时写入临时文件的数据量的总大小 + proxy_temp_file_write_size 128k; + + gzip on; + gzip_min_length 1k; + gzip_buffers 4 64k; + gzip_http_version 1.1; + gzip_comp_level 6; + gzip_types text/plain application/x-javascript text/css application/javascript text/javascript image/jpeg image/gif image/png application/xml application/json; + gzip_vary on; + gzip_disable "MSIE [1-6].(?!.*SV1)"; + + # 文件路径,临时存放代理服务器的大体积响应数据 + proxy_temp_path /home/www/data/nginx/tmp-test; + # 设置WEB缓存区名称为 cache_one ,内存缓存空间大小为100M,一天清理一次,硬盘缓存空间大小为10G + proxy_cache_path /home/www/data/nginx/cache-test levels=1:2 keys_zone=cache_one:100m inactive=1d max_size=10g; + + index index.php index.html index.htm; + include "/usr/local/openresty/nginx/conf/vhost/*.conf"; + } + ``` ++ `Server`配置文件 + + ```bash + server { + listen 8087; + server_name localhost; + + location / { + # 如果后端的服务器返回500 502 503 504 执行超时等错误,将请求转发到另外一台服务器 + proxy_next_upstream http_500 http_502 http_503 http_504 error timeout invalid_header; + # 定义用于缓存的共享内存区域 + proxy_cache cache_one; + # 针对不同的HTTP状态码设置不同的缓存时间 + proxy_cache_valid 200 304 1h; + proxy_cache_valid 404 1m; + # WEB缓存的Key值域名、URI、参数组成 + proxy_cache_key $host$uri$is_args$args; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + # 显示缓存的状态 + add_header Nginx-Cache "$upstream_cache_status"; + # 可以禁用一个或多个响应头字段的处理 [Nginx不缓存,可以添加以下语句] + proxy_ignore_headers X-Accel-Expires Expires Cache-Control Set-Cookie; + proxy_pass http://www.tinywan.com; + expires 1d; + } + } + ``` ++ 配置测试 + + 打印响应头 + ```bash + www@TinywanAliYun:$ curl -I http://127.0.0.1:8087/ + HTTP/1.1 200 OK + Server: openresty/1.11.2.5 + Date: Sat, 18 Nov 2017 15:46:55 GMT + Content-Type: text/html; charset=utf-8 + Connection: keep-alive + Keep-Alive: timeout=20 + Vary: Accept-Encoding + Expires: Sun, 19 Nov 2017 15:46:55 GMT + Cache-Control: max-age=86400 + Pragma: no-cache + Nginx-Cache: HIT + ``` + + 缓存目录 + ```bash + www@TinywanAliYun:~/data/nginx/cache-test$ tree -L 4 + . + ├── 0 + │   └── 49 + │   └── 51ab3cb31fd7929a0346796693d53490 + └── 9 + └── f4 + └── 3a020dc16513d3abee9ba74688d53f49 + + 4 directories, 2 files + www@TinywanAliYun:~/data/nginx/cache-test$ cat 0/49/51ab3cb31fd7929a0346796693d53490 + ³QZݵǘ£CZC°(±"58c8b5dd-423e" + KEY: 127.0.0.1/favicon.ico // 代理服务IP地址 + HTTP/1.1 200 OK + Server: nginx/1.6.0 // 被代理服务器信息 + Date: Sat, 18 Nov 2017 14:28:51 GMT + Content-Type: image/x-icon + Content-Length: 16958 + Last-Modified: Wed, 15 Mar 2017 03:32:45 GMT + Connection: close + ETag: "58c8b5dd-423e" + Accept-Ranges: bytes + ``` ++ `$upstream_cache_status` 包含以下几种状态 + + ```bash + ·MISS 未命中,请求被传送到后端 + ·HIT 缓存命中 + ·EXPIRED 缓存已经过期请求被传送到后端 + ·UPDATING 正在更新缓存,将使用旧的应答 + ·STALE 后端将得到过期的应答 + ``` ++ `nginx cache`命中率统计 + + ```bash + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"' + '"$upstream_cache_status"'; + ``` + > 命中率统计方法:用HIT的数量除以日志总量得出缓存命中率 + + > `awk '{if($NF==""HIT"") hit++} END {printf "%.2f%",hit/NR}' access.log` + ++ 通过crontab脚本将每天的命中率统计到一个日志中,以备查看 + + ```bash + #!/bin/bash + LOG_FILE='/usr/local/nginx/logs/access.log.1' + LAST_DAY=$(date +%F -d "-1 day") + awk '{if($NF==""HIT"") hit++} END {printf "'$LAST_DAY': %d %d %.2f%n", hit,NR,hit/NR}' $LOG_FILE + ``` ++ 帮助文档 + + [Nginx proxy_cache 缓存配置](http://blog.csdn.net/dengjiexian123/article/details/53386586) + + [Nginx Proxy Cache原理和最佳实践](http://www.jianshu.com/p/625c2b15dad5) + + [nginx缓存设置proxy_cache(PHP)](https://www.cnblogs.com/zlingh/p/5879988.html) \ No newline at end of file diff --git a/Nginx/Nginx-Web/Nginx-8-tcp-Proxy.md b/Nginx/Nginx-Web/Nginx-8-tcp-Proxy.md new file mode 100644 index 0000000..7bffc9a --- /dev/null +++ b/Nginx/Nginx-Web/Nginx-8-tcp-Proxy.md @@ -0,0 +1,20 @@ + +#### Nginx服务器TCP代理服务 +--- +* **Nginx 官方自带配置** + ``` + stream { + upstream rtmp { + server 127.0.0.1:8089; # 这里配置成要访问的地址 + server 127.0.0.2:1935; + server 127.0.0.3:1935; #需要代理的端口,在这里我代理一一个RTMP模块的接口1935 + } + server { + listen 1935; # 需要监听的端口 + proxy_timeout 20s; + proxy_pass rtmp; + } + } + ``` ++ [参考博客地址](http://www.cnblogs.com/tinywan/p/6560889.html) + diff --git a/Nginx/Nginx-Web/ngx_http_upstream_module.md b/Nginx/Nginx-Web/ngx_http_upstream_module.md new file mode 100644 index 0000000..51e6252 --- /dev/null +++ b/Nginx/Nginx-Web/ngx_http_upstream_module.md @@ -0,0 +1,209 @@ +## 详解:Nginx 反向代理、后端检测模块 + +#### Nginx + +``` +shell > yum -y install gcc gcc-c++ make wget zlib-devel pcre-devel openssl-devel +shell > wget http://nginx.org/download/nginx-1.12.2.tar.gz +shell > tar zxf nginx-1.12.2.tar.gz; cd nginx-1.12.2 +shell > ./configure --prefix=/usr/local/nginx-1.12.2 && make && make install +``` +#### 后端服务器 + +``` +shell > curl 192.168.10.24:8080 +welcome to tomcat1 +shell > curl 192.168.10.24:8081 +welcome to tomcat2 +shell > curl 192.168.10.24:8082 +welcome to tomcat3 +``` +好了,三台后端服务器已经启动,分别监听 8080、8081、8082,分别返回 1、2、3 + +配置`ngx_http_proxy_module`和`ngx_http_upstream_module`模块 + + +编辑配置文件`vim conf/nginx.conf` +``` +user nobody; +worker_processes 1; + +pid logs/nginx.pid; +events { + worker_connections 1024; +} + +http { + include mime.types; + default_type application/octet-stream; + + upstream ls { + server 192.168.10.24:8080 weight=1 max_fails=3 fail_timeout=20s; + server 192.168.10.24:8081 weight=2 max_fails=3 fail_timeout=20s; + server 192.168.10.24:8082 weight=3 max_fails=3 fail_timeout=20s; + } + + server { + listen 80; + + location / { + proxy_pass http://ls; + } + } +} +``` +这是一个最简配的 Nginx 配置文件,定义了一个负载均衡池,池中有三台服务器,权重分别是 1、2、3 ( 越大越高 ) + +最大失败次数 3 次,超过 3 次失败后,20 秒内不检测。 + +当用户访问该 IP 的 80 端口时,被转发到后端的服务器。下面是一些反向代理的配置。 + +``` +# 故障转移策略,当后端服务器返回如下错误时,自动负载到后端其余机器 +proxy_next_upstream http_500 http_502 http_503 error timeout invalid_header; + +# 设置后端服务器获取用户真实IP、代理者真实IP等 +proxy_redirect off; +proxy_set_header Host $host; +proxy_set_header X-Real-IP $remote_addr; +proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + +# 用于指定客户端请求主体缓存区大小,可以理解成先保存到本地再传给用户 +client_body_buffer_size 128k; + +# 表示与后端服务器连接的超时时间,即发起握手等侯响应的超时时间 +proxy_connect_timeout 90; + +# 表示后端服务器的数据回传时间,即在规定时间之后端服务器必须传完所有的数据,否则 Nginx 将断开这个连接 +proxy_send_timeout 90; + +# 设置 Nginx 从代理的后端服务器获取信息的时间,表示连接建立成功后,Nginx 等待后端服务器的响应时间,其实是 Nginx 已经进入后端的排队中等候处理的时间 +proxy_read_timeout 90; + +# 设置缓冲区大小,默认该缓冲区大小等于指令 proxy_buffers 设置的大小 +proxy_buffer_size 4k; + +# 设置缓冲区的数量和大小。Nginx 从代理的后端服务器获取的响应信息,会放置到缓冲区 +proxy_buffers 4 32k; + +# 用于设置系统很忙时可以使用的 proxy_buffers 大小,官方推荐大小为 proxu_buffers 的两倍 +proxy_busy_buffers_size 64k; + +# 指定 proxy 缓存临时文件的大小 +proxy_temp_file_write_size 64k; +shell > /usr/local/nginx-1.12.2/sbin/nginx -t +nginx: the configuration file /usr/local/nginx-1.12.2/conf/nginx.conf syntax is ok +nginx: configuration file /usr/local/nginx-1.12.2/conf/nginx.conf test is successful + +shell > /usr/local/nginx-1.12.2/sbin/nginx + +shell > i=0; while [ $i -lt 10 ];do curl localhost; let i++;done +welcome to tomcat2 +welcome to tomcat3 +welcome to tomcat3 +welcome to tomcat2 +welcome to tomcat1 +welcome to tomcat3 +welcome to tomcat2 +welcome to tomcat3 +welcome to tomcat3 +welcome to tomcat2 +``` + +总共请求10次,tomcat3 响应了5次,因为它的权重最高(weight=3)。 + +这样有一个问题,由于没有后端检测功能,当后端某一服务器无法提供服务时,该链接先被转发到这台机器,然后发现该机故障,而后才转发到其它机器。 + +导致资源浪费。 + +nginx_http_upstream_check_module + +``` +shell > git clone https://github.com/yaoweibin/nginx_upstream_check_module.git + +shell > yum -y install patch + +shell > cd /usr/local/src/nginx-1.12.2; patch -p1 < /usr/local/src/nginx_upstream_check_module/check_1.12.1+.patch +patching file src/http/modules/ngx_http_upstream_hash_module.c +patching file src/http/modules/ngx_http_upstream_ip_hash_module.c +patching file src/http/modules/ngx_http_upstream_least_conn_module.c +patching file src/http/ngx_http_upstream_round_robin.c +patching file src/http/ngx_http_upstream_round_robin.h +切换到 Nginx 源码目录,打补丁 ( 注意与自己的 Nginx 版本匹配 ) + +shell > ./configure --prefix=/usr/local/nginx-1.12.2 --add-module=/usr/local/src/nginx_upstream_check_module +shell > make && make install +``` +重新编译、安装 Nginx,注意加上原来的编译参数 + +`vim /usr/local/nginx-1.12.2/conf/nginx.conf` +配置文件如下所示: +``` +upstream ls { + server 192.168.10.24:8080; + server 192.168.10.24:8081; + server 192.168.10.24:8082; + + check interval=3000 rise=2 fall=5 timeout=1000 type=http; +} + +server { + listen 80; + + location / { + proxy_pass http://ls; + } + + location /status { + check_status; + access_log off; + # allow x.x.x.x; + # deny all; + } +} +``` +去掉了权重值,注意:是可以同时存在的。 + +添加了一行,检测间隔3000毫秒,连续成功2次标记为UP,连续失败5次标记为DOWN,超时时间1000毫秒,检测类型HTTP。 + +``` +shell > /usr/local/nginx-1.12.2/sbin/nginx -t +nginx: the configuration file /usr/local/nginx-1.12.2/conf/nginx.conf syntax is ok +nginx: configuration file /usr/local/nginx-1.12.2/conf/nginx.conf test is successful + +shell > /usr/local/nginx-1.12.2/sbin/nginx -s stop +shell > /usr/local/nginx-1.12.2/sbin/nginx +``` +直接 -s reload 貌似不行~ + +``` +shell > curl localhost/status?format=json +{"servers": + { + "total": 3, + "generation": 1, + "server": [ + {"index": 0, "upstream": "ls", "name": "192.168.10.24:8080", "status": "up", "rise": 20, "fall": 0, "type": "http", "port": 0}, + {"index": 1, "upstream": "ls", "name": "192.168.10.24:8081", "status": "up", "rise": 18, "fall": 0, "type": "http", "port": 0}, + {"index": 2, "upstream": "ls", "name": "192.168.10.24:8082", "status": "up", "rise": 19, "fall": 0, "type": "http", "port": 0} + ] + } +} +``` +总共有三台机器,都属于负载均衡 ls 组,状态 up,连续成功次数等等。 + +``` +shell > curl localhost/status?format=json +{"servers": + { + "total": 3, + "generation": 1, + "server": [ + {"index": 0, "upstream": "ls", "name": "192.168.10.24:8080", "status": "up", "rise": 73, "fall": 0, "type": "http", "port": 0}, + {"index": 1, "upstream": "ls", "name": "192.168.10.24:8081", "status": "down", "rise": 0, "fall": 6, "type": "http", "port": 0}, + {"index": 2, "upstream": "ls", "name": "192.168.10.24:8082", "status": "up", "rise": 68, "fall": 0, "type": "http", "port": 0} + ] + } +} +``` +关一台后端的话,就变成了这样!重启检测成功后,会被重新加入到负载均衡中! \ No newline at end of file diff --git a/Nginx/Nginx-Web/openresty-nginx-lua-Proxy.md b/Nginx/Nginx-Web/openresty-nginx-lua-Proxy.md new file mode 100644 index 0000000..67fef7d --- /dev/null +++ b/Nginx/Nginx-Web/openresty-nginx-lua-Proxy.md @@ -0,0 +1,345 @@ +### Openresty-Lua动态修改upstream后端服务 ++ nginx.conf 配置文件 + ``` + worker_processes 1; + pid logs/nginx.pid; + events { + worker_connections 1024; + } + + http { + include mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + sendfile on; + + keepalive_timeout 65; + + lua_shared_dict upstreams 1m; # 声明一个ngx多进程全局共享内存区域,_G 作为基于shm的Lua字典的存储空间ngx.shared. + upstream default_upstream { # 配置后端服务器组 + server 127.0.0.1:8081; + server 127.0.0.1:8082; + } + + upstream lua_upstream { # 配置后端服务器组 + server 127.0.0.1:8084; + server 127.0.0.1:8083; + } + + server { + listen 80; + server_name localhost; + + access_log logs/80.access.log main; + error_log logs/80.error.log error; + + location = /_switch_upstream { + content_by_lua_block{ + local ups = ngx.req.get_uri_args()["upstream"] + if ups == nil or ups == "" then + ngx.say("upstream is nil 1") + return nil + end + local host = ngx.var.http_host + local upstreams = ngx.shared.upstreams + local ups_src = upstreams:get(host) + ngx.say("Current upstream is :",ups_src) + ngx.log(ngx.WARN, host, " change upstream from ", ups_src, " to ", ups) + local succ, err, forcible = upstreams:set(host, ups) + ngx.say(host, " change upstream from ", ups_src, " to ", ups) + } + } + + location / { + set_by_lua_block $my_upstream { + local ups = ngx.shared.upstreams:get(ngx.var.http_host) + if ups ~= nil then + ngx.log(ngx.ERR, "get [", ups,"] from ngx.shared") + return ups + end + return "default_upstream" + } + + proxy_next_upstream off; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $host; + proxy_http_version 1.1; + proxy_set_header Connection ""; + proxy_pass http://$my_upstream ; + } + } + + server { + listen 8081; + server_name localhost; + + location / { + root html81; + index index.html index.htm; + } + } + + server { + listen 8082; + server_name localhost; + + location / { + root html82; + index index.html index.htm; + } + } + + server { + listen 8083; + server_name localhost; + + location / { + root html83; + index index.html index.htm; + } + } + + server { + listen 8084; + server_name localhost; + + location / { + root html84; + index index.html index.htm; + } + } + } + ``` ++ 4个端口分别对应4个html 根目录 + + html81/index.html 内容 `server name 8081` + + html82/index.html 内容 `server name 8082` + + html83/index.html 内容 `server name 8083` + + html84/index.html 内容 `server name 8084` ++ 如何切换后端upstream + + `default_upstream` 切换到 `lua_upstream` + ``` + root@tinywan:# curl http://127.0.0.1/_switch_upstream?upstream=lua_upstream + 127.0.0.1 change upstream from default_upstream to lua_upstream + ``` + + `lua_upstream` 切换(还原`default_upstream`)到 `default_upstream` + ``` + root@tinywan:# curl http://127.0.0.1/_switch_upstream?upstream=default_upstream + 127.0.0.1 change upstream from lua_upstream to default_upstream + ``` ++ 一个收集upstream_response_time的平均数据的例子 + ``` + lua_shared_dict log_dict 5M # 声明一个ngx多进程全局共享内存区域 + + server{ + location / { + proxy_pass http;//mybackend # 代理模块 + log_by_lua ' + local log_dict = ngx.shared.log_dict + local upstream_time = tonumber(ngx.var.upstream_response_time) + local sum = log_dict:get("upstream_time-sum") or 0 + sum = sum + upstream_time + log_dict:set("upsteam_time-sum", sum) + local newval, err = log_dict:incr("upstream_time-nb", 1) + if not newval and err == "not found" then + log_dict:add("upstream_time-nb", 0) + log_dict:incr("upstream_time-nb", 1) + end + ' + } + location = /status { + content_by_lua ' + local log_dict = ngx.shared.log_dict + local sum = log_dict:get("upstream_time-sum") + local nb = log_dict:get("upstream_time-nb") + + if nb and sum then + ngx.say("average upstream response time: ", sum/nb, " (", nb, " reqs)") + else + ngx.say("no data yet") + end + ' + } + } + ``` ++ 动态修改upstream后端服务和upstream_response_time的平均数据 合并后的代码 + ``` + #user nobody; + worker_processes 1; + pid logs/nginx.pid; + events { + worker_connections 1024; + } + + http { + include mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + sendfile on; + + keepalive_timeout 65; + + lua_shared_dict upstreams 1m; # ngx多进程全局共享内存,保存upstream值 + upstream default_upstream { # 配置后端服务器组 + server 127.0.0.1:8081; + server 127.0.0.1:8082; + } + + upstream lua_upstream { # 配置后端服务器组 + server 127.0.0.1:8084; + server 127.0.0.1:8083; + } + + + server { + listen 80; + server_name localhost; + + access_log logs/80.access.log main; + error_log logs/80.error.log error; + + location = /_switch_upstream { + default_type 'text/html'; + content_by_lua_block{ + local ups = ngx.req.get_uri_args()["upstream"] + if ups == nil or ups == "" then + ngx.say("upstream is nil 1") + return nil + end + local host = ngx.var.http_host + local upstreams = ngx.shared.upstreams + local ups_src = upstreams:get(host) + ngx.say("Current upstream is :",ups_src) + ngx.log(ngx.WARN, host, " change upstream from ", ups_src, " to ", ups) + local succ, err, forcible = upstreams:set(host, ups) + ngx.say(host, " change upstream from ", ups_src, " to ", ups) + } + } + + #location ~ (^/api/|^/p/|^/m/|^/oauthapi/) { + location / { + set_by_lua_block $my_upstream { + local ups = ngx.shared.upstreams:get(ngx.var.http_host) + if ups ~= nil then + ngx.log(ngx.ERR, "get [", ups,"] from ngx.shared") + return ups + end + return "default_upstream" + } + + proxy_next_upstream off; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $host; + proxy_http_version 1.1; + proxy_set_header Connection ""; + proxy_pass http://$my_upstream ; + + log_by_lua_block { + local log_dict = ngx.shared.upstreams + local upstream_time = tonumber(ngx.var.upstream_response_time) + + local sum = log_dict:get("upstream_time-sum") or 0 + sum = sum + upstream_time + log_dict:set("upstream_time-sum", sum) + + local newval, err = log_dict:incr("upstream_time-nb", 1) + if not newval and err == "not found" then + log_dict:add("upstream_time-nb", 0) + log_dict:incr("upstream_time-nb", 1) + end + } + } + + location = /status { + default_type 'text/html'; -- 通过浏览器可访问 + content_by_lua_block { + local log_dict = ngx.shared.upstreams + local sum = log_dict:get("upstream_time-sum") + local nb = log_dict:get("upstream_time-nb") + + if nb and sum then + ngx.say("average upstream response time: ", sum / nb, + " (", nb, " reqs)") + else + ngx.say("no data yet") + end + } + } + + } + + server { + listen 8099; + server_name localhost; + + #charset koi8-r; + + access_log logs/80.access.log main; + error_log logs/80.error.log error; + location / { + #proxy_pass http://live_node; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root html; + } + } + + server { + listen 8081; + server_name localhost; + + location / { + root html81; + index index.html index.htm; + } + } + + server { + listen 8082; + server_name localhost; + + location / { + root html82; + index index.html index.htm; + } + } + + server { + listen 8083; + server_name localhost; + + location / { + root html83; + index index.html index.htm; + } + } + + server { + listen 8084; + server_name localhost; + + location / { + root html84; + index index.html index.htm; + } + } + } + ``` ++ 测试结果: + ``` + root@tinywan:/opt/openresty/nginx/conf# curl http://127.0.0.1/status + average upstream response time: 0.0003953488372093 (129 reqs) + ``` ++ 浏览器测试,记得加上这个`default_type 'text/html';`,在浏览器地址栏直接输入即可访问 + `http://127.0.0.1/_switch_upstream?upstream=default_upstream` + ++ HELP:[http://chattool.sinaapp.com/?p=2372](http://chattool.sinaapp.com/?p=2372) \ No newline at end of file diff --git a/Nginx/more-domain-config.md b/Nginx/more-domain-config.md new file mode 100644 index 0000000..670bdca --- /dev/null +++ b/Nginx/more-domain-config.md @@ -0,0 +1,147 @@ +## Nginx 同一个IP上配置多个HTTPS主机 ++ [Nginx 同一个IP上配置多个HTTPS主机](http://www.ttlsa.com/web/multiple-https-host-nginx-with-a-ip-configuration/) ++ 域名列表 + + | 序号 | 名称 | 域名 | HTTPS主机 | + | :--: |:--: |:---------------:| :-----| + | 1 | 官方域名 | www.tinywan.com | https://www.tinywan.com/ | + | 2 | 直播域名 | live.tinywan.com | https://live.tinywan.com/ | + | 3 | 点播域名 | vod.tinywan.com | https://vod.tinywan.com/ | + | 4 | 文档域名 | doc.tinywan.com | https://doc.tinywan.com/ | + ++ Openresty 编译 + + ```bash + www@TinywanAliYun:~/DEMO/openresty-1.11.2.5$ + ./configure --prefix=/usr/local/openresty --with-luajit \ + --with-http_ssl_module --with-openssl=/usr/local/openssl \ + --with-openssl-opt="enable-tlsext" --without-http_redis2_module \ + --with-http_iconv_module --with-http_stub_status_module \ + --with-http_xslt_module --add-dynamic-module=/home/www/DEMO/nginx-ts-module \ + --add-dynamic-module=/home/www/DEMO/nginx-rtmp-module + ... + make + sudo make install + ``` + > 注意添加配置:`--with-openssl-opt="enable-tlsext" `,默认情况下是`TLS SNI support disabled` ++ `Nginx.conf`配置文件: + + 配置文件列表 + + ```bash + www@TinywanAliYun:/usr/local/openresty/nginx/conf/vhost$ ls + doc.tinywan.com.conf live_rtmp_hls.conf live.tinywan.com.conf + main.conf vod.tinywan.com.conf www.tinywan.com.conf + ``` + + `nginx.conf` + + ```bash + http { + ... + index index.php index.html index.htm; + include "/usr/local/openresty/nginx/conf/vhost/*.conf"; + } + ``` + + `main.conf` + + ```bash + # 配置HTTP请求重定向 + server { + listen 80; + server_name www.tinywan.com; #live.tinywan.com vod.tinywan.com; + rewrite ^ https://$http_host$request_uri? permanent; + } + ``` + + `www.tinywan.com.conf` + + ```bash + server { + #listen 80; + listen 443 ssl; + server_name www.tinywan.com; + set $root_path /home/www/web/go-study-line/public; + root $root_path; + + ssl on; + ssl_certificate /etc/letsencrypt/live/www.tinywan.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/www.tinywan.com//privkey.pem; + server_tokens off; + + location / { + #access_by_lua_file /usr/local/openresty/nginx/conf/lua_script/resty-limit-req.lua; + if (!-e $request_filename) { + rewrite ^(.*)$ /index.php?s=/$1 last; + break; + } + } + + location = /favicon.ico { + log_not_found off; + } + + location ~ \.php$ { + #access_by_lua_file /usr/local/openresty/nginx/conf/lua_script/resty-limit-req.lua; + fastcgi_pass unix:/var/run/php7.1.8-fpm.sock; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; + fastcgi_buffer_size 128k; + fastcgi_buffers 4 256k; + fastcgi_busy_buffers_size 256k; + fastcgi_connect_timeout 300; + fastcgi_send_timeout 300; + fastcgi_read_timeout 300; + } + } + ``` + + `live.tinywan.com.conf` + + ```bash + # live.tinywan.com + server{ + listen 443 ssl; + server_name live.tinywan.com; + + root /home/www/web/live.tinywan.com; + + ssl on; + ssl_certificate /etc/letsencrypt/live/www.tinywan.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/www.tinywan.com//privkey.pem; + server_tokens off; + + } + ``` + + `vod.tinywan.com.conf` + + ```bash + # vod.tinywan.com + server{ + listen 443 ssl; + server_name vod.tinywan.com; + + root /home/www/web/vod.tinywan.com; + + ssl on; + ssl_certificate /etc/letsencrypt/live/www.tinywan.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/www.tinywan.com//privkey.pem; + server_tokens off; + + } + ``` + + `doc.tinywan.com.conf` + + ```bash + # doc.tinywan.com + server{ + listen 443 ssl; + server_name doc.tinywan.com; + + root /home/www/web/doc.tinywan.com; + + ssl on; + ssl_certificate /etc/letsencrypt/live/www.tinywan.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/www.tinywan.com//privkey.pem; + server_tokens off; + + } + ``` + \ No newline at end of file diff --git a/Nginx/nginx b/Nginx/nginx new file mode 100644 index 0000000..42ea15a --- /dev/null +++ b/Nginx/nginx @@ -0,0 +1,408 @@ +#! /bin/sh +### BEGIN INIT INFO +# Provides: nginx +# Required-Start: $remote_fs $syslog +# Required-Stop: $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: nginx init.d dash script for Ubuntu or other *nix. +# Description: nginx init.d dash script for Ubuntu or other *nix. +### END INIT INFO +#------------------------------------------------------------------------------ +# nginx - this Debian Almquist shell (dash) script, starts and stops the nginx +# daemon for Ubuntu and other *nix releases. +# +# description: Nginx is an HTTP(S) server, HTTP(S) reverse \ +# proxy and IMAP/POP3 proxy server. This \ +# script will manage the initiation of the \ +# server and it's process state. +# +# processname: nginx +# config: /usr/local/nginx/conf/nginx.conf +# pidfile: /usr/local/nginx/logs/nginx.pid +# Provides: nginx +# +# Author: Jason Giedymin +# . +# +# Version: 3.9.0 12-MAY-2015 jason.giedymin AT gmail.com +# Notes: nginx init.d dash script for Ubuntu. +# Tested with: Ubuntu 14.10, nginx-1.7.9 +# +# This script's project home is: +# http://github.com/JasonGiedymin/nginx-init-ubuntu +# +#------------------------------------------------------------------------------ +# MIT X11 License +#------------------------------------------------------------------------------ +# +# Copyright (c) 2008-2013 Jason Giedymin, http://jasongiedymin.com +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#------------------------------------------------------------------------------ + +#------------------------------------------------------------------------------ +# Functions +#------------------------------------------------------------------------------ +LSB_FUNC=/lib/lsb/init-functions + +# Test that init functions exists +test -r $LSB_FUNC || { + echo "$0: Cannot find $LSB_FUNC! Script exiting." 1>&2 + exit 5 +} + +. $LSB_FUNC + +#------------------------------------------------------------------------------ +# Consts +#------------------------------------------------------------------------------ +# Include nginx defaults if available +if [ -f /etc/default/nginx ]; then + . /etc/default/nginx +fi + +# Minimize path +PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin + +PS=${PS:-"nginx"} # process name +DESCRIPTION=${DESCRIPTION:-"Nginx Server..."} # process description +NGINXPATH=${NGINXPATH:-/opt/openresty/nginx} # root path where installed +DAEMON=${DAEMON:-$NGINXPATH/sbin/nginx} # path to daemon binary +NGINX_CONF_FILE=${NGINX_CONF_FILE:-$NGINXPATH/conf/nginx.conf} # config file path + +PIDNAME=${PIDNAME:-"nginx"} # lets you do $PS-slave +PIDFILE=${PIDFILE:-$PIDNAME.pid} # pid file +PIDSPATH=${PIDSPATH:-$NGINXPATH/logs} # default pid location, you should change it +RUNAS=${RUNAS:-root} # user to run as + +SCRIPT_OK=0 # ala error codes +SCRIPT_ERROR=1 # ala error codes +TRUE=1 # boolean +FALSE=0 # boolean + +#------------------------------------------------------------------------------ +# Simple Tests +#------------------------------------------------------------------------------ + +# Test if nginx is a file and executable +test -x $DAEMON || { + echo "$0: You don't have permissions to execute nginx." 1>&2 + exit 4 +} + +# You can also set your conditions like so: +# set exit condition +# set -e + +#------------------------------------------------------------------------------ +# Functions +#------------------------------------------------------------------------------ + +setFilePerms(){ + if [ -f $PIDSPATH/$PIDFILE ]; then + chmod 400 $PIDSPATH/$PIDFILE + fi +} + +configtest() { + $DAEMON -t -c $NGINX_CONF_FILE +} + +getPSCount() { + return `pgrep -f $PS | wc -l` +} + +isRunning() { + if [ $1 ]; then + pidof_daemon $1 + PID=$? + + if [ $PID -gt 0 ]; then + return 1 + else + return 0 + fi + else + pidof_daemon + PID=$? + + if [ $PID -gt 0 ]; then + return 1 + else + return 0 + fi + fi +} + +#courtesy of php-fpm +wait_for_pid () { + try=0 + + while test $try -lt 35 ; do + case "$1" in + 'created') + if [ -f "$2" ]; then + try='' + break + fi + ;; + + 'removed') + if [ ! -f "$2" ]; then + try='' + break + fi + ;; + esac + + try=`expr $try + 1` + sleep 1 + done +} + +status(){ + isRunning + isAlive=$? + + if [ "${isAlive}" -eq $TRUE ]; then + log_warning_msg "$DESCRIPTION found running with processes: `pidof $PS`" + rc=0 + else + log_warning_msg "$DESCRIPTION is NOT running." + rc=3 + fi + + return +} + +removePIDFile(){ + if [ $1 ]; then + if [ -f $1 ]; then + rm -f $1 + fi + else + #Do default removal + if [ -f $PIDSPATH/$PIDFILE ]; then + rm -f $PIDSPATH/$PIDFILE + fi + fi +} + +start() { + log_daemon_msg "Starting $DESCRIPTION" + + isRunning + isAlive=$? + + if [ "${isAlive}" -eq $TRUE ]; then + log_end_msg $SCRIPT_ERROR + rc=0 + else + start-stop-daemon --start --quiet --chuid \ + $RUNAS --pidfile $PIDSPATH/$PIDFILE --exec $DAEMON \ + -- -c $NGINX_CONF_FILE + status=$? + setFilePerms + + if [ "${status}" -eq 0 ]; then + log_end_msg $SCRIPT_OK + rc=0 + else + log_end_msg $SCRIPT_ERROR + rc=7 + fi + fi + + return +} + +stop() { + log_daemon_msg "Stopping $DESCRIPTION" + + isRunning + isAlive=$? + + if [ "${isAlive}" -eq $TRUE ]; then + start-stop-daemon --stop --quiet --pidfile $PIDSPATH/$PIDFILE + + wait_for_pid 'removed' $PIDSPATH/$PIDFILE + + if [ -n "$try" ]; then + log_end_msg $SCRIPT_ERROR + rc=0 # lsb states 1, but under status it is 2 (which is more prescriptive). Deferring to standard. + else + removePIDFile + log_end_msg $SCRIPT_OK + rc=0 + fi + else + log_end_msg $SCRIPT_ERROR + rc=7 + fi + + return +} + +reload() { + configtest || return $? + + log_daemon_msg "Reloading (via HUP) $DESCRIPTION" + + isRunning + + if [ $? -eq $TRUE ]; then + kill -HUP `cat $PIDSPATH/$PIDFILE` + log_end_msg $SCRIPT_OK + rc=0 + else + log_end_msg $SCRIPT_ERROR + rc=7 + fi + + return +} + +quietupgrade() { + log_daemon_msg "Peforming Quiet Upgrade $DESCRIPTION" + + isRunning + isAlive=$? + + if [ "${isAlive}" -eq $TRUE ]; then + kill -USR2 `cat $PIDSPATH/$PIDFILE` + kill -WINCH `cat $PIDSPATH/$PIDFILE.oldbin` + + isRunning + isAlive=$? + + if [ "${isAlive}" -eq $TRUE ]; then + kill -QUIT `cat $PIDSPATH/$PIDFILE.oldbin` + wait_for_pid 'removed' $PIDSPATH/$PIDFILE.oldbin + removePIDFile $PIDSPATH/$PIDFILE.oldbin + + log_end_msg $SCRIPT_OK + rc=0 + else + log_end_msg $SCRIPT_ERROR + + log_daemon_msg "ERROR! Reverting back to original $DESCRIPTION" + + kill -HUP `cat $PIDSPATH/$PIDFILE` + kill -TERM `cat $PIDSPATH/$PIDFILE.oldbin` + kill -QUIT `cat $PIDSPATH/$PIDFILE.oldbin` + + wait_for_pid 'removed' $PIDSPATH/$PIDFILE.oldbin + removePIDFile $PIDSPATH/$PIDFILE.oldbin + + log_end_msg $SCRIPT_OK + rc=0 + fi + else + log_end_msg $SCRIPT_ERROR + rc=7 + fi + + return +} + +terminate() { + log_daemon_msg "Force terminating (via KILL) $DESCRIPTION" + + PIDS=`pidof $PS` || true + + [ -e $PIDSPATH/$PIDFILE ] && PIDS2=`cat $PIDSPATH/$PIDFILE` + + for i in $PIDS; do + if [ "$i" = "$PIDS2" ]; then + kill $i + wait_for_pid 'removed' $PIDSPATH/$PIDFILE + removePIDFile + fi + done + + log_end_msg $SCRIPT_OK + rc=0 +} + +destroy() { + log_daemon_msg "Force terminating and may include self (via KILLALL) $DESCRIPTION" + killall $PS -q >> /dev/null 2>&1 + log_end_msg $SCRIPT_OK + rc=0 +} + +pidof_daemon() { + PIDS=`pidof $PS` || true + + [ -e $PIDSPATH/$PIDFILE ] && PIDS2=`cat $PIDSPATH/$PIDFILE` + + for i in $PIDS; do + if [ "$i" = "$PIDS2" ]; then + return 1 + fi + done + + return 0 +} + +action="$1" +case "$1" in + start) + start + ;; + stop) + stop + ;; + restart|force-reload) + stop + # if [ $rc -ne 0 ]; then + # script_exit + # fi + sleep 1 + start + ;; + reload) + $1 + ;; + status) + status + ;; + configtest) + $1 + ;; + quietupgrade) + $1 + ;; + terminate) + $1 + ;; + destroy) + $1 + ;; + *) + FULLPATH=/etc/init.d/$PS + echo "Usage: $FULLPATH {start|stop|restart|force-reload|reload|status|configtest|quietupgrade|terminate|destroy}" + echo " The 'destroy' command should only be used as a last resort." + exit 3 + ;; +esac + +exit $rc diff --git a/Nginx/nginx-2-config.md b/Nginx/nginx-2-config.md index 1a7383b..93a3621 100644 --- a/Nginx/nginx-2-config.md +++ b/Nginx/nginx-2-config.md @@ -71,8 +71,9 @@ -–with-libatomic= 指向libatomic_ops安装目录 -–with-openssl= 指向openssl安装目录 -–with-openssl-opt 在编译时为openssl设置附加参数 --–with-debug 启用debug日志 +--with-debug 启用debug日志 --with-stream_ssl_module +--add-module=/home/tinywan/nchan-1.1.3/ # make && make install # mkdir -pv /var/tmp/nginx/client ``` diff --git a/Nginx/nginx-base-config.md b/Nginx/nginx-base-config.md new file mode 100644 index 0000000..5700a2a --- /dev/null +++ b/Nginx/nginx-base-config.md @@ -0,0 +1,366 @@ +#### [参考地址](https://mp.weixin.qq.com/s/Crj2Xo8-EJpbq40kXronug) +#### Nginx 配置文件 nginx.conf 详解 + +```javascript +#定义Nginx运行的用户和用户组 +user www www; + +#nginx进程数,建议设置为等于CPU总核心数。 +worker_processes 8; + +#全局错误日志定义类型,[ debug | info | notice | warn | error | crit ] +error_log /var/log/nginx/error.log info; + +#进程文件 +pid /var/run/nginx.pid; + +#一个nginx进程打开的最多文件描述符数目,理论值应该是最多打开文件数(系统的值ulimit -n)与nginx进程数相除,但是nginx分配请求并不均匀,所以建议与ulimit -n的值保持一致。 +worker_rlimit_nofile 65535; + +#工作模式与连接数上限 +events +{ +#参考事件模型,use [ kqueue | rtsig | epoll | /dev/poll | select | poll ]; epoll模型是Linux 2.6以上版本内核中的高性能网络I/O模型,如果跑在FreeBSD上面,就用kqueue模型。 +use epoll; +#单个进程最大连接数(最大连接数=连接数*进程数) +worker_connections 65535; +} + +#设定http服务器 +http +{ +include mime.types; #文件扩展名与文件类型映射表 +default_type application/octet-stream; #默认文件类型 +#charset utf-8; #默认编码 +server_names_hash_bucket_size 128; #服务器名字的hash表大小 +client_header_buffer_size 32k; #上传文件大小限制 +large_client_header_buffers 4 64k; #设定请求缓 +client_max_body_size 8m; #设定请求缓 +sendfile on; #开启高效文件传输模式,sendfile指令指定nginx是否调用sendfile函数来输出文件,对于普通应用设为 on,如果用来进行下载等应用磁盘IO重负载应用,可设置为off,以平衡磁盘与网络I/O处理速度,降低系统的负载。注意:如果图片显示不正常把这个改成off。 +autoindex on; #开启目录列表访问,合适下载服务器,默认关闭。 +tcp_nopush on; #防止网络阻塞 +tcp_nodelay on; #防止网络阻塞 +keepalive_timeout 120; #长连接超时时间,单位是秒 + +#FastCGI相关参数是为了改善网站的性能:减少资源占用,提高访问速度。下面参数看字面意思都能理解。 +fastcgi_connect_timeout 300; +fastcgi_send_timeout 300; +fastcgi_read_timeout 300; +fastcgi_buffer_size 64k; +fastcgi_buffers 4 64k; +fastcgi_busy_buffers_size 128k; +fastcgi_temp_file_write_size 128k; + +#gzip模块设置 +gzip on; #开启gzip压缩输出 +gzip_min_length 1k; #最小压缩文件大小 +gzip_buffers 4 16k; #压缩缓冲区 +gzip_http_version 1.0; #压缩版本(默认1.1,前端如果是squid2.5请使用1.0)开始压缩的http协议版本(可以不设置,目前几乎全是1.1协议) +gzip_comp_level 2; #推荐6压缩级别(级别越高,压的越小,越浪费CPU计算资源) +gzip_types text/plain application/x-javascript text/css application/xml; +#压缩类型,默认就已经包含text/html,所以下面就不用再写了,写上去也不会有问题,但是会有一个warn。 +gzip_vary on; # 是否传输gzip压缩标志 +#limit_zone crawler $binary_remote_addr 10m; #开启限制IP连接数的时候需要使用 + +upstream blog.ha97.com { +#upstream的负载均衡,weight是权重,可以根据机器配置定义权重。weigth参数表示权值,权值越高被分配到的几率越大。 +server 192.168.80.121:80 weight=3; +server 192.168.80.122:80 weight=2; +server 192.168.80.123:80 weight=3; +} + +#虚拟主机的配置 +server +{ + #监听端口 + listen 80; + #域名可以有多个,用空格隔开 + server_name www.ha97.com ha97.com; + index index.html index.htm index.php; + root /data/www/ha97; + location ~ .*\.(php|php5)?$ + { + fastcgi_pass 127.0.0.1:9000; + fastcgi_index index.php; + include fastcgi.conf; + } + #图片缓存时间设置 + location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ + { + expires 10d; + } + #JS和CSS缓存时间设置 + location ~ .*\.(js|css)?$ + { + expires 1h; + } + #日志格式设定 + log_format access '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" $http_x_forwarded_for'; + #定义本虚拟主机的访问日志 + access_log /var/log/nginx/ha97access.log access; + + #对 "/" 启用反向代理 + location / { + proxy_pass http://127.0.0.1:88; + proxy_redirect off; + proxy_set_header X-Real-IP $remote_addr; + #后端的Web服务器可以通过X-Forwarded-For获取用户真实IP + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + #以下是一些反向代理的配置,可选。 + proxy_set_header Host $host; + client_max_body_size 10m; #允许客户端请求的最大单文件字节数 + client_body_buffer_size 128k; #缓冲区代理缓冲用户端请求的最大字节数, + proxy_connect_timeout 90; #nginx跟后端服务器连接超时时间(代理连接超时) + proxy_send_timeout 90; #后端服务器数据回传时间(代理发送超时) + proxy_read_timeout 90; #连接成功后,后端服务器响应时间(代理接收超时) + proxy_buffer_size 4k; #设置代理服务器(nginx)保存用户头信息的缓冲区大小 + proxy_buffers 4 32k; #proxy_buffers缓冲区,网页平均在32k以下的设置 + proxy_busy_buffers_size 64k; #高负荷下缓冲大小(proxy_buffers*2) + proxy_temp_file_write_size 64k; + #设定缓存文件夹大小,大于这个值,将从upstream服务器传 + } + + #设定查看Nginx状态的地址 + location /NginxStatus { + stub_status on; + access_log on; + auth_basic "NginxStatus"; + auth_basic_user_file conf/htpasswd; + #htpasswd文件的内容可以用apache提供的htpasswd工具来产生。 + } + + #本地动静分离反向代理配置 + #所有jsp的页面均交由tomcat或resin处理 + location ~ .(jsp|jspx|do)?$ { + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_pass http://127.0.0.1:8080; + } + #所有静态文件由nginx直接读取不经过tomcat或resin + location ~ .*.(htm|html|gif|jpg|jpeg|png|bmp|swf|ioc|rar|zip|txt|flv|mid|doc|ppt|pdf|xls|mp3|wma)$ + { expires 15d; } + location ~ .*.(js|css)?$ + { expires 1h; } +} +} + +``` +#### 常用配置案例 + +```html +user nginx nginx; +worker_processes 1; + +error_log /var/log/nginx/error.log; +#error_log logs/error.log notice; +#error_log logs/error.log info; + +pid /var/run/nginx/nginx.pid; + +events { + worker_connections 1024; +} + + +http { + include mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + #tcp_nopush on; + + #keepalive_timeout 0; + keepalive_timeout 65; + + #隐藏Nginx版本信息,禁止网站目录浏览 + server_tokens off; + autoindex off; + #当FastCGI后端服务器处理请求给出http响应码为4xx和5xx时,就转发给nginx + fastcgi_intercept_errors on; + + #关于fastcgi的配置 + fastcgi_connect_timeout 300; + fastcgi_send_timeout 300; + fastcgi_read_timeout 300; + fastcgi_buffer_size 64k; + fastcgi_buffers 4 64k; + fastcgi_busy_buffers_size 128k; + fastcgi_temp_file_write_size 128k; + + #支持gzip压缩 + gzip on; + gzip_min_length 1k; + gzip_buffers 16 64k; + gzip_http_version 1.1; + gzip_comp_level 6; + gzip_types text/plain application/x-javascript text/css application/javascript text/javascript image/jpeg image/gif image/png application/xml application/json; + gzip_vary on; + gzip_disable "MSIE [1-6].(?!.*SV1)"; + + # + # 重定向所有带www请求到非www的请求 + # + server { + listen *:80; + listen *:443 ssl spdy; + server_name www.typecodes.com; + # ssl证书配置见文章 https://typecodes.com/web/lnmppositivessl.html + ssl_certificate /etc/nginx/ssl/typecodes.crt; + # ssl密钥文件见文章 https://typecodes.com/web/lnmppositivessl.html + ssl_certificate_key /etc/nginx/ssl/typecodes.key; + # 不产生日志 + access_log off; + + # 访问favicon.ico和robots.txt不跳转(把这两个文件存放在上级目录html中) + location ~* ^/(favicon.ico|robots.txt)$ { + root html; + expires max; + log_not_found off; + break; + } + + location / { + return 301 https://typecodes.com$request_uri; + } + } + + # + # 将所有http请求重定向到https + # + server { + listen *:80; + server_name typecodes.com; + # 不产生日志 + access_log off; + + # 访问favicon.ico和robots.txt不跳转(把这两个文件存放在上级目录html中) + location ~* ^/(favicon.ico|robots.txt)$ { + root html; + expires max; + log_not_found off; + break; + } + + location / { + return 301 https://typecodes.com$request_uri; + } + } + + # + # HTTPS server + # + server { + listen *:443 ssl spdy; + server_name typecodes.com; + + # ssl证书配置见文章 https://typecodes.com/web/lnmppositivessl.html + ssl_certificate /etc/nginx/ssl/typecodes.crt; + # ssl密钥文件见文章 https://typecodes.com/web/lnmppositivessl.html + ssl_certificate_key /etc/nginx/ssl/typecodes.key; + ssl_session_cache shared:SSL:20m; + ssl_session_timeout 10m; + ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA:!CAMELLIA; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #enables TLSv1, but not SSLv2, SSLv3 which is weak and should no longer be used. + ssl_prefer_server_ciphers on; + # 开启spdy功能 + add_header Alternate-Protocol 443:npn-spdy/3.1; + # 严格的https访问 + add_header Strict-Transport-Security "max-age=31536000; includeSubdomains;"; + + #设置网站根目录 + root /usr/share/nginx/html/typecodes; + index index.php index.html; + + charset utf-8; + + #access_log /var/log/nginx/log/host.access.log main; + + #设置css/javascript/图片等静态资源的缓存时间 + location ~ .*\.(css|js|ico|png|gif|jpg|json|mp3|mp4|flv|swf)(.*) { + expires 60d; + } + + # include /etc/nginx/default.d/*.conf; + # 设置typecho博客的config文章不被访问,保证安全 + location = /config.inc.php{ + deny all; + } + + # keep the uploads directory safe by excluding php, php5, html file accessing. Applying to wordpress and typecho. + # location ~ .*/uploads/.*\.(php|php5|html)$ { + # deny all; + # } + + # 设置wordpress和typecho博客中,插件目录无法直接访问php或者html文件 + location ~ .*/plugins/.*\.(php|php5|html)$ { + deny all; + } + + #Rewrite的伪静态(针对wordpress/typecho),url地址去掉index.php + location / { + if (-f $request_filename/index.html){ + rewrite (.*) $1/index.html break; + } + if (-f $request_filename/index.php){ + rewrite (.*) $1/index.php; + } + if (!-f $request_filename){ + rewrite (.*) /index.php; + } + } + + #访问favicon.ico时不产生日志 + location = /favicon.ico { + access_log off; + } + + #设置40系列错误的应答文件为40x.html + error_page 400 401 402 403 404 /40x.html; + location = /40x.html { + root html; + index index.html index.htm; + } + + #设置50系列错误的应答文件为50x.html + # + error_page 500 501 502 503 504 /50x.html; + location = /50x.html { + root html; + index index.html index.htm; + } + + # proxy the PHP scripts to Apache listening on 127.0.0.1:80 + # + #location ~ \.php$ { + # proxy_pass http://127.0.0.1; + #} + + # 设置Nginx和php通信机制为tcp的socket模式,而不是直接监听9000端口 + location ~ .*\.php(\/.*)*$ { + fastcgi_split_path_info ^(.+\.php)(/.+)$; + #fastcgi_pass 127.0.0.1:9000; + # the better form of fastcgi_pass than before + fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; + } + + # deny access to .htaccess files, if Apache's document root + # concurs with nginx's one + # + #location ~ /\.ht { + # deny all; + #} + } +} +``` \ No newline at end of file diff --git a/Nginx/nginx-basic.md b/Nginx/nginx-basic.md new file mode 100644 index 0000000..b23d86a --- /dev/null +++ b/Nginx/nginx-basic.md @@ -0,0 +1,48 @@ + +## Nginx 基础知识 +--- ++ [NGINX 所有 Modules](https://www.nginx.com/resources/wiki/modules/) +#### agentzh的Nginx教程(版本2016.07.21) ++ [agentzh的Nginx教程地址](https://openresty.org/download/agentzh-nginx-tutorials-zhcn.html) ++ Nginx 变量漫谈(一) + + Nginx 变量的值只有一种类型,那就是字符串 + + Nginx “变量插值” + ```bash + location /test { + set $first "hello "; + echo "${first}world"; + } + ``` + + set 指令(以及前面提到的 geo 指令)不仅有赋值的功能,它还有创建 Nginx 变量的副作用,即当作为赋值对象的变量尚不存在时 + + Nginx 变量一旦创建,其变量名的可见范围就是整个 Nginx 配置,甚至可以跨越不同虚拟主机的 server 配置块 + + Nginx 变量的生命期是不可能跨越请求边界的 ++ Nginx 变量漫谈(二) + + 跳转 + + 内部跳转:就是在处理请求的过程中,于服务器内部,从一个 location 跳转到另一个 location 的过程。 + + 外部跳转: HTTP 状态码 301 和 302 所进行的“外部跳转” + + 标准 ngx_rewrite 模块的 rewrite 配置指令其实也可以发起“内部跳转” + + Nginx 核心和各个 Nginx 模块提供的“预定义变量” + + Nginx 会在匹配参数名之前,自动把原始请求中的参数名调整为全部小写的形式 + + 如果你尝试改写另外一些只读的内建变量,比如 $arg_XXX 变量,在某些 Nginx 的版本中甚至可能导致进程崩溃。 ++ Nginx 变量漫谈(四) + + map 指令:用于定义两个 Nginx 变量之间的映射关系,或者说是函数关系 + + map 指令只能在 http 块中使用 + + map 配置指令的工作原理是为用户变量注册 “取处理程序”,并且实际的映射计算是在“取处理程序”中完成的,而“取处理程序”只有在该用户变量被实际读取时才会执行(当然,因为缓存的存在,只在请求生命期中的第一次读取中才被执行),所以对于那些根本没有用到相关变量的请求来说,就根本不会执行任何的无用计算。 ++ Nginx 变量漫谈(四) ++ [Nginx的11个Phases](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/nginx-phases.md) ++ [Nginx 陷阱和常见错误](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/nginx-1-config.md) ++ [Nginx 高并发系统内核优化](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/nginx-parameter-config.md) ++ [nginx 并发数问题思考:worker_connections,worker_processes与 max clients](http://liuqunying.blog.51cto.com/3984207/1420556?utm_source=tuicool) + + 从用户的角度,http 1.1协议下,由于浏览器默认使用两个并发连接,因此计算方法: + 1. nginx作为http服务器的时候: + `max_clients = worker_processes * worker_connections/2` + 1. nginx作为反向代理服务器的时候: + `max_clients = worker_processes * worker_connections/4` + + 从一般建立连接的角度,客户并发连接为1: + 1. nginx作为http服务器的时候: + `max_clients = worker_processes * worker_connections` + 1. nginx作为反向代理服务器的时候: + `max_clients = worker_processes * worker_connections/2` + + nginx做反向代理时,和客户端之间保持一个连接,和后端服务器保持一个连接 + + clients与用户数 + 同一时间的clients(客户端数)和用户数还是有区别的,当一个用户请求发送一个连接时这两个是相等的,但是当一个用户默认发送多个连接请求的时候,clients数就是用户数*默认发送的连接并发数了。 diff --git a/Nginx/nginx-config-bandwidth.md b/Nginx/nginx-config-bandwidth.md new file mode 100644 index 0000000..d109bdb --- /dev/null +++ b/Nginx/nginx-config-bandwidth.md @@ -0,0 +1,112 @@ +## 如何改进 NGINX 配置文件节省带宽? + +#### 为HTML,CSS和JavaScript文件启用Gzip压缩 + +如您所知,用于在现代网站上构建页面的HTML,CSS和JavaScript文件可能非常庞大。在大多数情况下,Web服务器可以即时压缩这些和其他文本文件,以节省网络带宽。 + +查看Web服务器是否正在压缩文件的一种方法是使用浏览器的开发人员工具。对于许多浏览器,您可以使用F12键访问这些工具,并且相关信息位于“网络”选项卡上。 + +![](/Images/screenshot_1588728211136.png) + +默认情况下,NGINX中禁用压缩,但是根据您的安装或Linux发行版,某些设置可能会在默认的nginx.conf文件中启用。在这里,我们在NGINX配置文件中启用gzip压缩: +``` +gzip on; +gzip_types application/xml application/json text/css text/javascript application/javascript; +gzip_vary on; +gzip_comp_level 6; +gzip_min_length 500; +``` + +#### 设置缓存头 +当浏览器检索网页的文件时,它会将副本保留在本地磁盘缓存中,这样,当您再次访问该页面时,它不必从服务器重新获取文件。每个浏览器都使用自己的逻辑来决定何时使用文件的本地副本以及何时在服务器上更改了文件时再次获取它。但是,作为网站所有者,您可以在发送的HTTP响应中设置缓存控制和过期标头,以提高浏览器的缓存行为的效率。从长远来看,您会收到很多不必要的HTTP请求。 + +首先,您可以为字体和图像设置较长的缓存过期时间,这些字体和图像可能不会经常更改(即使更改,它们通常也会获得新的文件名)。在以下示例中,我们指示客户端浏览器将字体和图像在本地缓存中保留一个月: + +``` +location ~* \.(?:jpg|jpeg|gif|png|ico|woff2)$ { + expires 1M; + add_header Cache-Control "public"; +} +``` +#### 启用HTTP / 2协议支持 +HTTP / 2是用于服务网页的下一代协议,旨在更好地利用网络和主机服务器。根据Google文档,它可以更快地加载页面: + +生成的协议对网络更友好,因为与HTTP / 1.x相比,使用的TCP连接更少。这意味着与其他流量的竞争减少,连接寿命更长,从而可以更好地利用可用网络容量。 + +NGINX 1.9.5和更高版本(以及NGINX Plus R7和更高版本)支持HTTP / 2协议,您所需要做的就是启用它。为此,请在您的NGINX配置文件中http2的listen指令中包含参数: + +``` +listen 443 ssl http2; +``` +请注意,在大多数情况下,您还需要启用TLS才能使用HTTP / 2。 + +您可以通过[HTTP2.Pro](https://http2.pro/) 服务验证您(或任何站点)是否支持HTTP / 2 : + + +#### 优化记录 +让自己喝一杯自己喜欢的饮料,舒适地坐着,然后思考:您上次查看访问日志文件是什么时候?上周,上个月,从来没有?即使将其用于站点的日常监视,您也可能只关注错误(400和500状态代码等),而不关注成功的请求。 + +通过减少或消除不必要的日志记录,可以节省服务器上的磁盘存储,CPU和I / O操作。这不仅使您的服务器更快一点-如果将您部署在云环境中,则释放的I / O吞吐量和CPU周期可能为同一虚拟机上的其他虚拟机或应用程序节省生命。 + +有几种不同的方法可以减少和优化日志记录。在这里,我们重点介绍三个。 + + +##### 方法1:禁用页面资源请求的记录 + +如果您不需要记录检索普通页面资源(例如图像,JavaScript文件和CSS文件)的请求,则这是一种快速简便的解决方案。您需要做的就是创建一个location与这些文件类型匹配的新块,并禁用其中的日志记录。(您也可以将此access_log指令添加到我们设置标头的上方的location块中。)Cache-Control +``` +location ~* \.(?:jpg|jpeg|gif|png|ico|woff2|js|css)$ { + access_log off; +} +``` +##### 方法2:禁用成功请求的日志记录 + +这是一种更强大的方法,因为它会丢弃带有或响应代码的查询,仅记录错误。它比方法1稍微复杂一点,因为它取决于如何配置NGINX日志记录。在我们的示例中,我们使用Ubuntu Server发行版中包含的标准**nginx.conf**,因此,无论虚拟主机如何,所有请求都记录到 **/var/log/nginx/access.log中**。`2xx``3xx` + +使用[官方NGINX文档中](https://nginx.org/en/docs/http/ngx_http_log_module.html#access_log)的示例,让我们打开条件日志记录。创建一个变量`$loggable`,并将其设置为,`0`以使用和代码进行请求,否则设置为 。然后在指令中将此变量作为条件引用。`2xx``3xx``1``access_log` + +这是 **/etc/nginx/nginx.conf** 中`http`上下文中的原始指令: +``` +access_log /var/log/nginx/access.log; +``` +添加一个[`map`](https://nginx.org/en/docs/http/ngx_http_map_module.html#map)块并从`access_log`指令中引用它: + +``` +map $status $loggable { + ~^[23] 0; + default 1; +} + +access_log /var/log/nginx/access.log combined if=$loggable; +``` +请注意,尽管这`combined`是默认的日志格式,但是在包含`if`参数时,您需要明确指定它。 + +##### 方法3:使用缓冲最小化I / O操作 + +即使您要记录所有请求,也可以通过打开访问日志缓冲来最大程度地减少I / O操作。使用此指令,NGINX会等待将日志数据写入磁盘,直到填满512 KB缓冲区或自上次刷新以来经过1分钟(以先发生者为准)。 + +``` +access_log /var/log/nginx/access.log combined buffer=512k flush=1m; +``` + +### 限制特定URL的带宽 + +如果服务器提供较大的文件(或较小但非常受欢迎的文件,例如表单或报表),则设置客户端下载文件的最大速度可能很有用。如果您的站点已经承受了很高的网络负载,则限制下载速度会留下更多带宽,以使应用程序的关键部分保持响应速度。这是硬件制造商使用的非常流行的解决方案–您可能需要等待更长的时间才能为打印机下载3 GB的驱动程序,但是同时有成千上万的其他人下载您仍然可以下载。😉 + +使用[`limit_rate`](https://nginx.org/en/docs/http/ngx_http_core_module.html#limit_rate)指令限制特定URL的带宽。在这里,我们将 **/ download** 下每个文件的传输速率限制为每秒50 KB。 +``` +location /download/ { + limit_rate 50k; +} +``` +您可能还希望仅对较大的文件进行速率限制,这可以使用[`limit_rate_after`](https://nginx.org/en/docs/http/ngx_http_core_module.html#limit_rate_after)指令进行。在此示例中,每个文件(来自任何目录)的前500 KB都不受速度限制地进行传输,之后的所有内容均以50 KB / s为上限。这样可以加快网站关键部分的交付速度,同时降低其他部分的速度。 + +``` +location / { + limit_rate_after 500k; + limit_rate 50k; +} +``` +请注意,速率限制适用于浏览器和NGINX之间的单个HTTP连接,因此请不要阻止用户使用下载管理器来解决速率限制。 + +最后,您还可以限制到服务器的并发连接数或请求速率。有关详细信息,请参见我们的[文档](https://docs.nginx.com/nginx/admin-guide/security-controls/controlling-access-proxied-http/)。 \ No newline at end of file diff --git a/Nginx/nginx-high-basic.md b/Nginx/nginx-high-basic.md new file mode 100644 index 0000000..51025c3 --- /dev/null +++ b/Nginx/nginx-high-basic.md @@ -0,0 +1,277 @@ +## Nginx高性能WEB服务器详解 +#### 第一章 初探 ++ [Nginx 编译安装以及参数详解](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/nginx-2-config.md) ++ NGINX变量详解 + - [x] [nginx变量使用方法详解笔记(1)](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx-Develop/notes-1.md) + - [x] [nginx变量使用方法详解笔记(2)](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx-Develop/notes-2.md) + - [x] [nginx变量使用方法详解笔记(3)](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/nginx-2-config.md) ++ Nginx指令执行顺序 + - [x] [Nginx指令执行命令(01)](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx-Develop/command-order-01.md) +#### 第二章 安装部署 ++ 启动错误:`Nginx [emerg]: bind() to 0.0.0.0:80 failed (98: Address already in use)`,执行:`sudo fuser -k 80/tcp` ++ [基于域名、IP的虚拟主机配置](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/Nginx-Web/Nginx-2-4-all-config.md) ++ [完整、标准配置实际示列](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/Nginx-Web/Nginx-2-4-basic-config.md) ++ [日志文件配置与切割](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/Nginx-Web/Nginx-2-4-log-cut.md) ++ alias 和 root 在location 下的应用 + - 通过alias 实现别名功能 + ``` + location /live { + alias /home/tinywan/HLS/; + } + ``` + - curl 请求结果 + ``` + tinywan@tinywan:~/HLS$ cat index.html + alias /home/tinywan/HLS/index.html + tinywan@tinywan:~/HLS$ curl http://127.0.0.1/live/index.html + alias /home/tinywan/HLS/index.html + ``` + - 结论: + 1. cul 请求 `/live/index.html`,那么Nginx将会在服务器上查找`/home/tinywan/HLS/index.html` 文件 + 1. 请求的`url` 中的`location`后面的部分会被追加到`alias `指定的目录后面,而`location`后面的`/live`路径将会别自动抛弃 + - 类似案例[2]: + - config配置信息 + ``` + location ~ ^/live/(.*)$ { + alias /home/tinywan/HLS/$1; + } + ``` + - curl 请求结果 + ``` + tinywan@tinywan:~/HLS$ pwd + /home/tinywan/HLS + tinywan@tinywan:~/HLS$ cat txt.txt + txt file + tinywan@tinywan:~/HLS$ curl http://127.0.0.1/live/txt.txt + txt file + ``` + - 如果url请求`/live/txt.txt`那么Nginx将会在服务器上查找`/home/tinywan/HLS/txt.txt` 文件 + - **与root 功能的差别**: + - config配置信息,注意:一下的`alias` 换成 `root ` + ``` + location ~ ^/live/(.*)$ { + root /home/tinywan/HLS/$1; + } + ``` + - curl 请求结果 + ``` + tinywan@tinywan:~/HLS$ curl http://127.0.0.1/live/txt.txt + + 404 Not Found + +

404 Not Found

+
openresty/1.11.2.1
+ + + ``` + - 日志文件信息(打开Nginx的rewrite日志:rewrite_log on;): + ``` + /home/tinywan/HLS/txt.txt/live/txt.txt + ``` + - **二者的区别** + 1. `alias` 指定的目录是当前目录 + 1. `root` 指定的是根目录 + 1. 一般建议的`location /`中通过`root`命令配置目录,其他目录匹配的位置使用`alias`命令 + - 案例[3]: + - config配置信息 + ``` + location ~ ^/live/(\w+)/(.*) { + alias /home/tinywan/HLS/live/$1/$2; + } + ``` + - curl 请求结果 + ``` + tinywan@tinywan:~/HLS/live/stream123$ pwd + /home/tinywan/HLS/live/stream123 + tinywan@tinywan:~/HLS/live/stream123$ cat index.m3u8 + 12312312312 + tinywan@tinywan:~/HLS/live/stream123$ curl "http://127.0.0.1/live/stream123/index.m3u8?token=1234&api=009132" + 12312312312 + ``` +####
第三章 架构初探 +- [ ] 测试一 +#### 第四章 高级配置 ++ 基本语法:location [=|~|~*|^~] /uri/ { … } + 1. `= `:严格匹配。如果这个查询匹配,那么将停止搜索并立即处理此请求。 + 2. `~ `:为区分大小写匹配(可用正则表达式) + 3. `!~ `:为区分大小写不匹配 + 4. `!~*`:为不区分大小写不匹配 + 5. ` ^~ `:如果把这个前缀用于一个常规字符串,那么告诉nginx 如果路径匹配那么不测试正则表达式 ++ [Perl 正则表达式参考](http://www.runoob.com/perl/perl-regular-expressions.html) ++ 正则中需要转义的特殊字符小结 + - [1] ` $` 匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 ‘\n' 或 ‘\r'。要匹配 $ 字符本身,请使用 \$。 + - [2] ` ( )` 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 和。 + - [3] ` * ` 匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 \*。 + - [4] ` +` 匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 \+。 + - [5] ` . ` 匹配除换行符 \n之外的任何单字符。要匹配 .,请使用 \。 + - [6] ` [ ]` 标记一个中括号表达式的开始。要匹配 [,请使用 \[。 + - [7] ` ? ` 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用 \?。 + - [8] ` \ ` 将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如, ‘n' 匹配字符 ‘n'。'\n' 匹配换行符。序列 ‘\\' 匹配 “\”,而 ‘\(' 则匹配 “(”。 + - [9] ` ^ ` 匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合。要匹配 ^ 字符本身,请使用 \^。 + - [10] ` { }` 标记限定符表达式的开始。要匹配 {,请使用 \{。 + - [11] ` | ` 指明两项之间的一个选择。要匹配 |,请使用 \|。 + ++ 正则表达式 (Regular expression) 匹配location + - [1] `location ~* \.(gif|jpg|jpeg)$ { }`:匹配所有以 gif,jpg或jpeg 结尾的请求 + - [2] `location ~ /documents/Abc { }`:匹配任何以 /documents/ 开头的地址,匹配符合以后,还要继续往下搜索 + - [3] **目录匹配:** + 1. 可以匹配静态文件目录`(static/lib)` + 2. HLS直播目录`(/home/HLS/stream123/index.m3u8)` + 3. HLS/MP4/FLV点播视频目录`(/home/HLS/stream123.m3u8)` + 4. 匹配URL地址:`http://127.0.0.1/live/stream123/index.m3u8` + 5. nginx.conf 配置信息 + ``` + # 匹配任何以/live/ 开头的任何查询并且停止搜索。任何正则表达式将不会被测试 + location ^~ /live/ { + root /home/tinywan/HLS/; + } + # 以上匹配成功后的组合:/home/tinywan/HLS/live/.... + ``` ++ 后缀匹配 + 1. 匹配任何后缀文件名`gif|jpg|jpeg|png|css|js|ico|m3u8|ts` 结尾的请求 + 2. TS 文件匹配`http://127.0.0.1/live/stream123/11.ts` + 3. M3U8 文件匹配`http://127.0.0.1/live/stream123/index.m3u8` + 4. 匹配URL地址:`http://127.0.0.1/hls/123.m3u8` + 5. nginx.conf 配置信息 + ``` + location ~* \.(gif|jpg|jpeg|png|css|js|ico|m3u8|ts)$ { + root /home/tinywan/HLS/; + } + ``` ++ HSL直播目录匹配实际案例(请测试上线) + 1. 可以后缀文件名:`http://127.0.0.1/live/stream123/index.m3u8` + ``` + location ^~ /live/ { + root /home/tinywan/HLS/; + } + ``` + ++ [nginx配置location总结及rewrite规则写法](http://seanlook.com/2015/05/17/nginx-location-rewrite/) +#### 第五章 Gzip压缩 ++ 测试一 +#### 第六章 Rewrite 功能 ++ Rewrite 常用全局变量 + + 请求案例: `curl -G -d "name=Tinywan&age=24" http://127.0.0.1/rewrite_var/1192/index.m3u8` + + 接受结果: + + | 变量 | 值 |描述 | + | --------- | ----------- |----------- | + | $args | name=Tinywan&age=24 |存放URL 请求的指令 | + | $content_length | 0 | 请求头中的Content-length字段| + | $content_type | 0 |请求头中的Content-Type字段 | + | $document_root | /opt/openresty/nginx/html | 当前请求在root指令中指定的值 | + | $document_uri | /rewrite_var/1192/index.m3u8 | 与$uri相同 | + | $host | 127.0.0.1 |请求主机头字段,否则为服务器名称 | + | $http_user_agent | curl/7.47.0 | 客户端agent信息| + | $http_cookie | 0 | COOKIE变量的值| + | $limit_rate | 0 | 限制连接速率| + | $request_body_file | null | 客户端请求主体信息的临时文件名| + | $request_method | GET | 客户端请求的动作,通常为GET或POST | + | $remote_addr | 127.0.0.1 |客户端的IP地址 | + | $remote_port | 33516 |客户端端口| + | $remote_user | 0 | 已经经过Auth Basic Module验证的用户名| + | $request_filename | /opt/openresty/nginx/html/rewrite_var/1192/index.m3u8 |当前请求的文件路径 | + | $request_uri | /rewrite_var/1192/index.m3u8?name=Tinywan&age=24 |包含请求参数的原始URI,不包含主机名 | + | $query_string | name=Tinywan&age=24 | 与$args相同| + | $scheme | http |HTTP方法(如http,https | + | $server_protocol | HTTP/1.1 |请求使用的协议,通常是HTTP/1.0或HTTP/1.1 | + | $server_addr | 127.0.0.1 |服务器地址 | + | $server_name | localhost | 服务器名称| + | $server_port | 80 |请求到达服务器的端口号 | + | $uri | /rewrite_var/1192/index.m3u8 | 不带请求参数的当前URI| + | $binary_remote_addr | 乱码 | 二进制格式的客户端地址| + + + uri 介绍 **(Nginx中的URI是相对的URI)** + + URL:`https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/config.md` + + 绝对URI:`https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/config.md` + + 相对URI:`/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/config.md` + ![Markdown](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Images/URI-URL-Image.jpg) + ++ Rewrite 正则匹配` uri `参数接收 + 1. 请求案例:`curl http://192.168.18.143/live/tinywan123/index.m3u8` + 2. Nginx.conf配置文件 + ```Lua + location ~* ^/live/(\w+)/(\D+)\.(m3u8|ts)$ { + set $num $2; + set $arg1 $1; + echo "args === ${arg1}"; + echo "1==$1 2==$2 3==$3"; + echo "Total_numbser :: $num"; + echo "URI $uri"; + } + + ``` + 3. 输出结果 + ``` + args === tinywan123 + $1==tinywan123 $2==index $3==m3u8 + Total_numbser :: index + URI /live/tinywan123/index.m3u8 + Total_numbser :: + ``` + 4. $1为正则匹配多个英文字母或数字的字符串 `(\w+)` + $2 为正则匹配多个非数字 `(\D+)` + $3 为正则匹配的第一个值 `(m3u8|ts)` + `.` 需要用转义字符转义`\.` +## 第七章 代理服务 ++ [正向代理和反向代理的概念](#title) ++ [正向代理服务](#title) ++ [反向代理的服务](#title) ++ [Nginx日志服务](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/Nginx-Web/Nginx-2-Log.md) ++ 负载均衡 ++ HTTP负载均衡 + - [x] [简单的负载平衡](http://nginx.org/en/docs/http/ngx_http_core_module.html?&_ga=1.179030369.49817296.1480411319#http) + - [x] [简单的负载平衡](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/Nginx-Web/Nginx-7-Proxy-1.md) + - [x] [负载均衡五个配置实例](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/Nginx-Web/Nginx-7-Proxy.md) + - [x] [Openresty-Lua动态修改upstream后端服务](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/Nginx-Web/openresty-nginx-lua-Proxy.md) ++ TCP负载均衡 + - [x] [Module ngx_stream_core_module](http://nginx.org/en/docs/stream/ngx_stream_core_module.html#stream) + - [x] [负载均衡](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/Nginx-Web/Nginx-8-tcp-Proxy.md) ++ proxy_pass 代理的URL总结 + + 在nginx中配置proxy_pass时,当在后面的url加上了/,相当于是绝对根路径,则nginx不会把location中匹配的路径部分代理走;如果没有/,则会把匹配的路径部分也给代理走。 + + 将url中以/wap/开头的请求转发到后台对应的某台server上,注意最后的?$args,表明把原始url最后的get参数也给代理到后台 + ```bash + location ~* /wap/(\d+)/(.+) + { + proxy_pass http://mx$1.test.com:6601/$2?$args; + } + ``` + + 第一种配置,访问:`http://127.0.0.1/proxy/index.html` 会被代理到:`http://127.0.0.1:8000/index.html` + ```bash + location /proxy/ { + proxy_pass http://127.0.0.1:8000/; + } + ``` + + 第二种配置,访问:`http://127.0.0.1/proxy/index.html` 会被代理到:`http://127.0.0.1:8000/proxy/index.html` + ```bash + location /proxy/ { + proxy_pass http://127.0.0.1:8000; + } + ``` + + 第三种配置,访问:`http://127.0.0.1/proxy/index.html` 会被代理到:`http://127.0.0.1:8000/video/index.html` + ```bash + location /proxy/ { + proxy_pass http://127.0.0.1:8000/video/; + } + ``` + + 第四种配置,访问:`http://127.0.0.1/proxy/index.html` 会被代理到:`http://127.0.0.1:8000/videoindex.html` + ```bash + location /proxy/ { + proxy_pass http://127.0.0.1:8000/video; + } + ``` ++ location 直接访问: + + 以下配置,当访问:`http://127.0.0.1:8000/proxy/index.html` 会被匹配到:`/usr/local/nginx/html/proxy/index.html` + ```bash + location /proxy/ { + root /usr/local/nginx/html; + index index.html index.htm; + } + ``` + +## 第八章 缓存机制 ++ [Proxy Cache 缓存机制](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/Nginx-Web/Nginx-8-proxy_cache.md) +## 第九章 Nginx初探1 ++ 测试一 +## 第十章 Nginx初探1 ++ 测试一 diff --git a/Nginx/nginx-install.md b/Nginx/nginx-install.md new file mode 100644 index 0000000..ca11e66 --- /dev/null +++ b/Nginx/nginx-install.md @@ -0,0 +1,212 @@ +## 在Ubuntu 16.04中如何从源代码编译Nginx ++ NGINX可用作HTTP / HTTPS服务器,反向代理服务器,邮件代理服务器,负载均衡器,TLS终结器或缓存服务器。它是相当模块化的设计。它具有由社区创建的本机模块和第三方模块。以C编程语言编写,它是一个非常快速和轻便的软件。 +#### 从源头构建NGINX的要求,强制性要求: ++ OpenSSL库版本介于1.0.2 - 1.1.0之间 ++ Zlib库版本介于1.1.3 - 1.2.11之间 ++ PCRE库版本在4.4 - 8.40之间 ++ GCC编译器 +#### 可选要求: ++ PERL ++ LIBATOMIC_OPS ++ LibFD ++ MaxMind GeoIP ++ libxml2的 ++ libxslt +#### 开始之前 ++ 创建普通用户`sudo`访问。 ++ 切换到新用户:`su - ` ++ 更新系统:`sudo apt update && sudo apt upgrade -y` +#### 从源代码构建NGINX ++ 1、NGINX是用C编写的程序,所以我们需要安装C编译器(GCC)。 + + sudo apt install build-essential -y + ++ 2、下载最新版本的NGINX源代码并解压缩: + + wget https://nginx.org/download/nginx-1.13.1.tar.gz && tar zxvf nginx-1.13.1.tar.gz + ++ 3、下载NGINX依赖项的源代码并解压缩 + > NGINX依赖于3个库:PCRE,zlib和OpenSSL: + + # PCRE version 4.4 - 8.40 + wget https://ftp.pcre.org/pub/pcre/pcre-8.40.tar.gz && tar xzvf pcre-8.40.tar.gz + + # zlib version 1.1.3 - 1.2.11 + wget http://www.zlib.net/zlib-1.2.11.tar.gz && tar xzvf zlib-1.2.11.tar.gz + + # OpenSSL version 1.0.2 - 1.1.0 + wget https://www.openssl.org/source/openssl-1.1.0f.tar.gz && tar xzvf openssl-1.1.0f.tar.gz + ++ 4、删除所有.tar.gz文件。我们不再需要了 + + wget https://nginx.org/download/nginx-1.13.1.tar.gz && tar zxvf nginx-1.13.1.tar.gz + ++ 5、转到NGINX源目录:`` + + cd ~/nginx-1.13.1 + ++ 6、有关帮助,您可以通过运行以下列出可用的配置开关 + + ./configure --help + ++ 7、配置,编译和安装NGINX: + + ./configure --prefix=/usr/share/nginx \ + --sbin-path=/usr/sbin/nginx \ + --modules-path=/usr/lib/nginx/modules \ + --conf-path=/etc/nginx/nginx.conf \ + --error-log-path=/var/log/nginx/error.log \ + --http-log-path=/var/log/nginx/access.log \ + --pid-path=/run/nginx.pid \ + --lock-path=/var/lock/nginx.lock \ + --user=www-data \ + --group=www-data \ + --build=Ubuntu \ + --http-client-body-temp-path=/var/lib/nginx/body \ + --http-fastcgi-temp-path=/var/lib/nginx/fastcgi \ + --http-proxy-temp-path=/var/lib/nginx/proxy \ + --http-scgi-temp-path=/var/lib/nginx/scgi \ + --http-uwsgi-temp-path=/var/lib/nginx/uwsgi \ + --with-openssl=../openssl-1.1.0f \ + --with-openssl-opt=enable-ec_nistp_64_gcc_128 \ + --with-openssl-opt=no-nextprotoneg \ + --with-openssl-opt=no-weak-ssl-ciphers \ + --with-openssl-opt=no-ssl3 \ + --with-pcre=../pcre-8.40 \ + --with-pcre-jit \ + --with-zlib=../zlib-1.2.11 \ + --with-compat \ + --with-file-aio \ + --with-threads \ + --with-http_addition_module \ + --with-http_auth_request_module \ + --with-http_dav_module \ + --with-http_flv_module \ + --with-http_gunzip_module \ + --with-http_gzip_static_module \ + --with-http_mp4_module \ + --with-http_random_index_module \ + --with-http_realip_module \ + --with-http_slice_module \ + --with-http_ssl_module \ + --with-http_sub_module \ + --with-http_stub_status_module \ + --with-http_v2_module \ + --with-http_secure_link_module \ + --with-mail \ + --with-mail_ssl_module \ + --with-stream \ + --with-stream_realip_module \ + --with-stream_ssl_module \ + --with-stream_ssl_preread_module \ + --with-debug \ + --with-cc-opt='-g -O2 -fPIE -fstack-protector-strong -Wformat -Werror=format-security + -Wdate-time -D_FORTIFY_SOURCE=2' \ + --with-ld-opt='-Wl,-Bsymbolic-functions -fPIE -pie -Wl,-z,relro -Wl,-z,now' + make + sudo make install + ++ 8、从主目录中删除所有下载的文件,在这种情况下/home/username: + + cd ~ + rm -r nginx-1.13.1/ openssl-1.1.0f/ pcre-8.40/ zlib-1.2.11/ + ++ 9、检查NGINX版本和编译时间选项: + + sudo nginx -v && sudo nginx -V + + # nginx version: nginx/1.13.0 (Ubuntu) + # built by gcc 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4) + # built with OpenSSL 1.1.0f 25 May 2017 + # TLS SNI support enabled + # configure arguments: --prefix=/etc/nginx . . . + # . . . + # . . . + ++ 10、检查语法和潜在错误: + + sudo nginx -t + # Will throw this error nginx: [emerg] mkdir() "/var/lib/nginx/body" failed (2: No such file or directory) + # Just create directory + mkdir -p /var/lib/nginx && sudo nginx -t + ++ 11、为NGINX创建systemd单元文件: + + sudo vim /etc/systemd/system/nginx.service + ++ 12、复制/粘贴以下内容: + > 注意:根据NGINX的编译方式,PID文件和NGINX二进制文件的位置可能不同。 + + [Unit] + Description=A high performance web server and a reverse proxy server + After=network.target + + [Service] + Type=forking + PIDFile=/run/nginx.pid + ExecStartPre=/usr/sbin/nginx -t -q -g 'daemon on; master_process on;' + ExecStart=/usr/sbin/nginx -g 'daemon on; master_process on;' + ExecReload=/usr/sbin/nginx -g 'daemon on; master_process on;' -s reload + ExecStop=-/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/nginx.pid + TimeoutStopSec=5 + KillMode=mixed + + [Install] + WantedBy=multi-user.target + ++ 13、启动并启用NGINX服务: + + sudo systemctl start nginx.service && sudo systemctl enable nginx.service + ++ 14、检查NGINX是否在重启后启动: + + sudo systemctl is-enabled nginx.service + # enabled + ++ 15、检查NGINX是否正在运行: + + sudo systemctl status nginx.service + ps aux | grep nginx + curl -I 127.0.0.1 + ++ 16、重新启动Ubuntu VPS以验证NGINX是否自动启动: + + sudo shutdown -r now + ++ 17、创建UFW NGINX应用程序配置文件: + + sudo vim /etc/ufw/applications.d/nginx + ++ 18、复制/粘贴以下内容: + + [Nginx HTTP] + title=Web Server (Nginx, HTTP) + description=Small, but very powerful and efficient web server + ports=80/tcp + + [Nginx HTTPS] + title=Web Server (Nginx, HTTPS) + description=Small, but very powerful and efficient web server + ports=443/tcp + + [Nginx Full] + title=Web Server (Nginx, HTTP + HTTPS) + description=Small, but very powerful and efficient web server + ports=80,443/tcp + ++ 19、现在,验证UFW应用配置文件是否被创建和识别: + sudo ufw app list + + # Available applications: + # Nginx Full + # Nginx HTTP + # Nginx HTTPS + # OpenSSH + +### Build + +cd to NGINX source directory & run this: + + ./configure --add-module=/path/to/nginx-rtmp-module + make + make install diff --git a/Nginx/nginx-parameter-config.md b/Nginx/nginx-parameter-config.md new file mode 100644 index 0000000..52a04bc --- /dev/null +++ b/Nginx/nginx-parameter-config.md @@ -0,0 +1,609 @@ +## 高并发系统内核优化 ++ [Socket优化](#Socket) + + Nginx + + 系统内核 ++ [文件优化](#file) + + Nginx + + 系统内核 ++ [配置文件优化](#config-file) + + Nginx配置文件 + + 内核配置文件 + + PHP7配置文件 + + PHP-FPM配置文件 + + php-fpm.conf 重要参数详解 +### Socket优化 +#### Nginx ++ 子进程允许打开的连接数:`worker_connections` +#### 系统内核 ++ [内核参数的优化](http://blog.csdn.net/moxiaomomo/article/details/19442737) ++ 实践优化配置 + + 编辑: `vim /etc/sysctl.conf` + + 配置结果 + + ```bash + net.ipv4.tcp_max_tw_buckets = 6000 + net.ipv4.ip_local_port_range = 1024 65000 + net.ipv4.tcp_tw_recycle = 1 + net.ipv4.tcp_tw_reuse = 1 + net.ipv4.tcp_syncookies = 1 + net.core.somaxconn = 262144 + net.core.netdev_max_backlog = 262144 + net.ipv4.tcp_max_orphans = 262144 + net.ipv4.tcp_max_syn_backlog = 262144 + net.ipv4.tcp_syn_retries = 1 + net.ipv4.tcp_fin_timeout = 1 + net.ipv4.tcp_keepalive_time = 30 + ``` + + 执行命令使之生效:`/sbin/sysctl -p` +### 文件优化 +#### Nginx ++ 指当一个nginx进程打开的最多文件描述符数目:`worker_rlimit_nofile 100000;` +#### 系统内核 ++ 系统限制其最大进程数:`ulimit -n` ++ 编辑文件:`/etc/security/limits.conf` + + ```conf + # End of file + root soft nofile 65535 + root hard nofile 65535 + * soft nofile 65535 + * hard nofile 65535 + ``` +### 配置文件优化 ++ Nginx配置文件 + + ```lua + user www www; + worker_processes 8; + worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000; + error_log /www/log/nginx_error.log crit; + pid /usr/local/nginx/nginx.pid; + worker_rlimit_nofile 204800; + + events + { + use epoll; + worker_connections 204800; + } + + http + { + include mime.types; + default_type application/octet-stream; + + charset utf-8; + + server_names_hash_bucket_size 128; + client_header_buffer_size 2k; + large_client_header_buffers 4 4k; + client_max_body_size 8m; + + sendfile on; + tcp_nopush on; + + keepalive_timeout 60; + + fastcgi_cache_path /usr/local/nginx/fastcgi_cache levels=1:2 + keys_zone=TEST:10m + inactive=5m; + fastcgi_connect_timeout 300; + fastcgi_send_timeout 300; + fastcgi_read_timeout 300; + fastcgi_buffer_size 64k; + fastcgi_buffers 8 64k; + fastcgi_busy_buffers_size 128k; + fastcgi_temp_file_write_size 128k; + fastcgi_cache TEST; + fastcgi_cache_valid 200 302 1h; + fastcgi_cache_valid 301 1d; + fastcgi_cache_valid any 1m; + fastcgi_cache_min_uses 1; + fastcgi_cache_use_stale error timeout invalid_header http_500; + + open_file_cache max=204800 inactive=20s; + open_file_cache_min_uses 1; + open_file_cache_valid 30s; + tcp_nodelay on; + + #gzip on; + gzip on; + gzip_min_length 1k; + gzip_buffes 16 64k; + gzip_http_version 1.1; + gzip_comp_level 6; + gzip_types text/plain application/x-javascript text/css application/javascript text/javascript image/jpeg image/gif image/png application/xml application/json; + gzip_vary on; + gzip_disable "MSIE [1-6].(?!.*SV1)"; + + index index.php index.html index.htm; + + server + { + listen 8080; + server_name backup.aiju.com; + root /www/html/; #这里的位置很重要,不要写在其它指令里面,我曾经就调试了好久才发现这个问题的 + + location /status + { + stub_status on; + } + + location ~ .*\.(html|htm|gif|jpg|jpeg|bmp|png|ico|txt|js|css)$ { + #root /home/www/sansan-web/public; + expires 3d; + } + + location ~ ^/(status|ping)$ + { + include fastcgi_params; + fastcgi_pass unix:/var/run/php7.0.22-fpm.sock; + fastcgi_param SCRIPT_FILENAME $fastcgi_script_name; + } + + location = /favicon.ico { + access_log off; + } + + error_page 400 401 402 403 404 /40x.html; + #location = /40x.html { + # root html; + #} + + error_page 500 501 502 503 504 /50x.html; + location = /50x.html { + root html; + } + + # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 + location ~ \.php$ { + fastcgi_pass unix:/var/run/php7.0.22-fpm.sock; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; + fastcgi_buffer_size 128k; + fastcgi_buffers 4 256k; + fastcgi_busy_buffers_size 256k; + fastcgi_connect_timeout 300; + fastcgi_send_timeout 300; + fastcgi_read_timeout 300; + } + + location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|js|css)$ + { + expires 30d; + } + + log_format access '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" $http_x_forwarded_for'; + access_log /www/log/access.log access; + } + } + ``` ++ 完整的内核优化配置 + + ```lua + net.ipv4.ip_forward = 0 + net.ipv4.conf.default.rp_filter = 1 + net.ipv4.conf.default.accept_source_route = 0 + kernel.sysrq = 0 + kernel.core_uses_pid = 1 + net.ipv4.tcp_syncookies = 1 + kernel.msgmnb = 65536 + kernel.msgmax = 65536 + kernel.shmmax = 68719476736 + kernel.shmall = 4294967296 + net.ipv4.tcp_max_tw_buckets = 6000 + net.ipv4.tcp_sack = 1 + net.ipv4.tcp_window_scaling = 1 + net.ipv4.tcp_rmem = 4096 87380 4194304 + net.ipv4.tcp_wmem = 4096 16384 4194304 + net.core.wmem_default = 8388608 + net.core.rmem_default = 8388608 + net.core.rmem_max = 16777216 + net.core.wmem_max = 16777216 + net.core.netdev_max_backlog = 262144 + net.core.somaxconn = 262144 + net.ipv4.tcp_max_orphans = 3276800 + net.ipv4.tcp_max_syn_backlog = 262144 + net.ipv4.tcp_timestamps = 0 + net.ipv4.tcp_synack_retries = 1 + net.ipv4.tcp_syn_retries = 1 + net.ipv4.tcp_tw_recycle = 1 + net.ipv4.tcp_tw_reuse = 1 + net.ipv4.tcp_mem = 94500000 915000000 927000000 + net.ipv4.tcp_fin_timeout = 1 + net.ipv4.tcp_keepalive_time = 30 + net.ipv4.ip_local_port_range = 1024 65000 + ``` +#### PHP.ini配置文件优化(PHP7) ++ 启用Zend Opcache,php.ini配置文件中加入 + + ```bash + opcache.enable=1 + zend_extension=opcache.so + opcache.memory_consumption=128 + opcache.interned_strings_buffer=8 + opcache.max_accelerated_files=4000 + opcache.revalidate_freq=60 + opcache.fast_shutdown=1 + opcache.enable_cli=1 + opcache.huge_code_pages=1 + opcache.file_cache=/tmp + ``` ++ 缓存文件记录 + + ```bash + www@TinywanAliYun:/tmp$ tree -L 6 + . + ├── 8fc9c56d14b6542c6ff7147207730f6b + │   └── home + │   └── www + │   └── web + │   └── go-study-line + │   ├── application + │   ├── config + │   ├── public + │   ├── runtime + │   ├── thinkphp + │   └── vendor + ``` ++ 使用新的编译器,使用新一点的编译器, 推荐GCC 4.8以上, 因为只有GCC 4.8以上PHP才会开启Global Register for opline and execute_data支持, 这个会带来5%左右的性能提升 ++ 开启HugePages,然后开启Opcache的huge_code_pages + + 系统中开启HugePages + + ```bash + sudo sysctl vm.nr_hugepages=512 + ``` + + 分配512个预留的大页内存 + + ```bash + $ cat /proc/meminfo | grep Huge + AnonHugePages: 106496 kB + HugePages_Total: 512 + HugePages_Free: 504 + HugePages_Rsvd: 27 + HugePages_Surp: 0 + Hugepagesize: 2048 kB + ``` + + 然后在php.ini中加入,`opcache.huge_code_pages=1` ++ 开启Opcache File Cache,`opcache.file_cache=/tmp` ++ 启用Zend Opcache +#### PHP-FPM优化 ++ 结构 + + ```bash + +---> php.ini PHP配置文件 + | + PHP-->|---> php-fpm 服务控制脚本 + +---> php-fpm.conf 进程服务主配置文件 + | + +---> www.conf 进程服务扩展配置文件 + ``` ++ `php.ini` + + ```php + # 设置错误日志的路径 + error_log = /var/log/php-fpm/error.log + + # 引入www.conf文件中的配置 + include=/usr/local/php7/etc/php-fpm.d/*.conf + + # 设置主进程打开的最大文件数 + rlimit_files = 102400 + ``` + ++ `php-fpm.conf` 进程服务主配置文件 + + ```php + pid = run/php-fpm.pid + # 设置错误日志的路径 + error_log = /var/log/php-fpm/error.log + + # 引入www.conf文件中的配置 + include=/usr/local/php7/etc/php-fpm.d/*.conf + + # 设置主进程打开的最大文件数 + rlimit_files = 65535 + ``` + ++ `www.conf` 进程服务扩展配置文件 + + ```php + # 设置启动进程的帐户和组 + user = www + group = www + + # 设置php监听方式,注意这里要设置PHP套接字文件的权限,默认是root,Nginx无法访问 + # listen = 127.0.0.1:9000 + listen = /var/run/php-fpm/php-fpm.sock + + #backlog数,-1表示无限制,由操作系统决定,此行注释掉就行。backlog含义参考:http://www.3gyou.cc/?p=41 + + listen.allowed_clients = 127.0.0.1 + # 允许访问FastCGI进程的IP,设置any为不限制IP,如果要设置其他主机的nginx也能访问这台FPM进程, + # listen处要设置成本地可被访问的IP。默认值是any。每个地址是用逗号分隔. + # 如果没有设置或者为空,则允许任何服务器请求连接 + + #backlog数,-1表示无限制,由操作系统决定,此行注释掉就行。backlog含义参考:http://www.3gyou.cc/?p=41 + listen.backlog = 4096 + + # unix socket设置选项,如果使用tcp方式访问,这里注释即可。 + listen.owner = www + listen.group = www + listen.mode = 0660 + + # 开启慢日志 + slowlog = /var/log/php-fpm/php-slow.log + request_slowlog_timeout = 10s + + # 如果客户端请求出现502请修改以下配置参数,默认值:0,如果执行shell脚本,建议默认就可以。 + request_terminate_timeout = 30 + + #对于专用服务器,pm可以设置为static。 + pm = dynamic + + # 设置工作进程数(根据实际情况设置) + pm.max_children = 50 + + # pm.start_servers不能小于pm.min_spare_servers,推荐为最大的pm.max_children的%10 + pm.start_servers = 8 + pm.min_spare_servers = 5 + pm.max_spare_servers = 10 + pm.max_requests = 10240 + + # cat /usr/local/php-5.5.10/etc/php-fpm.conf | grep status_path + pm.status_path = /status + + # 设置扩展配置主进程打开的最大文件数 + rlimit_files = 65535 + + # 设置php的session目录(所属用户和用户组都是www) + php_value[session.save_handler] = files + php_value[session.save_path] = /var/tmp/php/session + ``` + ++ 调整PHP-FPM(Nginx)的子进程 + + 日志中出现以下警告消息,这意味着没有足够的PHP-FPM进程 + + ```php + [19-Aug-2017 01:02:20] WARNING: [pool www] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers) + [19-Aug-2017 01:02:21] WARNING: [pool www] server reached pm.max_children setting (256), consider raising it + ``` + + + 根据系统内存量来计算和更改这些值,` /etc/php-fpm.d/www.conf` + + ```php + pm.max_children = 50 + pm.start_servers = 5 + pm.min_spare_servers = 5 + pm.max_spare_servers = 35 + ``` + + + 以下命令将帮助我们确定每个(PHP-FPM)子进程使用的内存: + > RSS列显示PHP-FPM进程的未交换的物理内存使用量,单位为千字节 + > 平均每个PHP-FPM进程在我的机器上占用大约75MB的RAM + + ```php + ps -ylC php-fpm --sort:rss + ``` + + + pm.max_children的适当值可以计算为: + + ```php + pm.max_children = Total RAM dedicated to the web server / Max child process size + ``` + + + 在我的情况下是56MB,服务器有16GB的RAM,所以: + >我留下了一些记忆,让系统呼吸。在计算内存使用情况时,您需要考虑计算机上运行的任何其他服务。 + + ```php + pm.max_children = 15806MB / 56MB = 282 + # Tinywan 计算方式(实战) + # pm.max_children = (15806MB - 1024MB) / 57MB = 259 + ``` + + + 已经改变了如下设置 + >请注意,非常高的价值并不意味着任何好处 + + ```php + pm.max_children = 256 + pm.start_servers = 32 + pm.min_spare_servers = 32 + pm.max_spare_servers = 128 + pm.max_requests = 65535 + ``` + + + 您可以使用此方便的命令检查单个PHP-FPM进程的平均内存使用情况 + + ```php + ps --no-headers -o "rss,cmd" -C php-fpm | awk '{ sum+=$1 } END { printf ("%d%s\n", sum/NR/1024,"M") }' + ``` +## php-fpm.conf 重要参数详解 + ++ 常用参数解释 + + ```php + 1)pm = dynamic #对于专用服务器,pm可以设置为static。 + #如何控制子进程,选项有static和dynamic。如果选择static,则由pm.max_children指定固定的子进程数。如果选择dynamic,则由pm.max_children、pm.start_servers、pm.min_spare_servers、pm.max_spare_servers 参数决定: + 2)pm.max_children + + 在同一时间最大的进程数 + + pm.max_children = 120 + + 3)pm.start_servers + + php-fpm启动时开启的等待请求到来的进程数,默认值为:min_spare_servers + (max_spare_servers - min_spare_servers) / 2 + + pm.start_servers = 80 + + 4)pm.min_spare_servers + 在空闲状态下,运行的最小进程数,如果小于此值,会创建新的进程 + pm.min_spare_servers = 60 + + 5)pm.max_spare_servers + 在空闲状态下,运行的最大进程数,如果大于此值,会kill部分进程 + pm.max_spare_servers = 120 + + 6)pm.process_idle_timeout + 空闲多少秒之后进程会被kill,默认为10s + pm.process_idle_timeout = 10s + + 7)pm.max_requests + 每个进程处理多少个请求之后自动终止,可以有效防止内存溢出,如果为0则不会自动终止,默认为0#设置每个子进程重生之前服务的请求数. 对于可能存在内存泄漏的第三方模块来说是非常有用的. 如果设置为 '0' 则一直接受请求. 等同于 PHP_FCGI_MAX_REQUESTS 环境变量. 默认值: 0. + pm.max_requests = 5000 + + 8)pm.status_path + 注册的URI,以展示php-fpm状态的统计信息 + pm.status_path = /status + 其中统计页面信息有: + pool 进程池名称 + process manager 进程管理器名称(static, dynamic or ondemand) + start time php-fpm启动时间 + start since php-fpm启动的总秒数 + accepted conn 当前进程池接收的请求数 + listen queue 等待队列的请求数 + max listen queue 自启动以来等待队列中最大的请求数 + listen queue len 等待连接socket队列大小 + idle processes 当前空闲的进程数 + active processes 活动的进程数 + total processes 总共的进程数(idle+active) + max active processes 自启动以来活动的进程数最大值 + max children reached 达到最大进程数的次数 + + 9)ping.path + ping url,可以用来测试php-fpm是否存活并可以响应 + ping.path = /ping + + 10)ping.response + ping url的响应正文返回为 HTTP 200 的 text/plain 格式文本. 默认值: pong. + ping.response = pong + + + 11)pid = run/php-fpm.pid + #pid设置,默认在安装目录中的var/run/php-fpm.pid,建议开启 + + 12)error_log = log/php-fpm.log + #错误日志,默认在安装目录中的var/log/php-fpm.log + + 13)log_level = notice + #错误级别. 可用级别为: alert(必须立即处理), error(错误情况), warning(警告情况), notice(一般重要信息), debug(调试信息). 默认: notice. + + 14)emergency_restart_threshold = 60 + emergency_restart_interval = 60s + #表示在emergency_restart_interval所设值内出现SIGSEGV或者SIGBUS错误的php-cgi进程数如果超过 emergency_restart_threshold个,php-fpm就会优雅重启。这两个选项一般保持默认值。 + + 15)process_control_timeout = 0 + #设置子进程接受主进程复用信号的超时时间. 可用单位: s(秒), m(分), h(小时), 或者 d(天) 默认单位: s(秒). 默认值: 0. + + 16)daemonize = yes + #后台执行fpm,默认值为yes,如果为了调试可以改为no。在FPM中,可以使用不同的设置来运行多个进程池。 这些设置可以针对每个进程池单独设置。 + + 17)listen = 127.0.0.1:9000 + #fpm监听端口,即nginx中php处理的地址,一般默认值即可。可用格式为: 'ip:port', 'port', '/path/to/unix/socket'. 每个进程池都需要设置. + + 18)listen.backlog = -1 + #backlog数,-1表示无限制,由操作系统决定,此行注释掉就行。 19)listen.allowed_clients = 127.0.0.1 + #允许访问FastCGI进程的IP,设置any为不限制IP,如果要设置其他主机的nginx也能访问这台FPM进程,listen处要设置成本地可被访问的IP。默认值是any。每个地址是用逗号分隔. 如果没有设置或者为空,则允许任何服务器请求连接 + listen.owner = www + listen.group = www + listen.mode = 0666 + + 20)#unix socket设置选项,如果使用tcp方式访问,这里注释即可。 + user = www + group = www + #启动进程的帐户和组 + + 21)request_terminate_timeout = 0 + #设置单个请求的超时中止时间. 该选项可能会对php.ini设置中的'max_execution_time'因为某些特殊原因没有中止运行的脚本有用. 设置为 '0' 表示 'Off'.当经常出现502错误时可以尝试更改此选项。 + + 22)request_slowlog_timeout = 10s + #当一个请求该设置的超时时间后,就会将对应的PHP调用堆栈信息完整写入到慢日志中. 设置为 '0' 表示 'Off' + + 23)slowlog = log/$pool.log.slow + #慢请求的记录日志,配合request_slowlog_timeout使用 + + 24)rlimit_files = 1024 + #设置文件打开描述符的rlimit限制. 默认值: 系统定义值默认可打开句柄是1024,可使用 ulimit -n查看,ulimit -n 2048修改。 + + 25)rlimit_core = 0 + #设置核心rlimit最大限制值. 可用值: 'unlimited' 、0或者正整数. 默认值: 系统定义值. + + 26)chroot = + #启动时的Chroot目录. 所定义的目录需要是绝对路径. 如果没有设置, 则chroot不被使用. + + 27)chdir = + #设置启动目录,启动时会自动Chdir到该目录. 所定义的目录需要是绝对路径. 默认值: 当前目录,或者/目录(chroot时) + + 28)catch_workers_output = yes + #重定向运行过程中的stdout和stderr到主要的错误日志文件中. 如果没有设置, stdout 和 stderr 将会根据FastCGI的规则被重定向到 /dev/null . 默认值: 空. + ``` ++ 二、php对子进程的三种管理方式 + + ```php + tatic: + 表示在php-fpm运行时直接fork出 pm.max_chindren个子进程 + dynamic: + 表示,运行时fork出pm.start_servers个进程,随着负载的情况,动态的调整,最多不超过pm.max_children个进程。同时,保证闲置进程数不少于pm.min_spare_servers数量,否则新的进程会被创建,当然也不是无限制的创建,最多闲置进程不超过pm.max_spare_servers数量,超过则一些闲置进程被清理。 + ondemand: + 当有请求时,创建进程,启动不创建,最多不超过pm.max_chindren进程数,当进程闲置会在pm.process_idle_timeout秒后被及时释放。 + ``` + ++ 三、重要参数的理解与设置 + + ```php + 【重要一】 + request_terminate_timeout = 120 + #表示等待120秒后,结束那些没有自动结束的php脚本,以释放占用的资源。 + 当PHP运行在php-fpm模式下,php.ini配置的max_execute_time是无效的,需要在php-fpm.conf中配置另外一个配置项:request_terminate_timeout;以下是官方文档的说明: + + + set_time_limit()和max_execution_time只影响脚本本身执行的时间。(这两个参数在php.ini中)任何发生在诸如使用system()的系统调用,流操作,数据库操作等的脚本执行的最大时间不包括其中. + + + 下面4个参数的意思分别为: + pm.max_children:静态方式下开启的php-fpm进程数量。 + pm.start_servers:动态方式下的起始php-fpm进程数量。 + pm.min_spare_servers:动态方式下的最小php-fpm进程数量。 + pm.max_spare_servers:动态方式下的最大php-fpm进程数量。 + + 如果dm设置为static,那么其实只有pm.max_children这个参数生效。系统会开启设置数量的php-fpm进程。 + 如果dm设置为 dynamic,那么pm.max_children参数失效,后面3个参数生效。 + 系统会在php-fpm运行开始 的时候启动pm.start_servers个php-fpm进程,然后根据系统的需求动态在pm.min_spare_servers和 pm.max_spare_servers之间调整php-fpm进程数。 + + 比如说512M的VPS,建议pm.max_spare_servers设置为20。至于pm.min_spare_servers,则建议根据服 + 务器的负载情况来设置,比较合适的值在5~10之间。 + + + pm = dynamic模式非常灵活,也通常是默认的选项。但是,dynamic模式为了最大化地优化服务器响应,会造成更多内存使用,因为这种模式只会杀掉超出最大闲置进程数(pm.max_spare_servers)的闲置进程, + 比如最大闲置进程数是30,然后网站经历了一次访问高峰,高峰期时共动态开启了50个进程全部忙碌,0个闲置进程数,接着过了高峰期,可能没有一个请求,于是会有50个闲置进程,但是此时php-fpm只会杀掉20个 + 子进程,始终剩下30个进程继续作为闲置进程来等待请求,这可能就是为什么过了高峰期后即便请求数大量减少服务器内存使用却也没有大量减少,也可能是为什么有些时候重启下服务器情况就会好很多,因为重启 + 后,php-fpm的子进程数会变成最小闲置进程数,而不是之前的最大闲置进程数。 + + + 第三种就是文章中提到的pm = ondemand模式,这种模式和pm = dynamic相反,把内存放在第一位,他的工作模式很简单,每个闲置进程,在持续闲置了pm.process_idle_timeout秒后就会被杀掉,有了这个模式,到了服务器低峰期内存自然会降下来,如果服务器长时间没有请求,就只会有一个php-fpm主进程,当然弊端是, + 遇到高峰期或者如果pm.process_idle_timeout的值太短的话,无法避免服务器频繁创建进程的问题,因此pm = dynamic和pm = ondemand谁更适合视实际情况而定。 + + + 【重要二】 + pm.max_requests = 500 + 设置每个子进程重生之前服务的请求数. 对于可能存在内存泄漏的第三方模块来说是非常有用的. 如果设置为 ’0′ 则一直接受请求. 等同于 PHP_FCGI_MAX_REQUESTS 环境变量. 默认值: 0.这段配置的意思是,当一个 PHP-CGI 进程处理的请求数累积到 500 个后,自动重启该进程。 + + 但是为什么要重启进程呢? + 一般在项目中,我们多多少少都会用到一些 PHP 的第三方库,这些第三方库经常存在内存泄漏问题,如果不定期重启 PHP-CGI 进程,势必造成内存使用量不断增长。因此 PHP-FPM 作为 PHP-CGI 的管理器,提供了这么一项监控功能,对请求达到指定次数的 PHP-CGI 进 + 程进行重启,保证内存使用量不增长。 + 【一些问题及网上解决办法】 + 正是因为这个机制,在高并发的站点中,经常导致 502 错误,我猜测原因是 PHP-FPM 对从 NGINX 过来的请求队列没处理好。不过我目前用的还是 PHP 5.3.2,不知道在 PHP 5.3.3 中是否还存在这个问题。 + + 目前我们的解决方法是,把这个值尽量设置大些,尽可能减少 PHP-CGI 重新 SPAWN 的次数,同时也能提高总体性能。在我们自己实际的生产环境中发现,内存泄漏并不明显,因此我们将这个值设置得非常大(204800)。大家要根据自己的实际情况设置这个值,不能盲目 + 地加大。 + ``` + +## HELP ++ [php-fpm - 启动参数及重要配置详解](http://www.4wei.cn/archives/1002061) ++ [php-fpm backlog参数潜在问题](http://blog.csdn.net/willas/article/details/11634825) ++ [Adjusting child processes for PHP-FPM (Nginx)](https://myshell.co.uk/blog/2012/07/adjusting-child-processes-for-php-fpm-nginx/) ++ [Nginx的worker_processes优化](http://blog.chinaunix.net/uid-26000296-id-3987521.html) ++ [php-fpm.conf重要参数详解](http://blog.csdn.net/sinat_22991367/article/details/73431269) + diff --git a/Nginx/nginx-phases.md b/Nginx/nginx-phases.md new file mode 100644 index 0000000..9ccc952 --- /dev/null +++ b/Nginx/nginx-phases.md @@ -0,0 +1,155 @@ +## nginx的11个phases ++ 一个请求经过nginx处理的过程中,会经过一系列的阶段(phases),下面这个表格列出了nginx的所有phases,每个阶段可选的退出方式,包含的模块和对应的指令 + + | Phases | modules / directives | description | + | :------------ |:---------------:| -----:| + | NGX_HTTP_POST_READ_PHASE | HttpRealIpModule | 读取请求内容阶段 | + | NGX_HTTP_SERVER_REWRITE_PHASE
Location ( server rewrite ) | HttpRewriteModule
rewrite | 请求地址重写阶段| + | NGX_HTTP_FIND_CONFIG_PHASE
Location ( location selection ) | HttpCoreModule
location |配置查找阶段| + | NGX_HTTP_REWRITE_PHASE
Location ( location rewrite ) | HttpLuaModule
set_by_lua、rewrite_by_lua |请求地址重写阶段| + | NGX_HTTP_POST_REWRITE_PHASE | 不注册其他模块 |请求地址重写提交阶段| + | NGX_HTTP_PREACCESS_PHASE
( location selection ) | degradation
NginxHttpLimitZoneModule / limit_zone
HttpLimitReqModule / limit req |访问权限检查准备阶段| + | NGX_HTTP_ACCESS_PHASE | HttpAccessModule
allow, deny
NginxHttpAuthBasicModule
HttpLuaModule
access_by_lua |访问权限检查阶段| + | NGX_HTTP_POST_ACCESS_PHASE | 该指令可以用于控制access阶段的指令彼此之间的协作方式 |访问权限检查提交阶段| + | NGX_HTTP_TRY_FILES_PHASE | HttpCoreModule
try_files |配置项try_files处理阶段| + | NGX_HTTP_CONTENT_PHASE | HttpProxyModule / proxy
HttpLuaModule / content_by_lua
HttpCoreModule / proxy_pass
HttpFcgiModule / FastCGI |内容产生阶段| + | NGX_HTTP_TRY_FILES_PHASE | HttpLogModuel / access_log |日志模块处理阶段| + ++ 各个phase说明 + + (1) post read phase + > `post-read ` 属于 `rewrite`阶段 + + > `post-read` 支持Nginx模块的钩子 + + > 内置模块 `ngx_realip` 把它的处理程序`post-read`分阶段挂起,强制重写请求的原始地址作为特定请求头的值 + + ``` + server { + listen 8080; + + set_real_ip_from 127.0.0.1; + real_ip_header X-My-IP; + + location /test { + set $addr $remote_addr; + echo "from: $addr"; + } + } + ``` + + > 该配置告诉Nginx强制将每个请求的原始地址重写127.0.0.1为请求头的值X-My-IP。同时它使用内置变量 `$remote_addr`来输出请求的原始地址 + + ``` + $ curl -H 'X-My-IP: 1.2.3.4' localhost:8080/test + from: 1.2.3.4 + ``` + > curl 参数 -H :自定义头信息传递给服务器 + + > 该选项X-My-IP: 1.2.3.4在请求中包含一个额外的HTTP头 + + > 测试结果 + ``` + $ curl localhost:8080/test + from: 127.0.0.1 + + $ curl -H 'X-My-IP: abc' localhost:8080/test + from: 127.0.0.1 + `` + + + server_rewrite phase + + > 这个阶段主要进行初始化全局变量,或者server级别的重写。如果把重写指令放到 server 中,那么就进入了server rewrite 阶段。(重写指令见rewrite phase) + + > ( 1 ) `server-rewrite ` 阶段运行时间早于 `rewrite` 阶段 + + ``` + location /tinywan { + set $bbb "$aaa, world"; + echo $bbb; + } + set $aaa "HELLO"; + ``` + + > `set $a hello` 声明被放在`server`指令中,所以它运行在`server-rewrite`阶段 + + > 因此 `set $b "$a, world'" `,在location指令中执行 `set ` 指令后,它将获得正确的`$a`值 + + > 执行结果 + + ``` + # curl http://127.0.0.2:8008/tinywan + HELLO, world + ``` + > ( 2 ) `post-read` 阶段阶段运行时间早于 `server-rewrite` 阶段执行 + + ``` + server { + listen 8080; + + set $addr $remote_addr; + + set_real_ip_from 127.0.0.1; + real_ip_header X-Real-IP; + + location /test { + echo "from: $addr"; + } + } + ``` + + > ( 3 ) `ngx_realip` 阶段阶段运行时间早于 `server 的 set 指令` 阶段执行 + + ``` + $ curl -H 'X-Real-IP: 1.2.3.4' localhost:8080/test + from: 1.2.3.4 + ``` + + > 服务器指令中的命令集始终比模块ngx_realip晚, + + > ( 4 ) `server-rewrite` 阶段阶段运行时间早于 `find-config` 阶段执行 + + + find config phase + > ( 5 ) `find-config` 阶段阶段运行时间早于 `rewrite` 阶段执行 + + > 这个阶段使用重写之后的uri来查找对应的location,值得注意的是该阶段可能会被执行多次,因为也可能有location级别的重写指令。这个阶段并不支持 Nginx 模块注册处理程序,而是由 Nginx 核心来完成当前请求与 location 配置块之间的配对工作 + + + rewrite phase: + > 如果把重写指令放到 location中,那么就进入了rewrite phase,这个阶段是location级别的uri重写阶段,重写指令也可能会被执行多次 + + > 有`HttpRewriteModule` 的set指令、rewrite指令 + + > HttpLuaModule的 set_by_lua指令, + + > ngx_set_misc模块的set_unescape_uri指令 + + > 另外HttpRewriteModule的几乎所有指令都属于rewrite阶段。 ++ 结论:作用域为同一个phase的不同modules的指令,如果modules之间做了特殊的兼容,则它们按照指令在配置文件中出现的顺序依次执行下来 ++ HttpLuaModule 模块指令 + + init_by_lua + > 在nginx重新加载配置文件时,运行里面lua脚本,常用于全局变量的申请。例如lua_shared_dict共享内存的申请,只有当nginx重起后,共享内存数据才清空,这常用于统计。 + + + set_by_lua + > 设置一个变量,常用与计算一个逻辑,然后返回结果,该阶段不能运行Output API、Control API、Subrequest API、Cosocket API + + + rewrite_by_lua + > 在access阶段前运行,主要用于rewrite + + + access_by_lua + > 主要用于访问控制,能收集到大部分变量,类似status需要在log阶段才有。这条指令运行于nginx access阶段的末尾,因此总是在 allow 和 deny 这样的指令之后运行,虽然它们同属 access 阶段。 + + + content_by_lua + > 阶段是所有请求处理阶段中最为重要的一个,运行在这个阶段的配置指令一般都肩负着生成内容(content)并输出HTTP响应。 + + + header_filter_by_lua + > 一般只用于设置Cookie和Headers等,该阶段不能运行Output API、Control API、Subrequest API、Cosocket API + + + body_filter_by_lua + > 一般会在一次请求中被调用多次, 因为这是实现基于 HTTP 1.1 chunked 编码的所谓“流式输出”的,该阶段不能运行Output API、Control API、Subrequest API、Cosocket API + + + log_by_lua + > 该阶段总是运行在请求结束的时候,用于请求的后续操作,如在共享内存中进行统计数据,如果要高精确的数据统计,应该使用body_filter_by_lua + ++ --with-http_realip_module 模块 + + set_real_ip_from   192.168.1.0/24;     指定接收来自哪个前端发送的 IP head 可以是单个IP或者IP段 + + set_real_ip_from 192.168.2.1; + + real_ip_header X-Real-IP; IP head 的对应参数,默认即可。 \ No newline at end of file diff --git a/Nginx/nginx-start-script.md b/Nginx/nginx-start-script.md new file mode 100644 index 0000000..d667548 --- /dev/null +++ b/Nginx/nginx-start-script.md @@ -0,0 +1,757 @@ +## 服务启动、停止和重启脚本 ++ [Ubuntu 14.04.2 LTS 启动脚本](#Ubuntu14) + + PHP-FPM 服务 + + Nginx 服务 ++ [Ubuntu 16.04.2 LTS 启动脚本](#Ubuntu16) + + PHP-FPM 服务 + + Nginx 服务 +##
Ubuntu 14.04.2 LTS 启动脚本 +### PHP-FPM 服务 ++ 下载文件[php-fpm.sh](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/PHP/PHP-FPM/php-fpm.sh) ++ 注意配置文件:`sudo vim /usr/local/php-7.2/etc/php-fpm.conf` + > 务必开启配置文件的pid路径:`pid = run/php-fpm.pid` +   否则会报错:`no pid file found - php-fpm is not running ?`  ++ CP到默认开启的服务脚本: + + ``` + sudo cp php-fpm.sh /etc/init.d/php-fpm + ``` ++ 给予权限: + + ``` + sudo chmod +x /etc/init.d/php-fpm + ``` ++ 使用`sysv-rc-conf`安装,[如何安装sysv-rc-conf管理服务](http://blog.csdn.net/gatieme/article/details/45251389) + + ![Markdown](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Images/nginx_start_script.png) ++ `php-fpm.sh`代码 + + ``` + #! /bin/sh + ### BEGIN INIT INFO + # Provides: php-fpm + # Required-Start: $remote_fs $network + # Required-Stop: $remote_fs $network + # Default-Start: 2 3 4 5 + # Default-Stop: 0 1 6 + # Short-Description: starts php-fpm + # Description: starts the PHP FastCGI Process Manager daemon + ### END INIT INFO + + prefix=/opt/php-7.0.9 # 只需要修改这里就可以里,这里是编译路径 + exec_prefix=${prefix} + + php_fpm_BIN=${exec_prefix}/sbin/php-fpm + php_fpm_CONF=${prefix}/etc/php-fpm.conf + php_fpm_PID=${prefix}/var/run/php-fpm.pid + + php_opts="--fpm-config $php_fpm_CONF --pid $php_fpm_PID" + + wait_for_pid () { + try=0 + + while test $try -lt 35 ; do + + case "$1" in + 'created') + if [ -f "$2" ] ; then + try='' + break + fi + ;; + + 'removed') + if [ ! -f "$2" ] ; then + try='' + break + fi + ;; + esac + + echo -n . + try=`expr $try + 1` + sleep 1 + + done + + } + case "$1" in + start) + echo -n "Starting PHP-FPM Server ... " + + $php_fpm_BIN --daemonize $php_opts + + if [ "$?" != 0 ] ; then + echo " failed" + exit 1 + fi + + wait_for_pid created $php_fpm_PID + + if [ -n "$try" ] ; then + echo " failed" + exit 1 + else + echo "[OK]" + fi + ;; + + stop) + echo -n "Stopping PHP-FPM Server ... " + + if [ ! -r $php_fpm_PID ] ; then + echo "warning, no pid file found - php-fpm is not running ?" + exit 1 + fi + + kill -QUIT `cat $php_fpm_PID` + + wait_for_pid removed $php_fpm_PID + + if [ -n "$try" ] ; then + echo " failed. Use force-quit" + exit 1 + else + echo " [OK]" + fi + ;; + + force-quit) + echo -n "Terminating PHP-FPM " + + if [ ! -r $php_fpm_PID ] ; then + echo "warning, no pid file found - php-fpm is not running ?" + exit 1 + fi + + kill -TERM `cat $php_fpm_PID` + + wait_for_pid removed $php_fpm_PID + + if [ -n "$try" ] ; then + echo " failed" + exit 1 + else + echo " [OK]" + fi + ;; + + restart) + $0 stop + $0 start + ;; + + reload) + + echo -n "Reload service php-fpm " + + if [ ! -r $php_fpm_PID ] ; then + echo "warning, no pid file found - php-fpm is not running ?" + exit 1 + fi + + kill -USR2 `cat $php_fpm_PID` + + echo "[OK]" + ;; + + *) + echo "Usage: $0 {start|stop|force-quit|restart|reload}" + exit 1 + ;; + + esac + ``` ++ 运行效果 + + ```bash + www@tinywan:~$ sudo service php-fpm restart + Stopping PHP-FPM Server ... [OK] + Starting PHP-FPM Server ... [OK] + ``` +### Nginx 服务 + ++ 第一种安装方式 + + 查看当前nginx是否已经在开机启动项里面: + + ```bash + ls /etc/rc* + ``` + + 如何安装 + + ```bash + #使用wget -O 下载并以不同的文件名保存 + sudo wget https://raw.github.com/JasonGiedymin/nginx-init-ubuntu/master/nginx -O /etc/init.d/nginx + # 给与权限 + sudo chmod +x /etc/init.d/nginx + # 设置为启动项 + sudo update-rc.d nginx defaults + ``` + +   需要修改的地方: + +   1、`NGINXPATH=${NGINXPATH:-/opt/openresty/nginx}` 修改为自己的路径 + +   2、`PIDSPATH=${PIDSPATH:-$NGINXPATH/logs}` pid文件路径 + > 如果在配置文件修改为:`pid /run/nginx.pid;` +         PIDSPATH=${PIDSPATH:-$NGINXPATH/logs}修改为:PIDSPATH="/run" ++   第二种安装方式 + + 和PHP-FPM一样,`nginx.sh`代码 + + ```bash + #! /bin/sh + ### BEGIN INIT INFO + # Provides: nginx + # Required-Start: $remote_fs $syslog + # Required-Stop: $remote_fs $syslog + # Default-Start: 2 3 4 5 + # Default-Stop: 0 1 6 + # Short-Description: nginx init.d dash script for Ubuntu or other *nix. + # Description: nginx init.d dash script for Ubuntu or other *nix. + ### END INIT INFO + #------------------------------------------------------------------------------ + # nginx - this Debian Almquist shell (dash) script, starts and stops the nginx + # daemon for Ubuntu and other *nix releases. + # + # description: Nginx is an HTTP(S) server, HTTP(S) reverse \ + # proxy and IMAP/POP3 proxy server. This \ + # script will manage the initiation of the \ + # server and it's process state. + # + # processname: nginx + # config: /usr/local/nginx/conf/nginx.conf + # pidfile: /usr/local/nginx/logs/nginx.pid + # Provides: nginx + + #------------------------------------------------------------------------------ + # Functions + #------------------------------------------------------------------------------ + LSB_FUNC=/lib/lsb/init-functions + + # Test that init functions exists + test -r $LSB_FUNC || { + echo "$0: Cannot find $LSB_FUNC! Script exiting." 1>&2 + exit 5 + } + + . $LSB_FUNC + + #------------------------------------------------------------------------------ + # Consts + #------------------------------------------------------------------------------ + # Include nginx defaults if available + if [ -f /etc/default/nginx ]; then + . /etc/default/nginx + fi + + # Minimize path + PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin + + PS=${PS:-"nginx"} # process name + DESCRIPTION=${DESCRIPTION:-"Nginx Server..."} # process description + NGINXPATH=${NGINXPATH:-/opt/openresty/nginx} # root path where installed + DAEMON=${DAEMON:-$NGINXPATH/sbin/nginx} # path to daemon binary + NGINX_CONF_FILE=${NGINX_CONF_FILE:-$NGINXPATH/conf/nginx.conf} # config file path + PIDNAME=${PIDNAME:-"nginx"} # lets you do $PS-slave + PIDFILE=${PIDFILE:-$PIDNAME.pid} # pid file + PIDSPATH=${PIDSPATH:-$NGINXPATH/logs} # default pid location, you should change it + RUNAS=${RUNAS:-root} # user to run as + SCRIPT_OK=0 # ala error codes + SCRIPT_ERROR=1 # ala error codes + TRUE=1 # boolean + FALSE=0 # boolean + + #------------------------------------------------------------------------------ + # Simple Tests + #------------------------------------------------------------------------------ + + # Test if nginx is a file and executable + test -x $DAEMON || { + echo "$0: You don't have permissions to execute nginx." 1>&2 + exit 4 + } + + # You can also set your conditions like so: + # set exit condition + # set -e + + #------------------------------------------------------------------------------ + # Functions + #------------------------------------------------------------------------------ + + setFilePerms(){ + if [ -f $PIDSPATH/$PIDFILE ]; then + chmod 400 $PIDSPATH/$PIDFILE + fi + } + + configtest() { + $DAEMON -t -c $NGINX_CONF_FILE + } + + getPSCount() { + return `pgrep -f $PS | wc -l` + } + + isRunning() { + if [ $1 ]; then + pidof_daemon $1 + PID=$? + + if [ $PID -gt 0 ]; then + return 1 + else + return 0 + fi + else + pidof_daemon + PID=$? + + if [ $PID -gt 0 ]; then + return 1 + else + return 0 + fi + fi + } + + #courtesy of php-fpm + wait_for_pid () { + try=0 + + while test $try -lt 35 ; do + case "$1" in + 'created') + if [ -f "$2" ]; then + try='' + break + fi + ;; + + 'removed') + if [ ! -f "$2" ]; then + try='' + break + fi + ;; + esac + + try=`expr $try + 1` + sleep 1 + done + } + + status(){ + isRunning + isAlive=$? + + if [ "${isAlive}" -eq $TRUE ]; then + log_warning_msg "$DESCRIPTION found running with processes: `pidof $PS`" + rc=0 + else + log_warning_msg "$DESCRIPTION is NOT running." + rc=3 + fi + + return + } + + removePIDFile(){ + if [ $1 ]; then + if [ -f $1 ]; then + rm -f $1 + fi + else + #Do default removal + if [ -f $PIDSPATH/$PIDFILE ]; then + rm -f $PIDSPATH/$PIDFILE + fi + fi + } + + start() { + log_daemon_msg "Starting $DESCRIPTION" + + isRunning + isAlive=$? + + if [ "${isAlive}" -eq $TRUE ]; then + log_end_msg $SCRIPT_ERROR + rc=0 + else + start-stop-daemon --start --quiet --chuid \ + $RUNAS --pidfile $PIDSPATH/$PIDFILE --exec $DAEMON \ + -- -c $NGINX_CONF_FILE + status=$? + setFilePerms + + if [ "${status}" -eq 0 ]; then + log_end_msg $SCRIPT_OK + rc=0 + else + log_end_msg $SCRIPT_ERROR + rc=7 + fi + fi + + return + } + + stop() { + log_daemon_msg "Stopping $DESCRIPTION" + + isRunning + isAlive=$? + + if [ "${isAlive}" -eq $TRUE ]; then + start-stop-daemon --stop --quiet --pidfile $PIDSPATH/$PIDFILE + + wait_for_pid 'removed' $PIDSPATH/$PIDFILE + + if [ -n "$try" ]; then + log_end_msg $SCRIPT_ERROR + rc=0 # lsb states 1, but under status it is 2 (which is more prescriptive). Deferring to standard. + else + removePIDFile + log_end_msg $SCRIPT_OK + rc=0 + fi + else + log_end_msg $SCRIPT_ERROR + rc=7 + fi + + return + } + + reload() { + configtest || return $? + + log_daemon_msg "Reloading (via HUP) $DESCRIPTION" + + isRunning + + if [ $? -eq $TRUE ]; then + kill -HUP `cat $PIDSPATH/$PIDFILE` + log_end_msg $SCRIPT_OK + rc=0 + else + log_end_msg $SCRIPT_ERROR + rc=7 + fi + + return + } + + quietupgrade() { + log_daemon_msg "Peforming Quiet Upgrade $DESCRIPTION" + + isRunning + isAlive=$? + + if [ "${isAlive}" -eq $TRUE ]; then + kill -USR2 `cat $PIDSPATH/$PIDFILE` + kill -WINCH `cat $PIDSPATH/$PIDFILE.oldbin` + + isRunning + isAlive=$? + + if [ "${isAlive}" -eq $TRUE ]; then + kill -QUIT `cat $PIDSPATH/$PIDFILE.oldbin` + wait_for_pid 'removed' $PIDSPATH/$PIDFILE.oldbin + removePIDFile $PIDSPATH/$PIDFILE.oldbin + + log_end_msg $SCRIPT_OK + rc=0 + else + log_end_msg $SCRIPT_ERROR + + log_daemon_msg "ERROR! Reverting back to original $DESCRIPTION" + + kill -HUP `cat $PIDSPATH/$PIDFILE` + kill -TERM `cat $PIDSPATH/$PIDFILE.oldbin` + kill -QUIT `cat $PIDSPATH/$PIDFILE.oldbin` + + wait_for_pid 'removed' $PIDSPATH/$PIDFILE.oldbin + removePIDFile $PIDSPATH/$PIDFILE.oldbin + + log_end_msg $SCRIPT_OK + rc=0 + fi + else + log_end_msg $SCRIPT_ERROR + rc=7 + fi + + return + } + + terminate() { + log_daemon_msg "Force terminating (via KILL) $DESCRIPTION" + + PIDS=`pidof $PS` || true + + [ -e $PIDSPATH/$PIDFILE ] && PIDS2=`cat $PIDSPATH/$PIDFILE` + + for i in $PIDS; do + if [ "$i" = "$PIDS2" ]; then + kill $i + wait_for_pid 'removed' $PIDSPATH/$PIDFILE + removePIDFile + fi + done + + log_end_msg $SCRIPT_OK + rc=0 + } + + destroy() { + log_daemon_msg "Force terminating and may include self (via KILLALL) $DESCRIPTION" + killall $PS -q >> /dev/null 2>&1 + log_end_msg $SCRIPT_OK + rc=0 + } + + pidof_daemon() { + PIDS=`pidof $PS` || true + + [ -e $PIDSPATH/$PIDFILE ] && PIDS2=`cat $PIDSPATH/$PIDFILE` + + for i in $PIDS; do + if [ "$i" = "$PIDS2" ]; then + return 1 + fi + done + + return 0 + } + + action="$1" + case "$1" in + start) + start + ;; + stop) + stop + ;; + restart|force-reload) + stop + # if [ $rc -ne 0 ]; then + # script_exit + # fi + sleep 1 + start + ;; + reload) + $1 + ;; + status) + status + ;; + configtest) + $1 + ;; + quietupgrade) + $1 + ;; + terminate) + $1 + ;; + destroy) + $1 + ;; + *) + FULLPATH=/etc/init.d/$PS + echo "Usage: $FULLPATH {start|stop|restart|force-reload|reload|status|configtest|quietupgrade|terminate|destroy}" + echo " The 'destroy' command should only be used as a last resort." + exit 3 + ;; + esac + + exit $rc + ``` ++ 根据自己环境,配置文件路径,下面修改为Openresty下的Nginx启动项(Nginx 安装在/usr/local/openresty/目录下) + + ```bash + sudo vim /etc/init.d/nginx + NGINXPATH=${NGINXPATH:-/usr/local/openresty/nginx} + ``` ++ 开启服务 + + ```bash + sudo service nginx restart + [sudo] password for www: + * Stopping Nginx Server... [ OK ] + * Starting Nginx Server... [ OK ] + ``` +## Ubuntu 16.04.2 LTS 启动脚本 +### PHP-FPM 服务 ++ `php-fpm.sh`脚本代码 同上 ++ 注意,需要重新加载服务: + + ``` + sudo systemctl daemon-reload + ``` ++ 开启服务 + + ```bash + sudo systemctl start php-fpm.service + ``` ++ 停止服务 + + ```javascript + sudo systemctl stop php-fpm.service + ``` ++ 重启服务 + + ```javascript + sudo systemctl restart php-fpm.service + ``` ++ 服务状态 + + ```bash + sudo systemctl status php-fpm.service + ● php-fpm.service - LSB: starts php-fpm + Loaded: loaded (/etc/init.d/php-fpm; bad; vendor preset: enabled) + Active: active (running) since Sun 2017-10-22 11:16:06 CST; 1 day 5h ago + Docs: man:systemd-sysv-generator(8) + CGroup: /system.slice/php-fpm.service + ├─ 7670 php-fpm: pool www + ├─ 7711 php-fpm: pool www + ├─ 7752 php-fpm: pool www + └─18244 php-fpm: master process (/usr/local/php-7.1.8/etc/php-fpm.conf) + + Oct 22 11:16:06 TinywanAliYun php-fpm[18232]: Stopping PHP-FPM Server ... . [OK] + Oct 22 11:16:06 TinywanAliYun systemd[1]: Stopped LSB: starts php-fpm. + Oct 22 11:16:06 TinywanAliYun systemd[1]: Starting LSB: starts php-fpm... + Oct 22 11:16:06 TinywanAliYun php-fpm[18239]: Starting PHP-FPM Server ... [OK] + Oct 22 11:16:06 TinywanAliYun systemd[1]: Started LSB: starts php-fpm. + ``` +### Nginx 服务 ++ [Debian/Ubuntu Nginx init Script](http://kbeezie.com/debian-ubuntu-nginx-init-script/) + + > [1]通常情况下,如果你从存储库安装Nginx,这个初始化脚本已经包含在内。但是,如果您从源代码安装,或者没有使用标准路径,您可能需要这个。 + [2]如果发现停止/重新启动等不起作用,则您的pid文件位置可能不正确。您可以将其设置在nginx.conf中,也可以在此处更改init脚本以指向正确的pid位置 + ++ `nginx.sh`代码: + + ```javascript + #!/bin/sh + + ### BEGIN INIT INFO + # Provides: nginx + # Required-Start: $all + # Required-Stop: $all + # Default-Start: 2 3 4 5 + # Default-Stop: 0 1 6 + # Short-Description: starts the nginx web server + # Description: starts nginx using start-stop-daemon + ### END INIT INFO + + PATH=/opt/bin:/opt/sbin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin + DAEMON=/usr/local/openresty/nginx/sbin/nginx + NAME=nginx + DESC=nginx + + test -x $DAEMON || exit 0 + + # Include nginx defaults if available + if [ -f /etc/default/nginx ] ; then + . /etc/default/nginx + fi + + set -e + + case "$1" in + start) + echo -n "Starting $DESC: " + start-stop-daemon --start --quiet --pidfile /var/run/nginx.pid \ + --exec $DAEMON -- $DAEMON_OPTS + echo "$NAME." + ;; + stop) + echo -n "Stopping $DESC: " + start-stop-daemon --stop --quiet --pidfile /var/run/nginx.pid \ + --exec $DAEMON + echo "$NAME." + ;; + restart|force-reload) + echo -n "Restarting $DESC: " + start-stop-daemon --stop --quiet --pidfile \ + /var/run/nginx.pid --exec $DAEMON + sleep 1 + start-stop-daemon --start --quiet --pidfile \ + /var/run/nginx.pid --exec $DAEMON -- $DAEMON_OPTS + echo "$NAME." + ;; + reload) + echo -n "Reloading $DESC configuration: " + start-stop-daemon --stop --signal HUP --quiet --pidfile /var/run/nginx.pid \ + --exec $DAEMON + echo "$NAME." + ;; + *) + N=/etc/init.d/$NAME + echo "Usage: $N {start|stop|restart|force-reload}" >&2 + exit 1 + ;; + esac + + exit 0 + ``` ++ CP到默认开启的服务脚本: + + ``` + sudo cp nginx.sh /etc/init.d/nginx + ``` ++ 给予权限: + + ``` + sudo chmod +x /etc/init.d/nginx + ``` ++ 设置为开机启动项: + + ``` + sudo update-rc.d nginx defaults + ``` ++ 重新加载服务: + + ``` + sudo systemctl daemon-reload + ``` ++ 开启服务 + + ```javascript + sudo systemctl start nginx.service + ``` ++ 停止服务 + + ```javascript + sudo systemctl stop nginx.service + ``` ++ 重启服务 + + ```javascript + sudo systemctl restart nginx.service + ``` ++ 服务状态 + + ```bash + sudo systemctl status nginx.service + ● nginx.service + Loaded: loaded (/etc/init.d/nginx; bad; vendor preset: enabled) + Active: active (running) since Mon 2017-10-23 16:48:24 CST; 1min 28s ago + Docs: man:systemd-sysv-generator(8) + Process: 19089 ExecStop=/etc/init.d/nginx stop (code=exited, status=0/SUCCESS) + Process: 19138 ExecStart=/etc/init.d/nginx start (code=exited, status=0/SUCCESS) + CGroup: /system.slice/nginx.service + ├─19142 nginx: master process /usr/local/openresty/nginx/sbin/nginx -c /usr/local/openresty/nginx/conf/nginx.con + ├─19143 nginx: worker process + └─19144 nginx: cache manager process + + Oct 23 16:48:24 TinywanAliYun systemd[1]: Starting nginx.service... + Oct 23 16:48:24 TinywanAliYun nginx[19138]: Starting NGINX Web Server: ok + Oct 23 16:48:24 TinywanAliYun systemd[1]: Started nginx.service. + ``` ++ 参考文章: + + [Nginx官方参考](https://www.nginx.com/resources/wiki/start/topics/tutorials/solaris_11/#startup-script) + + [linux wget 命令用法详解(附实例说明)](http://www.jb51.net/LINUXjishu/86326.html) + + [理解Linux系统/etc/init.d目录和/etc/rc.local脚本](http://blog.csdn.net/acs713/article/details/7322082) + + [Ubuntu启动项设置——之update-rc.d 命令使用](http://blog.csdn.net/typ2004/article/details/38712887) diff --git a/Openresty/Lua-file/ngx_timer_at_demo1.lua b/Openresty/Lua-file/ngx_timer_at_demo1.lua new file mode 100644 index 0000000..d0957f7 --- /dev/null +++ b/Openresty/Lua-file/ngx_timer_at_demo1.lua @@ -0,0 +1,18 @@ +local delay = 5 +local handler +handler = function (premature,param) + -- do some routine job in Lua just like a cron job + if premature then + return + end + + ngx.log(ngx.ERR, "param is : ", param) +end + +local ok, err = ngx.timer.at(delay, handler,"Hello Tinywan") + +--[[ + curl http://127.0.0.1/ngx_timer_at + + 2017/05/04 23:24:38 [error] 95933#0: *433016 [lua] get_timer_at.lua:9: param is : Hello Tinywan, context: ngx.timer, client: 127.0.0.1, server: 0.0.0.0:80 + --]] \ No newline at end of file diff --git a/Openresty/Lua-file/test01.lua b/Openresty/Lua-file/test01.lua new file mode 100644 index 0000000..9d9fe9c --- /dev/null +++ b/Openresty/Lua-file/test01.lua @@ -0,0 +1,39 @@ +local server = require "resty.websocket.server" +local wb, err = server:new{ + timeout = 5000, + max_payload_len = 65535 +} +if not wb then + ngx.log(ngx.ERR, "failed to new websocket: ", err) + return ngx.exit(444) +end +while true do + local data, typ, err = wb:recv_frame() + if wb.fatal then + ngx.log(ngx.ERR, "failed to receive frame: ", err) + return ngx.exit(444) + end + if not data then + local bytes, err = wb:send_ping() + if not bytes then + ngx.log(ngx.ERR, "failed to send ping: ", err) + return ngx.exit(444) + end + elseif typ == "close" then break + elseif typ == "ping" then + local bytes, err = wb:send_pong() + if not bytes then + ngx.log(ngx.ERR, "failed to send pong: ", err) + return ngx.exit(444) + end + elseif typ == "pong" then + ngx.log(ngx.INFO, "client ponged") + elseif typ == "text" then + local bytes, err = wb:send_text(data) + if not bytes then + ngx.log(ngx.ERR, "failed to send text: ", err) + return ngx.exit(444) + end + end +end +wb:send_close() \ No newline at end of file diff --git a/Openresty/Nginx/Lua/http-lua-test.lua b/Openresty/Nginx/Lua/http-lua-test.lua new file mode 100644 index 0000000..87a0bfb --- /dev/null +++ b/Openresty/Nginx/Lua/http-lua-test.lua @@ -0,0 +1,50 @@ +local http = require("resty.http") +--创建http客户端实例 +local httpc = http.new() + +local resp, err = httpc:request_uri("http://s.taobao.com", { + method = "GET", + path = "/search?q=hello", + headers = { + ["User-Agent"] = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.111 Safari/537.36" + } +}) + +if not resp then + ngx.say("request error :", err) + return +end + +--获取状态码 +ngx.status = resp.status + +--获取响应头 +for k, v in pairs(resp.headers) do + if k ~= "Transfer-Encoding" and k ~= "Connection" then + ngx.header[k] = v + end +end +--响应体 +ngx.say(resp.body) + +httpc:close() + + +local var = ngx.var +local uri = var.uri + +local stream_name = var.stream_name +local auth_key = var.auth_key +local expire_time = string.sub(auth_key,0,10) +local rand = "0" +local uid = "0" +local private_key = "Tinywan123" +local sstring = uri.."-"..expire_time.."-"..rand.."-"..uid.."-"..private_key +local hash_value = ngx.md5(sstring) + +ngx.say("URI :: ",uri) +ngx.say("stream_name::",stream_name) +ngx.say("auth_key ::",auth_key) +ngx.say("expire_time :: ",expire_time) +ngx.say("sstring :: ",sstring) +ngx.say("hash_value :: ",hash_value) diff --git a/Openresty/Nginx/Lua/m3u8_redis_access.lua b/Openresty/Nginx/Lua/m3u8_redis_access.lua new file mode 100644 index 0000000..b276971 --- /dev/null +++ b/Openresty/Nginx/Lua/m3u8_redis_access.lua @@ -0,0 +1,19 @@ +package.path = '/opt/openresty/nginx/lua/?.lua;' + +-- Get Nginx ConfigInformation +local get_args = ngx.req.get_uri_args() +local ws_secret = tostring(get_args["wsSecret"]) -- tel +local ws_time = tostring(get_args["wsTime"]) -- string + +-- Sign md5 + + +-- Redis Info +local redis = require("resty.redis_iresty") +local red = redis:new() + +local ok, err = red:set("AMI1999", "Redis is an animal 1999") +if not ok then + ngx.say("failed to set dog: ", err) + return +end diff --git a/Openresty/Nginx/Lua/resty/redis_iresty.lua b/Openresty/Nginx/Lua/resty/redis_iresty.lua new file mode 100644 index 0000000..d952558 --- /dev/null +++ b/Openresty/Nginx/Lua/resty/redis_iresty.lua @@ -0,0 +1,247 @@ +-- file name: resty/redis_iresty.lua +package.path = package.path ..'/opt/openresty/nginx/lua/resty/?.lua;/opt/openresty/lualib/?.lua'; +local redis_c = require "resty.redis" + + +local ok, new_tab = pcall(require, "table.new") +if not ok or type(new_tab) ~= "function" then + new_tab = function (narr, nrec) return {} end +end + + +local _M = new_tab(0, 155) +_M._VERSION = '0.01' + + +local commands = { + "append", "auth", "bgrewriteaof", + "bgsave", "bitcount", "bitop", + "blpop", "brpop", + "brpoplpush", "client", "config", + "dbsize", + "debug", "decr", "decrby", + "del", "discard", "dump", + "echo", + "eval", "exec", "exists", + "expire", "expireat", "flushall", + "flushdb", "get", "getbit", + "getrange", "getset", "hdel", + "hexists", "hget", "hgetall", + "hincrby", "hincrbyfloat", "hkeys", + "hlen", + "hmget", "hmset", "hscan", + "hset", + "hsetnx", "hvals", "incr", + "incrby", "incrbyfloat", "info", + "keys", + "lastsave", "lindex", "linsert", + "llen", "lpop", "lpush", + "lpushx", "lrange", "lrem", + "lset", "ltrim", "mget", + "migrate", + "monitor", "move", "mset", + "msetnx", "multi", "object", + "persist", "pexpire", "pexpireat", + "ping", "psetex", "psubscribe", + "pttl", + "publish", --[[ "punsubscribe", ]] "pubsub", + "quit", + "randomkey", "rename", "renamenx", + "restore", + "rpop", "rpoplpush", "rpush", + "rpushx", "sadd", "save", + "scan", "scard", "script", + "sdiff", "sdiffstore", + "select", "set", "setbit", + "setex", "setnx", "setrange", + "shutdown", "sinter", "sinterstore", + "sismember", "slaveof", "slowlog", + "smembers", "smove", "sort", + "spop", "srandmember", "srem", + "sscan", + "strlen", --[[ "subscribe", ]] "sunion", + "sunionstore", "sync", "time", + "ttl", + "type", --[[ "unsubscribe", ]] "unwatch", + "watch", "zadd", "zcard", + "zcount", "zincrby", "zinterstore", + "zrange", "zrangebyscore", "zrank", + "zrem", "zremrangebyrank", "zremrangebyscore", + "zrevrange", "zrevrangebyscore", "zrevrank", + "zscan", + "zscore", "zunionstore", "evalsha" +} + + +local mt = { __index = _M } + + +local function is_redis_null( res ) + if type(res) == "table" then + for k,v in pairs(res) do + if v ~= ngx.null then + return false + end + end + return true + elseif res == ngx.null then + return true + elseif res == nil then + return true + end + + return false +end + + +-- change connect address as you need +function _M.connect_mod( self, redis ) + redis:set_timeout(self.timeout) + return redis:connect("127.0.0.1", 6379) +end + + +function _M.set_keepalive_mod( redis ) + -- put it into the connection pool of size 100, with 60 seconds max idle time + return redis:set_keepalive(60000, 1000) +end + + +function _M.init_pipeline( self ) + self._reqs = {} +end + + +function _M.commit_pipeline( self ) + local reqs = self._reqs + + if nil == reqs or 0 == #reqs then + return {}, "no pipeline" + else + self._reqs = nil + end + + local redis, err = redis_c:new() + if not redis then + return nil, err + end + + local ok, err = self:connect_mod(redis) + if not ok then + return {}, err + end + + redis:init_pipeline() + for _, vals in ipairs(reqs) do + local fun = redis[vals[1]] + table.remove(vals , 1) + + fun(redis, unpack(vals)) + end + + local results, err = redis:commit_pipeline() + if not results or err then + return {}, err + end + + if is_redis_null(results) then + results = {} + ngx.log(ngx.WARN, "is null") + end + -- table.remove (results , 1) + + self.set_keepalive_mod(redis) + + for i,value in ipairs(results) do + if is_redis_null(value) then + results[i] = nil + end + end + + return results, err +end + + +function _M.subscribe( self, channel ) + local redis, err = redis_c:new() + if not redis then + return nil, err + end + + local ok, err = self:connect_mod(redis) + if not ok or err then + return nil, err + end + + local res, err = redis:subscribe(channel) + if not res then + return nil, err + end + + res, err = redis:read_reply() + if not res then + return nil, err + end + + redis:unsubscribe(channel) + self.set_keepalive_mod(redis) + + return res, err +end + + +local function do_command(self, cmd, ... ) + if self._reqs then + table.insert(self._reqs, {cmd, ...}) + return + end + + local redis, err = redis_c:new() + if not redis then + return nil, err + end + + local ok, err = self:connect_mod(redis) + if not ok or err then + return nil, err + end + + local fun = redis[cmd] + local result, err = fun(redis, ...) + if not result or err then + -- ngx.log(ngx.ERR, "pipeline result:", result, " err:", err) + return nil, err + end + + if is_redis_null(result) then + result = nil + end + + self.set_keepalive_mod(redis) + + return result, err +end + + +for i = 1, #commands do + local cmd = commands[i] + _M[cmd] = + function (self, ...) + return do_command(self, cmd, ...) + end +end + + +function _M.new(self, opts) + opts = opts or {} + local timeout = (opts.timeout and opts.timeout * 1000) or 1000 + local db_index= opts.db_index or 0 + + return setmetatable({ + timeout = timeout, + db_index = db_index, + _reqs = nil }, mt) +end + + +return _M \ No newline at end of file diff --git "a/Openresty/Openresty\347\274\226\347\250\213.pdf" "b/Openresty/Openresty\347\274\226\347\250\213.pdf" new file mode 100644 index 0000000..7748a3c Binary files /dev/null and "b/Openresty/Openresty\347\274\226\347\250\213.pdf" differ diff --git a/Openresty/default-config.md b/Openresty/default-config.md new file mode 100644 index 0000000..54fc357 --- /dev/null +++ b/Openresty/default-config.md @@ -0,0 +1,64 @@ +## dasd + +## 安装配置信息 + ++ 配置信息 + +``` +./configure \ +--prefix=/opt/openresty \ +--with-debug \ +--with-lua51 \ +--with-luajit \ +--without-http_redis2_module \ +--with-http_iconv_module \ +--with-stream \ +--with-http_stub_status_module \ +--with-http_xslt_module \ +--with-stream_ssl_module \ +--with-http_realip_module \ +--with-http_ssl_module \ +--add-module=../stream-lua-nginx-module\ +--add-module=../nginx-rtmp-module-1.1.11/ + +``` ++ 默认安装的模块为 +``` +--prefix=/opt/openresty/nginx +--with-cc-opt=-O2 -- 默认安装 +--add-module=../ngx_devel_kit-0.3.0 -- 默认安装 +--add-module=../iconv-nginx-module-0.14 -- NO +--add-module=../echo-nginx-module-0.60 -- 默认安装 +--add-module=../xss-nginx-module-0.05 -- 默认安装 +--add-module=../ngx_coolkit-0.2rc3 -- 默认安装 +--add-module=../set-misc-nginx-module-0.31 +--add-module=../form-input-nginx-module-0.12 +--add-module=../encrypted-session-nginx-module-0.06 +--add-module=../srcache-nginx-module-0.31 +--add-module=../ngx_lua-0.10.6 +--add-module=../ngx_lua_upstream-0.06 +--add-module=../headers-more-nginx-module-0.31 +--add-module=../array-var-nginx-module-0.05 +--add-module=../memc-nginx-module-0.17 +--add-module=../redis-nginx-module-0.3.7 +--add-module=../rds-json-nginx-module-0.14 +--add-module=../rds-csv-nginx-module-0.07 +--with-ld-opt=-Wl,-rpath,/opt/openresty/luajit/lib +--with-stream +--with-stream_ssl_module +--with-http_ssl_module +--add-module=/home/tinywan/openresty-1.11.2.1/../stream-lua-nginx-module +``` + +#### Nginx.conf 配置选项详解 ++ `--with-http_realip_module` 选项 + > 通过这个模块允许我们改变客户端请求头中客户端IP地址值(例如,X-Real-IP 或 X-Forwarded-For) + + > 配置示例 + + ``` + set_real_ip_from 192.168.1.0/24; + set_real_ip_from 192.168.2.1; + real_ip_header X-Real-IP; + ``` + > [--with-http_realip_module 选项(后台Nginx服务器记录原始客户端的IP地址 )](http://blog.csdn.net/cscrazybing/article/details/50789234) diff --git a/Openresty/lua-cjson/cjson-str-obj.lua b/Openresty/lua-cjson/cjson-str-obj.lua new file mode 100644 index 0000000..a41fb29 --- /dev/null +++ b/Openresty/lua-cjson/cjson-str-obj.lua @@ -0,0 +1,81 @@ +local cjson = require("cjson") + +--lua对象到字符串 +local obj = { + id = 1, + name = "zhangsan", + age = nil, + is_male = false, + hobby = {"film", "music", "read"} +} + +local str = cjson.encode(obj) +ngx.say(str, "
") -- 打印输出:{"hobby":["film","music","read"],"is_male":false,"name":"zhangsan","id":1}
+ +--字符串到lua对象 +str = '{ + "hobby":["film","music","read"],"is_male":false,"name":"zhangsan","id":1,"age":null}' +local obj = cjson.decode(str) -- 字符串转换成一个对象 + +ngx.say(obj.age, "
") -- 对象访问形式:obs.age +ngx.say(obj.age == nil, "
") -- 数组访问形式:hobby[1] +ngx.say(obj.age == cjson.null, "
") +ngx.say(obj.hobby[1], "
") + +--obj.obj +str_obj = '{"hobby":{"name":"tinywan","age":24,"reader":"AMAI"},"is_male":false}' +local obj_obj = cjson.decode(str_obj) + +ngx.say(obj_obj.is_male, "
") +ngx.say(obj_obj.hobby.name, "
") +ngx.say(obj_obj.hobby.age, "
") +ngx.say(obj_obj.hobby.reader, "
") + + +--循环引用 +obj = { + id = 1 +} +obj.obj = obj +-- Cannot serialise, excessive nesting +--ngx.say(cjson.encode(obj), "
") +local cjson_safe = require("cjson.safe") +--nil +ngx.say(cjson_safe.encode(obj), "
") + + +--[[ +打印结果: + root@tinywan:/opt/openresty/nginx/conf/Lua# curl http://127.0.0.1/dcjson + {"hobby":["film","music","read"],"is_male":false,"name":"zhangsan","id":1}
+ nil + null
+ false
+ true
+ film
+ false
+ tinywan
+ 24
+ AMAI
+ nil
+ +经验分享: +[1] lua对象到字符串 ,通过cjson.encode(obj)把所有的对象转换成了json格式,对象中的对象,被转换成了数组(数组作为 JSON 对象) +[2] JSON 对象中的数组 + { + "name":"网站", + "num":3, + "sites":[ "Google", "Runoob", "Taobao" ] + } +[3] 嵌套 JSON 对象 + myObj = { + "name":"runoob", + "alexa":10000, + "sites": { + "site1":"www.runoob.com", + "site2":"m.runoob.com", + "site3":"c.runoob.com" + } + } + +--]] \ No newline at end of file diff --git a/Openresty/lua-common-package/lua-require.md b/Openresty/lua-common-package/lua-require.md new file mode 100644 index 0000000..874dc4d --- /dev/null +++ b/Openresty/lua-common-package/lua-require.md @@ -0,0 +1,516 @@ +![Markdown](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Images/nginx-hls-locations.png) +### 整体目录 ++ 整体目录 + ``` + . + ├── conf + │ ├── nginx.conf -- Nginx 配置文件 + ├── logs + │ ├── error.log -- Nginx 错误日子 + │ └── nginx.pid + ├── lua + │ ├── access_check.lua -- 权限验证文件 + │ ├── business_redis.lua -- 业务 Redis 处理文件 + │ ├── redis.lua -- 直接使用Lua的redis以及别的函数库 + │ ├── ... + │ └── resty -- 存放Lua 的所有公共、封装好的库 + │ └── redis_iresty.lua -- Redis 接口的二次封装 + │ └── param.lua -- 参数过滤库 + └── sbin + └── nginx + ``` ++ [Lua require 相对路径 博客园详解](http://www.cnblogs.com/smallboat/p/5552407.html) +#### 相对路径总结(相对于nginx安装目录) ++ **当前目录** :`/opt/openresty/nginx/conf/Lua` + + 注意:如果在当前没有了,以下的代码运行是没有问题的 + + 就是所有的lua脚本代码全部写在Lua文件夹下面去 + + 拼接路径:`package.path = package.path ..';..\\?.lua';` + + require 直接这样应用就是`Lua.functions` ++ 目录结构 + ``` + root@tinywan:/opt/openresty/nginx/conf/Lua# pwd + /opt/openresty/nginx/conf/Lua + root@tinywan:/opt/openresty/nginx/conf/Lua# ls + cjosn01.lua functions.lua lua-01.lua main.lua + ``` ++ 可以看出在Lua文件夹下面有多个Lua文件,我们拿main.lua和functions.lua 文件来测试包的进入 ++ 规则 在main.lua中引入functions.lua 文件 ++ main.lua 文件内容 + ``` + package.path = package.path ..';..\\?.lua'; + require("Lua.functions") + ngx.say("Hello") + ngx.say(MathFun.Add(12,90)) + ``` ++ functions.lua 文件内容 + ``` + -- 全局的表package + MathFun = {} + -- 以下把所有的函数放在这个全局表中 + -- + + function MathFun.Add(a,b) + return a+b + end + + -- - + function MathFun.Minus(a,b) + return a-b + end + + -- * + function MathFun.Multi(a,b) + return a*b + end + -- / + function MathFun.Div(a,b) + return a/b + end + ``` ++ nginx.conf 配置文件 + ``` + location /api { + lua_code_cache off; + content_by_lua_file /opt/openresty/nginx/conf/Lua/main.lua; # 绝对路径 + #content_by_lua_file conf/Lua/main.lua; # 相对路径(相对于nginx安装目录) + } + ``` ++ CURL 请求结果 + ``` + root@tinywan:/opt/openresty/nginx/conf/Lua# curl '127.0.0.1:80/api' + Hello + 102 + ``` +#### 绝对路径总结 ++ 目录结果:`/opt/openresty/nginx/lua/resty` ++ 包路径:`package.path = package.path ..'/opt/openresty/nginx/lua/resty/?.lua;/opt/openresty/lualib/?.lua';` ++ 加载绝对路径的Redis的二次封装库,redis_iresty.lua文件: + ``` + package.path = package.path ..'/opt/openresty/nginx/lua/resty/?.lua;/opt/openresty/lualib/?.lua'; + local redis_c = require "resty.redis" + local ok, new_tab = pcall(require, "table.new") + if not ok or type(new_tab) ~= "function" then + new_tab = function (narr, nrec) return {} end + end + ``` ++ Redis的二次封装库的应用,test_redis2.lua文件: + ``` + package.path = '/opt/openresty/nginx/lua/?.lua;' -- 注意:这个最好是修改掉最好 + local redis = require("resty.redis_iresty") + local red = redis:new() + local ok, err = red:set("Redis1999", "Redis is an animal 1999") + if not ok then + ngx.say("failed to set dog: ", err) + return + end + ``` ++ nginx.conf 配置文件(注意以下的目录路径和上面的不一样) + ``` + location /api { + lua_code_cache off; + content_by_lua_file /opt/openresty/nginx/lua/test_redis2.lua; + } + ``` ++ CURL 请求结果 + ``` + root@tinywan:/opt/openresty/nginx/conf/Lua# curl '127.0.0.1:80/api' + Hello + 102 + ``` + + Redis 数据库结果 + ``` + 127.0.0.1:6379> keys * + 1) "dog" + 2) "Redis2222222222" + 3) "Redis1999" + ``` +#### 项目实战目录介绍 ++ 公共配置nginx.conf `(/opt/openresty/nginx/conf/nginx.conf)` + ```Lua + worker_processes 2; + error_log logs/error.log; + pid logs/nginx.pid; + + events { + use epoll; + worker_connections 1024; + } + + http { + include mime.types; + default_type text/html; + #lua模块路径,其中”;;”表示默认搜索路径,默认到/usr/servers/nginx下找 + lua_package_path "/home/tinywan/Openresty_Protect/First_Protect/lualib/?.lua;;"; #lua 模块 + lua_package_cpath "/home/tinywan/Openresty_Protect/First_Protect/lualib/?.so;;"; #c模块 + include /home/tinywan/Openresty_Protect/First_Protect/nginx_first.conf; + } + ``` ++ 项目结构图 + ```Lua + tinywan@tinywan:~/Openresty_Protect$ pwd + /home/tinywan/Openresty_Protect + tinywan@tinywan:~/Openresty_Protect$ tree -L 3 + . + └── First_Protect + ├── lua + │   ├── functions.lua + │   ├── get_redis_iresty.lua + │   ├── get_redis.lua + │   ├── main.lua + │   └── test.lua + ├── lualib + │   ├── cjson.so + │   ├── ngx + │   ├── rds + │   ├── redis + │   └── resty + └── nginx_first.conf + ``` +##### [1]单独项目配置文件 ++ 在这里我的所有项目放在`/home/tinywan/Openresty_Protect` 这个目录下面 ++ 这里我们拿第一个项目做测试,项目名称:`First_Protect` + ++ nginx_first.conf 配置文件 + ```Lua + server { + listen 80; + server_name _; + + location /lua { + default_type 'text/html'; + lua_code_cache off; + content_by_lua_file /home/tinywan/Openresty_Protect/First_Protect/lua/test.lua; + } + } + ``` ++ test.lua 文件内容:`ngx.say("Hello! First_Protect")` ++ CURL 请求测试结果: + ```Lua + tinywan@tinywan:/opt/openresty/nginx/conf$ curl http://127.0.0.1/lua + Hello! First_Protect + ``` +##### [2]测试引入官方库文件 ++ get_redis.lua + ```Lua + local json = require("cjson") + local redis = require("resty.redis") + local red = redis:new() + local get_args = ngx.req.get_uri_args() + + 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 key = get_args["name"] + local new_timer = ngx.localtime() + local value = key.."::"..new_timer + local ok , err = red:set(key,value) + if not ok then + ngx.say("failed to set "..key, err) + return + end + ngx.say("set result: ", ok) + + local res, err = red:get(key) + if not res then + ngx.say("get from redis error : ", err) + return + end + if res == ngx.null then + ngx.say(key.."not found.") + return + end + red:close() + ngx.say("First_Protect Success get Redis Data",json.encode({content=res})) + ``` ++ nginx_first.conf 配置文件 + ```Lua + server { + listen 80; + server_name _; + + location /lua { + default_type 'text/html'; + lua_code_cache off; + content_by_lua_file /home/tinywan/Openresty_Protect/First_Protect/lua/get_redis.lua; + } + } + ``` ++ CURL 请求测试结果: + ```Lua + tinywan@$ curl -G -d "name=Tinywan_github" http://127.0.0.1/lua_get_redis + set result: OK + First_Protect Success get Redis Data{"content":"Tinywan_github::2017-04-23 19:53:24"} + ``` +##### [3]自己调用自己分装好的文件 ++ Redis 接口的二次封装 redis_iresty.lua (路径:`/home/tinywan/Openresty_Protect/First_Protect/lualib/resty`) + ```Lua + -- file name: resty/redis_iresty.lua + local redis_c = require "resty.redis" + + local ok, new_tab = pcall(require, "table.new") + if not ok or type(new_tab) ~= "function" then + new_tab = function (narr, nrec) return {} end + end + + local _M = new_tab(0, 155) + _M._VERSION = '0.01' + + local commands = { + "append", "auth", "bgrewriteaof", + "bgsave", "bitcount", "bitop", + "blpop", "brpop", + "brpoplpush", "client", "config", + "dbsize", + "debug", "decr", "decrby", + "del", "discard", "dump", + "echo", + "eval", "exec", "exists", + "expire", "expireat", "flushall", + "flushdb", "get", "getbit", + "getrange", "getset", "hdel", + "hexists", "hget", "hgetall", + "hincrby", "hincrbyfloat", "hkeys", + "hlen", + "hmget", "hmset", "hscan", + "hset", + "hsetnx", "hvals", "incr", + "incrby", "incrbyfloat", "info", + "keys", + "lastsave", "lindex", "linsert", + "llen", "lpop", "lpush", + "lpushx", "lrange", "lrem", + "lset", "ltrim", "mget", + "migrate", + "monitor", "move", "mset", + "msetnx", "multi", "object", + "persist", "pexpire", "pexpireat", + "ping", "psetex", "psubscribe", + "pttl", + "publish", --[[ "punsubscribe", ]] "pubsub", + "quit", + "randomkey", "rename", "renamenx", + "restore", + "rpop", "rpoplpush", "rpush", + "rpushx", "sadd", "save", + "scan", "scard", "script", + "sdiff", "sdiffstore", + "select", "set", "setbit", + "setex", "setnx", "setrange", + "shutdown", "sinter", "sinterstore", + "sismember", "slaveof", "slowlog", + "smembers", "smove", "sort", + "spop", "srandmember", "srem", + "sscan", + "strlen", --[[ "subscribe", ]] "sunion", + "sunionstore", "sync", "time", + "ttl", + "type", --[[ "unsubscribe", ]] "unwatch", + "watch", "zadd", "zcard", + "zcount", "zincrby", "zinterstore", + "zrange", "zrangebyscore", "zrank", + "zrem", "zremrangebyrank", "zremrangebyscore", + "zrevrange", "zrevrangebyscore", "zrevrank", + "zscan", + "zscore", "zunionstore", "evalsha" + } + + local mt = { __index = _M } + + local function is_redis_null( res ) + if type(res) == "table" then + for k,v in pairs(res) do + if v ~= ngx.null then + return false + end + end + return true + elseif res == ngx.null then + return true + elseif res == nil then + return true + end + + return false + end + + -- change connect address as you need + function _M.connect_mod( self, redis ) + redis:set_timeout(self.timeout) + return redis:connect("127.0.0.1", 6379) + end + + function _M.set_keepalive_mod( redis ) + -- put it into the connection pool of size 100, with 60 seconds max idle time + return redis:set_keepalive(60000, 1000) + end + + function _M.init_pipeline( self ) + self._reqs = {} + end + + function _M.commit_pipeline( self ) + local reqs = self._reqs + + if nil == reqs or 0 == #reqs then + return {}, "no pipeline" + else + self._reqs = nil + end + + local redis, err = redis_c:new() + if not redis then + return nil, err + end + + local ok, err = self:connect_mod(redis) + if not ok then + return {}, err + end + + redis:init_pipeline() + for _, vals in ipairs(reqs) do + local fun = redis[vals[1]] + table.remove(vals , 1) + + fun(redis, unpack(vals)) + end + + local results, err = redis:commit_pipeline() + if not results or err then + return {}, err + end + + if is_redis_null(results) then + results = {} + ngx.log(ngx.WARN, "is null") + end + -- table.remove (results , 1) + + self.set_keepalive_mod(redis) + + for i,value in ipairs(results) do + if is_redis_null(value) then + results[i] = nil + end + end + + return results, err + end + + function _M.subscribe( self, channel ) + local redis, err = redis_c:new() + if not redis then + return nil, err + end + + local ok, err = self:connect_mod(redis) + if not ok or err then + return nil, err + end + + local res, err = redis:subscribe(channel) + if not res then + return nil, err + end + + res, err = redis:read_reply() + if not res then + return nil, err + end + + redis:unsubscribe(channel) + self.set_keepalive_mod(redis) + + return res, err + end + + local function do_command(self, cmd, ... ) + if self._reqs then + table.insert(self._reqs, {cmd, ...}) + return + end + + local redis, err = redis_c:new() + if not redis then + return nil, err + end + + local ok, err = self:connect_mod(redis) + if not ok or err then + return nil, err + end + + local fun = redis[cmd] + local result, err = fun(redis, ...) + if not result or err then + -- ngx.log(ngx.ERR, "pipeline result:", result, " err:", err) + return nil, err + end + + if is_redis_null(result) then + result = nil + end + + self.set_keepalive_mod(redis) + + return result, err + end + + for i = 1, #commands do + local cmd = commands[i] + _M[cmd] = + function (self, ...) + return do_command(self, cmd, ...) + end + end + + function _M.new(self, opts) + opts = opts or {} + local timeout = (opts.timeout and opts.timeout * 1000) or 1000 + local db_index= opts.db_index or 0 + + return setmetatable({ + timeout = timeout, + db_index = db_index, + _reqs = nil }, mt) + end + + return _M + ``` ++ nginx_first.conf 配置文件 + ```Lua + server { + listen 80; + server_name _; + + location /get_redis_iresty { + default_type 'text/html'; + lua_code_cache off; + content_by_lua_file /home/tinywan/Openresty_Protect/First_Protect/lua/get_redis_iresty.lua; + } + } + ``` ++ 调用示例代码 get_redis_iresty.lua: + ```Lua + local redis = require "resty.redis_iresty" + local red = redis:new() + + local ok, err = red:set("TinyAiAI", "an Boby") + if not ok then + ngx.say("failed to set: ", err) + return + end + + ngx.say("set result: ", ok) + ``` ++ CURL 请求测试结果: + ```Lua + tinywan@$ curl http://127.0.0.1/get_redis_iresty + set result: OK + ``` \ No newline at end of file diff --git a/Openresty/lua-get-start/lua_request.lua b/Openresty/lua-get-start/lua_request.lua new file mode 100644 index 0000000..ff277f4 --- /dev/null +++ b/Openresty/lua-get-start/lua_request.lua @@ -0,0 +1,97 @@ +--接受nginx变量 +local var = ngx.var +ngx.say("ngx.var.a : ", var.a, "
") +ngx.say("ngx.var.b : ", var.b, "
") +ngx.say("ngx.var[2] : ", var[2], "
") +ngx.var.b = 2; + +ngx.say("
") + +--请求头 +local headers = ngx.req.get_headers() +ngx.say("headers begin", "
") +ngx.say("Host : ", headers["Host"], "
") +ngx.say("user-agent : ", headers["user-agent"], "
") +ngx.say("user-agent : ", headers.user_agent, "
") +ngx.say("user-agent : ", ngx.req.get_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 +ngx.say("headers end", "
") +ngx.say("
") + +--get请求uri参数 +ngx.say("uri args begin", "
") +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 +ngx.say("uri args end", "
") +ngx.say("
") + +--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 +ngx.say("post args end", "
") +ngx.say("
") + +--请求的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(), "
") +ngx.say("
") + +--[[ +执行结果: +root@tinywan:/opt/openresty/nginx/conf# curl http://127.0.0.1/lua_request/123/890 +ngx.var.a : 123
+ngx.var.b : 127.0.0.1
+ngx.var[2] : 890
+
+headers begin
+Host : 127.0.0.1
+user-agent : curl/7.47.0
+user-agent : curl/7.47.0
+host : 127.0.0.1
+accept : */*
+user-agent : curl/7.47.0
+headers end
+
+uri args begin
+uri args end
+
+post args begin
+post args end
+
+ngx.req.http_version : 1.1
+ngx.req.get_method : GET
+ngx.req.raw_header : GET /lua_request/123/890 HTTP/1.1 +Host: 127.0.0.1 +User-Agent: curl/7.47.0 +Accept: */* + +
+ngx.req.get_body_data() : nil
+
+ngx.var.b 2 +--]] \ No newline at end of file diff --git a/Openresty/lua-get-start/lua_request_01_location.conf b/Openresty/lua-get-start/lua_request_01_location.conf new file mode 100644 index 0000000..27bacf9 --- /dev/null +++ b/Openresty/lua-get-start/lua_request_01_location.conf @@ -0,0 +1,39 @@ +####################################################################################################### +## 【01】 +####################################################################################################### +location ~ /lua_request/(\d+)/(\d+) # 正则匹配 +{ + #设置nginx变量 (rewrite pharse) + set $a $1; + set $b $2; + set $c $host; + echo $a; + echo $b; + echo $c; +} +################################################ 访问结果为以下 +root@tinywan:/opt/openresty/nginx/conf# curl http://127.0.0.1/lua_request/123/890 +123 +890 +127.0.0.1 +### 总结: + +####################################################################################################### +## 【02】 +####################################################################################################### +location ~ /lua_request/(\d+)/(\d+) # 正则匹配 +{ + #设置nginx变量 (rewrite pharse) + set $a $1; + set $b $2; + set $c $host; + echo $a; + echo $b; + echo $c; +} +################################################ 访问结果为以下 +root@tinywan:/opt/openresty/nginx/conf# curl http://127.0.0.1/lua_request/123/890 +123 +890 +127.0.0.1 + diff --git a/Openresty/lua-get-start/request-nginx.conf b/Openresty/lua-get-start/request-nginx.conf new file mode 100644 index 0000000..3f9cd8d --- /dev/null +++ b/Openresty/lua-get-start/request-nginx.conf @@ -0,0 +1,10 @@ +location ~ /lua_request/(\d+)/(\d+) { + #设置nginx变量 + set $a $1; + set $b $host; + default_type "text/html"; + #nginx内容处理 + content_by_lua_file /mnt/hgfs/Linux-Share/Lua/lua_request.lua ; + #内容体处理完成后调用 + echo_after_body "ngx.var.b $b"; +} \ No newline at end of file diff --git a/Openresty/lua-resty-http/lua-http-test.lua b/Openresty/lua-resty-http/lua-http-test.lua new file mode 100644 index 0000000..7ddeb41 --- /dev/null +++ b/Openresty/lua-resty-http/lua-http-test.lua @@ -0,0 +1,21 @@ +local http = require("resty.http") +local httpc = http.new() +local resp, err = httpc:request_uri("http://s.taobao.com", { + method = "GET", + path = "/search?q=hello", + headers = { + ["User-Agent"] = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.111 Safari/537.36" + } +}) +if not resp then + ngx.say("request error :", err) + return +end +ngx.status = resp.status +for k, v in pairs(resp.headers) do + if k ~= "Transfer-Encoding" and k ~= "Connection" then + ngx.header[k] = v + end +end +ngx.say(resp.body) +httpc:close() \ No newline at end of file diff --git a/Openresty/lua-resty-mysql/lua-msyql-test.lua b/Openresty/lua-resty-mysql/lua-msyql-test.lua new file mode 100644 index 0000000..809d7fe --- /dev/null +++ b/Openresty/lua-resty-mysql/lua-msyql-test.lua @@ -0,0 +1,88 @@ +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 + +-- or connect to a unix domain socket file listened +-- by a mysql server: +-- local ok, err, errno, sqlstate = +-- db:connect{ +-- path = "/path/to/mysql.sock", +-- database = "ngx_test", +-- user = "ngx_test", +-- password = "ngx_test" } + +local ok, err, errno, sqlstate = db:connect{ + host = "127.0.0.1", + port = 3306, + database = "mysql", + user = "root", + password = "123456", + max_packet_size = 1024 * 1024 } + +if not ok then + ngx.say("failed to connect: ", err, ": ", errno, " ", sqlstate) + return +end + +ngx.say("connected to mysql.") + +local res, err, errno, sqlstate = + db:query("drop table if exists cats") +if not res then + ngx.say("bad result: ", err, ": ", errno, ": ", sqlstate, ".") + return +end + +res, err, errno, sqlstate = + db:query("create table cats " + .. "(id serial primary key, " + .. "name varchar(5))") +if not res then + ngx.say("bad result: ", err, ": ", errno, ": ", sqlstate, ".") + return +end + +ngx.say("table cats created.") + +res, err, errno, sqlstate = + db:query("insert into cats (name) " + .. "values (\'Bob1\'),(\'111\'),(null)") +if not res then + ngx.say("bad result: ", err, ": ", errno, ": ", sqlstate, ".") + return +end + +ngx.say(res.affected_rows, " rows inserted into table cats ", + "(last insert id: ", res.insert_id, ")") + +-- run a select query, expected about 10 rows in +-- the result set: +res, err, errno, sqlstate = + db:query("select * from cats order by id asc", 10) +if not res then + ngx.say("bad result: ", err, ": ", errno, ": ", sqlstate, ".") + return +end + +local cjson = require "cjson" +ngx.say("result: ", cjson.encode(res)) + +-- put it into the connection pool of size 100, +-- with 10 seconds max idle timeout +local ok, err = db:set_keepalive(10000, 100) +if not ok then + ngx.say("failed to set keepalive: ", err) + return +end + +-- or just close the connection right away: +-- local ok, err = db:close() +-- if not ok then +-- ngx.say("failed to close: ", err) +-- return +-- end diff --git a/Openresty/lua-resty-redis/HLS-Access-Redis.lua b/Openresty/lua-resty-redis/HLS-Access-Redis.lua new file mode 100644 index 0000000..f74546d --- /dev/null +++ b/Openresty/lua-resty-redis/HLS-Access-Redis.lua @@ -0,0 +1,17 @@ +-- Get Nginx ConfigInformation +local get_args = ngx.req.get_uri_args() +local ws_secret = tostring(get_args["wsSecret"]) -- tel +local ws_time = tostring(get_args["wsTime"]) -- string + +-- Sign md5 + + +-- Redis Info +local redis = require("resty.redis_iresty") +local red = redis:new() + +local ok, err = red:set("AMI1999", "Redis is an animal 1999") +if not ok then + ngx.say("failed to set dog: ", err) + return +end diff --git a/Openresty/lua-resty-redis/Hls-Line-Number-Total.lua b/Openresty/lua-resty-redis/Hls-Line-Number-Total.lua new file mode 100644 index 0000000..b0070a7 --- /dev/null +++ b/Openresty/lua-resty-redis/Hls-Line-Number-Total.lua @@ -0,0 +1,51 @@ +local function close_redis(redis_instance) + if not redis_instance then + return + end + + --释放连接(连接池实现) + local pool_max_idle_time = 10000 --毫秒 + local pool_size = 100 --连接池大小 + local ok, err = redis_instance:set_keepalive(pool_max_idle_time, pool_size) + if not ok then + ngx.say("set keepalive error : ", err) + end +end + +-- 接受Nginx传递进来的参数$1 也就是SteamName +local stream_name = ngx.var.stream_name +local total = ngx.var.total + +local redis = require("resty.redis"); +-- 创建一个redis对象实例。在失败,返回nil和描述错误的字符串的情况下 +local redis_instance = redis:new(); +--设置后续操作的超时(以毫秒为单位)保护,包括connect方法 +redis_instance:set_timeout(1000) +--建立连接 +local ip = '127.0.0.1' +local port = 6379 +--尝试连接到redis服务器正在侦听的远程主机和端口 +local ok,err = redis_instance:connect(ip,port) +if not ok then + ngx.say("connect redis error : ",err) + return err +end + + +--数据库选择 +--redis_instance:select(2) + +--调用API获取数据 +local resp, err = redis_instance:incr("NUMBERS:"..stream_name) +if not resp then + ngx.say("get msg error : ", err) + return err +end + +--得到的数据为空处理 +if resp == ngx.null then + ngx.say("this is not redis_data") --比如默认值 + return nil +end +ngx.var.total_numbers = resp +close_redis(redis_instance) diff --git a/Openresty/lua-resty-redis/Nginx+Lua+Redis+Mysql.conf b/Openresty/lua-resty-redis/Nginx+Lua+Redis+Mysql.conf new file mode 100644 index 0000000..d554235 --- /dev/null +++ b/Openresty/lua-resty-redis/Nginx+Lua+Redis+Mysql.conf @@ -0,0 +1,36 @@ + +upstream backend { + server 127.0.0.1:8080 max_fails=5 fail_timeout=10s weight=1; + server 127.0.0.1:8090 max_fails=5 fail_timeout=10s weight=1; + check interval=3000 rise=1 fall=2 timeout=5000 type=tcp default_down=false; + keepalive 100; +} + +server { + listen 80; + server_name _; + + location ~ ^/ad/(\d+)$ { + default_type 'text/html'; + charset utf-8; + lua_code_cache on; + set $id $1; + content_by_lua_file /usr/chapter6/ad.lua; + } + + location ~ /backend/(.*) { + internal; + keepalive_timeout 30s; + keepalive_requests 1000; + #支持keep-alive + proxy_http_version 1.1; + proxy_set_header Connection ""; + + rewrite /backend(/.*) $1 break; + proxy_pass_request_headers off; + #more_clear_input_headers Accept-Encoding; + proxy_next_upstream error timeout; + proxy_pass http://backend; + } +} + diff --git a/Openresty/lua-resty-redis/Nginx+Lua+Redis+Mysql.lua b/Openresty/lua-resty-redis/Nginx+Lua+Redis+Mysql.lua new file mode 100644 index 0000000..c566a2b --- /dev/null +++ b/Openresty/lua-resty-redis/Nginx+Lua+Redis+Mysql.lua @@ -0,0 +1,92 @@ +local redis = require("resty.redis") +local cjson = require("cjson") +local cjson_encode = cjson.encode +local ngx_log = ngx.log +local ngx_ERR = ngx.ERR +local ngx_exit = ngx.exit +local ngx_print = ngx.print +local ngx_re_match = ngx.re.match +local ngx_var = ngx.var + + +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) + + if not ok then + ngx_log(ngx_ERR, "set redis keepalive error : ", err) + end +end +local function read_redis(id) + local red = redis:new() + red:set_timeout(1000) + local ip = "127.0.0.1" + local port = 1111 + local ok, err = red:connect(ip, port) + if not ok then + ngx_log(ngx_ERR, "connect to redis error : ", err) + return close_redis(red) + end + + local resp, err = red:get(id) + if not resp then + ngx_log(ngx_ERR, "get redis content error : ", err) + return close_redis(red) + end + + --得到的数据为空处理 + if resp == ngx.null then + resp = nil + end + close_redis(red) + + return resp +end + +local function read_http(id) + local resp = ngx.location.capture("/backend/ad", { + method = ngx.HTTP_GET, + args = {id = id} + }) + + if not resp then + ngx_log(ngx_ERR, "request error :", err) + return + end + + if resp.status ~= 200 then + ngx_log(ngx_ERR, "request error, status :", resp.status) + return + end + + return resp.body +end + + +--获取id +local id = ngx_var.id + +--从redis获取 +local content = read_redis(id) + +--如果redis没有,回源到tomcat +if not content then + ngx_log(ngx_ERR, "redis not found content, back to http, id : ", id) + content = read_http(id) +end + +--如果还没有返回404 +if not content then + ngx_log(ngx_ERR, "http not found content, id : ", id) + return ngx_exit(404) +end + +--输出内容 +ngx.print("show_ad(") +ngx_print(cjson_encode({content = content})) +ngx.print(")") diff --git a/Openresty/lua-resty-redis/proxy_pass_cdn.lua b/Openresty/lua-resty-redis/proxy_pass_cdn.lua new file mode 100644 index 0000000..9567f78 --- /dev/null +++ b/Openresty/lua-resty-redis/proxy_pass_cdn.lua @@ -0,0 +1,81 @@ +local function close_redis(redis_instance) + if not redis_instance then + return + end + -- local ok, err = redis_instance:close() + -- if not ok then + -- ngx.say("close redis error : ", err) + -- end + + --释放连接(连接池实现) + local pool_max_idle_time = 10000 --毫秒 + local pool_size = 100 --连接池大小 + local ok, err = redis_instance:set_keepalive(pool_max_idle_time, pool_size) + if not ok then + ngx.say("set keepalive error : ", err) + end +end + +-- 接受Nginx传递进来的参数$1 也就是SteamName +local stream_a = ngx.var.a + +local redis = require("resty.redis"); +-- 创建一个redis对象实例。在失败,返回nil和描述错误的字符串的情况下 +local redis_instance = redis:new(); +--设置后续操作的超时(以毫秒为单位)保护,包括connect方法 +redis_instance:set_timeout(1000) +--建立连接 +local ip = '127.0.0.1' +local port = 6379 +--尝试连接到redis服务器正在侦听的远程主机和端口 +local ok,err = redis_instance:connect(ip,port) +if not ok then + ngx.say("connect redis error : ",err) + return err +end + +-- -- 权限验证 +-- local res,err = redis_instance:auth('pass_tinywan') +-- if not res then +-- ngx.say("failed to authenticate: ", err) +-- return +-- end + +-- 权限验证 请注意这里 auth 的调用过程 +local count +--[[ + 如果当前连接不是从内建连接池中获取的,该方法总是返回 0 ,也就是说,该连接还没有被使用过。如果连接来自连接池,那么返回值永远都是非零。所以这个方法可以用来确认当前连接是否来自池子 + 对于 Redis 授权,实际上只需要建立连接后,首次认证一下,后面只需直接使用即可。换句话说,从连接池中获取的连接都是经过授权认证的,只有新创建的连接才需要进行授权认证。 + 所以大家就看到了 count, err = red:get_reused_times() 这段代码,并有了下面 if 0 == count then 的判断逻辑 +--]] +count, err = redis_instance:get_reused_times() +if 0 == count then + ok, err = redis_instance:auth("password") + if not ok then + ngx.say("failed to auth: ", err) + return + end +elseif err then + ngx.say("failed to get reused times: ", err) + return +end + +--数据库选择 +redis_instance:select(2) + +--调用API获取数据 +local resp, err = redis_instance:hget("CDNnode:"..stream_a,'livenode') +if not resp then + ngx.say("get msg error : ", err) + return err +end + +--得到的数据为空处理 +if resp == ngx.null then + ngx.say("this is not redis_data") --比如默认值 + return nil +end +ngx.var.stream_id = resp +close_redis(redis_instance) +-- ngx.say("reds get result : ", resp) +-- help doc http://jinnianshilongnian.iteye.com/blog/2187328 \ No newline at end of file diff --git a/Openresty/lua-resty-upstream-healthcheck.md b/Openresty/lua-resty-upstream-healthcheck.md new file mode 100644 index 0000000..9259818 --- /dev/null +++ b/Openresty/lua-resty-upstream-healthcheck.md @@ -0,0 +1,154 @@ +####
lua-resty-upstream-healthcheck使用 ++ health.txt 在每个upstream 服务器组的root 目录下创建这个文件,目录结构如下所示 + ```javascript + ├── html + │   ├── 50x.html + │   ├── index.html + │   ├── websocket001.html + │   └── websocket02.html + ├── html81 + │   ├── 50x.html + │   ├── health.txt + │   └── index.html + ├── html82 + │   ├── 50x.html + │   ├── health.txt + │   └── index.html + ├── html83 + │   ├── 50x.html + │   ├── health.txt + │   └── index.html + ├── html84 + │   ├── 50x.html + │   └── index.html + ``` ++ nginx.conf + ```Lua + worker_processes 8; + + error_log logs/error.log; + pid logs/nginx.pid; + + events { + use epoll; + worker_connections 1024; + } + + http { + include mime.types; + default_type text/html; + #lua模块路径,其中”;;”表示默认搜索路径,默认到/usr/servers/nginx下找 + lua_package_path "/home/tinywan/Openresty_Protect/First_Protect/lualib/?.lua;;"; #lua 模块 + lua_package_cpath "/home/tinywan/Openresty_Protect/First_Protect/lualib/?.so;;"; #c模块 + include /home/tinywan/Openresty_Protect/First_Protect/nginx_first.conf; + } + + ``` ++ nginx_first.conf + ```Lua + upstream tomcat { + server 127.0.0.1:8081; + server 127.0.0.1:8082; + server 127.0.0.1:8083; + server 127.0.0.1:8084 backup; + } + + lua_shared_dict healthcheck 1m; + + lua_socket_log_errors off; + + init_worker_by_lua_block { + local hc = require "resty.upstream.healthcheck" + local ok, err = hc.spawn_checker{ + shm = "healthcheck", -- defined by "lua_shared_dict" + upstream = "tomcat", -- defined by "upstream" + type = "http", + + http_req = "GET /health.txt HTTP/1.0\r\nHost: tomcat\r\n\r\n", + -- raw HTTP request for checking + + interval = 2000, -- run the check cycle every 2 sec + timeout = 1000, -- 1 sec is the timeout for network operations + fall = 3, -- # of successive failures before turning a peer down + rise = 2, -- # of successive successes before turning a peer up + valid_statuses = {200, 302}, -- a list valid HTTP status code + concurrency = 10, -- concurrency level for test requests + } + } + + server { + listen 80; + server_name localhost; + + location / { + proxy_pass http://tomcat; + } + + location /server/status { + access_log off; + allow 127.0.0.1; + + default_type text/plain; + content_by_lua_block { + local hc = require "resty.upstream.healthcheck" + ngx.say("Nginx Worker PID: ", ngx.worker.pid()) + ngx.print(hc.status_page()) + } + } + } + + server { + listen 8081; + server_name localhost; + + location / { + root html81; + index index.html index.htm; + } + + location /health_status { + + } + } + + server { + listen 8082; + server_name localhost; + + location / { + root html82; + index index.html index.htm; + } + + location /health_status { + + } + } + + server { + listen 8083; + server_name localhost; + + location / { + root html83; + index index.html index.htm; + } + + location /health_status { + + } + } + + server { + listen 8084; + server_name localhost; + + location / { + root html84; + index index.html index.htm; + } + } + + ``` ++ 状态查看,通过访问:`http://127.0.0.1/server/status` + ![Markdown](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Images/Openresty_lua-resty-upstream-healthcheck.png) \ No newline at end of file diff --git a/Openresty/lua-resty-websocket/websocket.html b/Openresty/lua-resty-websocket/websocket.html new file mode 100644 index 0000000..61be687 --- /dev/null +++ b/Openresty/lua-resty-websocket/websocket.html @@ -0,0 +1,57 @@ + + + + + +
+ + + + +
+
    + + \ No newline at end of file diff --git a/Openresty/lua-resty-websocket/websocket.lua b/Openresty/lua-resty-websocket/websocket.lua new file mode 100644 index 0000000..bd0e718 --- /dev/null +++ b/Openresty/lua-resty-websocket/websocket.lua @@ -0,0 +1,39 @@ +local server = require "resty.websocket.server" +local wb, err = server:new{ + timeout = 5000, + max_payload_len = 65535 +} +if not wb then + ngx.log(ngx.ERR, "failed to new websocket: ", err) + return ngx.exit(444) +end +while true do + local data, typ, err = wb:recv_frame() + if wb.fatal then + ngx.log(ngx.ERR, "failed to receive frame: ", err) + return ngx.exit(444) + end + if not data then + local bytes, err = wb:send_ping() + if not bytes then + ngx.log(ngx.ERR, "failed to send ping: ", err) + return ngx.exit(444) + end + elseif typ == "close" then break + elseif typ == "ping" then + local bytes, err = wb:send_pong() + if not bytes then + ngx.log(ngx.ERR, "failed to send pong: ", err) + return ngx.exit(444) + end + elseif typ == "pong" then + ngx.log(ngx.INFO, "client ponged") + elseif typ == "text" then + local bytes, err = wb:send_text(data) + if not bytes then + ngx.log(ngx.ERR, "failed to send text: ", err) + return ngx.exit(444) + end + end +end +wb:send_close() diff --git a/Openresty/lua-resty-websocket/websocket01.html b/Openresty/lua-resty-websocket/websocket01.html new file mode 100644 index 0000000..ea21638 --- /dev/null +++ b/Openresty/lua-resty-websocket/websocket01.html @@ -0,0 +1,107 @@ + + + + + + + +
    + + + + +
    +
      + + + + \ No newline at end of file diff --git a/Openresty/lua-resty-websocket/websocket01.lua b/Openresty/lua-resty-websocket/websocket01.lua new file mode 100644 index 0000000..53d36d4 --- /dev/null +++ b/Openresty/lua-resty-websocket/websocket01.lua @@ -0,0 +1,104 @@ +-- simple chat with redis +local server = require "resty.websocket.server" +local redis = require "resty.redis" + +local channel_name = "chat" +local msg_id = 0 + +--create connection +local wb, err = server:new{ + timeout = 10000, + max_payload_len = 65535 +} + +--create success +if not wb then + ngx.log(ngx.ERR, "failed to new websocket: ", err) + return ngx.exit(444) +end + + +local push = function() + -- --create redis + local red = redis:new() + red:set_timeout(5000) -- 1 sec + local ok, err = red:connect("127.0.0.1", 6379) + if not ok then + ngx.log(ngx.ERR, "failed to connect redis: ", err) + wb:send_close() + return + end + + --sub + local res, err = red:subscribe(channel_name) + if not res then + ngx.log(ngx.ERR, "failed to sub redis: ", err) + wb:send_close() + return + end + + -- loop : read from redis + while true do + local res, err = red:read_reply() + if res then + local item = res[3] + local bytes, err = wb:send_text(tostring(msg_id).." "..item) + if not bytes then + -- better error handling + ngx.log(ngx.ERR, "failed to send text: ", err) + return ngx.exit(444) + end + msg_id = msg_id + 1 + end + end +end + + +local co = ngx.thread.spawn(push) + +--main loop +while true do + -- 获取数据 + local data, typ, err = wb:recv_frame() + + -- 如果连接损坏 退出 + if wb.fatal then + ngx.log(ngx.ERR, "failed to receive frame: ", err) + return ngx.exit(444) + end + + if not data then + local bytes, err = wb:send_ping() + if not bytes then + ngx.log(ngx.ERR, "failed to send ping: ", err) + return ngx.exit(444) + end + ngx.log(ngx.ERR, "send ping: ", data) + elseif typ == "close" then + break + elseif typ == "ping" then + local bytes, err = wb:send_pong() + if not bytes then + ngx.log(ngx.ERR, "failed to send pong: ", err) + return ngx.exit(444) + end + elseif typ == "pong" then + ngx.log(ngx.ERR, "client ponged") + elseif typ == "text" then + --send to redis + local red2 = redis:new() + red2:set_timeout(1000) -- 1 sec + local ok, err = red2:connect("127.0.0.1", 6379) + if not ok then + ngx.log(ngx.ERR, "failed to connect redis: ", err) + break + end + local res, err = red2:publish(channel_name, data) + if not res then + ngx.log(ngx.ERR, "failed to publish redis: ", err) + end + end +end + +wb:send_close() +ngx.thread.wait(co) \ No newline at end of file diff --git a/Openresty/lua_api_demo/ip_blacklist.lua b/Openresty/lua_api_demo/ip_blacklist.lua new file mode 100644 index 0000000..1476d40 --- /dev/null +++ b/Openresty/lua_api_demo/ip_blacklist.lua @@ -0,0 +1,60 @@ +--[[----------------------------------------------------------------------- +* | Copyright (C) Shaobo Wan (Tinywan) +* | Github: https://github.com/Tinywan +* | Date: 2017/4/20 16:25 +* | Mail: Overcome.wan@Gmail.com +* |------------------------------------------------------------------------ +* | version: 1.0 +* | function: IP地址黑名单过滤 +* |------------------------------------------------------------------------ +--]] +local redis_host = "your.redis.server.here" +local redis_port = 6379 + +-- connection timeout for redis in ms. don't set this too high! +local redis_connection_timeout = 100 + +-- check a set with this key for blacklist entries +local redis_key = "ip_blacklist" + +-- cache lookups for this many seconds +local cache_ttl = 60 + +-- end configuration + +local ip = ngx.var.remote_addr +local ip_blacklist = ngx.shared.ip_blacklist +local last_update_time = ip_blacklist:get("last_update_time"); + +-- only update ip_blacklist from Redis once every cache_ttl seconds: +if last_update_time == nil or last_update_time < (ngx.now() - cache_ttl) then + + local redis = require "resty.redis"; + local red = redis:new(); + + red:set_timeout(redis_connect_timeout); + + local ok, err = red:connect(redis_host, redis_port); + if not ok then + ngx.log(ngx.DEBUG, "Redis connection error while retrieving ip_blacklist: " .. err); + else + local new_ip_blacklist, err = red:smembers(redis_key); + if err then + ngx.log(ngx.DEBUG, "Redis read error while retrieving ip_blacklist: " .. err); + else + -- replace the locally stored ip_blacklist with the updated values: + ip_blacklist:flush_all(); + for index, banned_ip in ipairs(new_ip_blacklist) do + ip_blacklist:set(banned_ip, true); + end + + -- update time + ip_blacklist:set("last_update_time", ngx.now()); + end + end +end + +if ip_blacklist:get(ip) then + ngx.log(ngx.DEBUG, "Banned IP detected and refused access: " .. ip); + return ngx.exit(ngx.HTTP_FORBIDDEN); +end \ No newline at end of file diff --git a/Openresty/openresty-api.md b/Openresty/openresty-api.md new file mode 100644 index 0000000..1acc5bc --- /dev/null +++ b/Openresty/openresty-api.md @@ -0,0 +1,217 @@ +####
      ngx Lua APi 方法和常量 ++ ngx_lua 核心常量 + ```lua + ngx.OK (0) + ngx.ERROR (-1) + ngx.AGAIN (-2) + ngx.DONE (-4) + ngx.DECLINED (-5) + ngx.nil + -- 指令常量 + ngx.arg[index] #ngx指令参数,当这个变量在set_by_lua或者set_by_lua_file内使用的时候是只读的,指的是在配置指令输入的参数. + ngx.var.varname #读写NGINX变量的值,最好在lua脚本里缓存变量值,避免在当前请求的生命周期内内存的泄漏 + ngx.config.ngx_lua_version #当前ngx_lua模块版本号 + ngx.config.nginx_version #nginx版本 + ngx.worker.exiting #当前worker进程是否正在关闭 + ngx.worker.pid #当前worker进程的PID + ngx.config.nginx_configure #编译时的./configure命令选项 + ngx.config.prefix #编译时的prefix选项 + ``` ++ ngx_lua 方法 (#经常在ngx.location.catpure和ngx.location.capture_multi方法中被调用.) + ```lua + ngx.HTTP_GET + ngx.HTTP_HEAD + ngx.HTTP_PUT + ngx.HTTP_POST + ngx.HTTP_DELETE + ngx.HTTP_OPTIONS + ngx.HTTP_MKCOL + ngx.HTTP_COPY + ngx.HTTP_MOVE + ngx.HTTP_PROPFIND + ngx.HTTP_PROPPATCH + ngx.HTTP_LOCK + ngx.HTTP_UNLOCK + ngx.HTTP_PATCH + ngx.HTTP_TRACE + ``` ++ 错误日志级别常量 + ```lua + ngx.STDERR + ngx.EMERG + ngx.ALERT + ngx.CRIT + ngx.ERR + ngx.WARN + ngx.NOTICE + ngx.INFO + ngx.DEBUG + ``` ++ API中的常用方法 + ```lua + print() #与 ngx.print()方法有区别,print() 相当于ngx.log() + ngx.ctx #这是一个lua的table,用于保存ngx上下文的变量,在整个请求的生命周期内都有效,详细参考官方 + ngx.location.capture() #发出一个子请求,详细用法参考官方文档。 + ngx.location.capture_multi() #发出多个子请求,详细用法参考官方文档。 + ngx.status #读或者写当前请求的相应状态. 必须在输出相应头之前被调用. + ngx.header.HEADER #访问或设置http header头信息,详细参考官方文档。 + ngx.req.set_uri() #设置当前请求的URI,详细参考官方文档 + ngx.set_uri_args(args) #根据args参数重新定义当前请求的URI参数. + ngx.req.get_uri_args() #返回一个LUA TABLE,包含当前请求的全部的URL参数 + ngx.req.get_post_args() #返回一个LUA TABLE,包括所有当前请求的POST参数 + ngx.req.get_headers() #返回一个包含当前请求头信息的lua table. + ngx.req.set_header() #设置当前请求头header某字段值.当前请求的子请求不会受到影响. + ngx.req.read_body() #在不阻塞ngnix其他事件的情况下同步读取客户端的body信息.[详细] + ngx.req.discard_body() #明确丢弃客户端请求的body + ngx.req.get_body_data() #以字符串的形式获得客户端的请求body内容 + ngx.req.get_body_file() #当发送文件请求的时候,获得文件的名字 + ngx.req.set_body_data() #设置客户端请求的BODY + ngx.req.set_body_file() #通过filename来指定当前请求的file data。 + ngx.req.clear_header() #清求某个请求头 + ngx.exec(uri,args) #执行内部跳转,根据uri和请求参数 + ngx.redirect(uri, status) #执行301或者302的重定向。 + ngx.send_headers() #发送指定的响应头 + ngx.headers_sent #判断头部是否发送给客户端ngx.headers_sent=true + ngx.print(str) #发送给客户端的响应页面 + ngx.say() #作用类似ngx.print,不过say方法输出后会换行 + ngx.log(log.level,...) #写入nginx日志 + ngx.flush() #将缓冲区内容输出到页面(刷新响应) + ngx.exit(http-status) #结束请求并输出状态码 + ngx.eof() #明确指定关闭结束输出流 + ngx.escape_uri() #URI编码(本函数对逗号,不编码,而php的urlencode会编码) + ngx.unescape_uri() #uri解码 + ngx.encode_args(table) #将tabel解析成url参数 + ngx.decode_args(uri) #将参数字符串编码为一个table + ngx.encode_base64(str) #BASE64编码 + ngx.decode_base64(str) #BASE64解码 + ngx.crc32_short(str) #字符串的crs32_short哈希 + ngx.crc32_long(str) #字符串的crs32_long哈希 + ngx.hmac_sha1(str) #字符串的hmac_sha1哈希 + ngx.md5(str) #返回16进制MD5 + ngx.md5_bin(str) #返回2进制MD5 + ngx.today() #返回当前日期yyyy-mm-dd + ngx.time() #返回当前时间戳 + ngx.now() #返回当前时间 + ngx.update_time() #刷新后返回 + ngx.localtime() #返回 yyyy-mm-dd hh:ii:ss + ngx.utctime() #返回yyyy-mm-dd hh:ii:ss格式的utc时间 + ngx.cookie_time(sec) #返回用于COOKIE使用的时间 + ngx.http_time(sec) #返回可用于http header使用的时间 + ngx.parse_http_time(str) #解析HTTP头的时间 + ngx.is_subrequest #是否子请求(值为 true or false) + ngx.re.match(subject,regex,options,ctx) #ngx正则表达式匹配,详细参考官网 + ngx.re.gmatch(subject,regex,opt) #全局正则匹配 + ngx.re.sub(sub,reg,opt) #匹配和替换(未知) + ngx.re.gsub() #未知 + ngx.shared.DICT #ngx.shared.DICT是一个table 里面存储了所有的全局内存共享变量 + ngx.shared.DICT.get + ngx.shared.DICT.get_stale + ngx.shared.DICT.set + ngx.shared.DICT.safe_set + ngx.shared.DICT.add + ngx.shared.DICT.safe_add + ngx.shared.DICT.replace + ngx.shared.DICT.delete + ngx.shared.DICT.incr + ngx.shared.DICT.flush_all + ngx.shared.DICT.flush_expired + ngx.shared.DICT.get_keys + ndk.set_var.DIRECTIVE + ``` ++ Lua HTTP状态常量 + ```Lua + value = ngx.HTTP_CONTINUE (100) (first added in the v0.9.20 release) + value = ngx.HTTP_SWITCHING_PROTOCOLS (101) (first added in the v0.9.20 release) + value = ngx.HTTP_OK (200) + value = ngx.HTTP_CREATED (201) + value = ngx.HTTP_ACCEPTED (202) (first added in the v0.9.20 release) + value = ngx.HTTP_NO_CONTENT (204) (first added in the v0.9.20 release) + value = ngx.HTTP_PARTIAL_CONTENT (206) (first added in the v0.9.20 release) + value = ngx.HTTP_SPECIAL_RESPONSE (300) + value = ngx.HTTP_MOVED_PERMANENTLY (301) + value = ngx.HTTP_MOVED_TEMPORARILY (302) + value = ngx.HTTP_SEE_OTHER (303) + value = ngx.HTTP_NOT_MODIFIED (304) + value = ngx.HTTP_TEMPORARY_REDIRECT (307) (first added in the v0.9.20 release) + value = ngx.HTTP_BAD_REQUEST (400) + value = ngx.HTTP_UNAUTHORIZED (401) + value = ngx.HTTP_PAYMENT_REQUIRED (402) (first added in the v0.9.20 release) + value = ngx.HTTP_FORBIDDEN (403) + value = ngx.HTTP_NOT_FOUND (404) + value = ngx.HTTP_NOT_ALLOWED (405) + value = ngx.HTTP_NOT_ACCEPTABLE (406) (first added in the v0.9.20 release) + value = ngx.HTTP_REQUEST_TIMEOUT (408) (first added in the v0.9.20 release) + value = ngx.HTTP_CONFLICT (409) (first added in the v0.9.20 release) + value = ngx.HTTP_GONE (410) + value = ngx.HTTP_UPGRADE_REQUIRED (426) (first added in the v0.9.20 release) + value = ngx.HTTP_TOO_MANY_REQUESTS (429) (first added in the v0.9.20 release) + value = ngx.HTTP_CLOSE (444) (first added in the v0.9.20 release) + value = ngx.HTTP_ILLEGAL (451) (first added in the v0.9.20 release) + value = ngx.HTTP_INTERNAL_SERVER_ERROR (500) + value = ngx.HTTP_METHOD_NOT_IMPLEMENTED (501) + value = ngx.HTTP_BAD_GATEWAY (502) (first added in the v0.9.20 release) + value = ngx.HTTP_SERVICE_UNAVAILABLE (503) + value = ngx.HTTP_GATEWAY_TIMEOUT (504) (first added in the v0.3.1rc38 release) + value = ngx.HTTP_VERSION_NOT_SUPPORTED (505) (first added in the v0.9.20 release) + value = ngx.HTTP_INSUFFICIENT_STORAGE (507) (first added in the v0.9.20 release) + ``` ++ 案列使用,get_string_md5.lua: + ```Lua + local args = ngx.req.get_uri_args() + local salt = args.salt + if not salt then + ngx.say(ngx.HTTP_BAD_REQUEST) + end + local string = ngx.md5(ngx.time()..salt) + ngx.say(string) + + ``` ++ curl 请求(-i 参数,输出时包括protocol头信息): + ```Bash + tinywan@tinywan:$ curl -i http://127.0.0.1/get_rand_string?salt=tinywan123 + HTTP/1.1 200 OK + Server: openresty/1.11.2.1 + Date: Fri, 21 Apr 2017 14:27:16 GMT + Content-Type: application/octet-stream + Transfer-Encoding: chunked + Connection: keep-alive + ``` +#### ngx Lua APi 介绍使用 ++ 强烈建议使用ngx Lua APi 接口`(非阻塞的)`,而不是Lua自身的API`(阻塞的)`,Lua 自身API会阻塞掉的 ++ ngx_lua_api_test.lua + ```Lua + local json = require "cjson" -- 引入cjson 扩展 + + -- 同步读取客户端请求正文,而不会阻止Nginx事件循环 + ngx.req.read_body() + local args = ngx.req.get_post_args() + + if not args or not args.info then + ngx.say(ngx.HTTP_BAD_REQUEST) -- ngx.HTTP_BAD_REQUEST (400) + end + + local client_id = ngx.var.remote_addr + local user_agent = ngx.req.get_headers()['user-agent'] or "" + local info = ngx.decode_base64(args.info) + + local response = {} + response.info = info + response.client_id = client_id + response.user_agent = user_agent + + ngx.say(json.encode(response)) + + ``` ++ CURL Post 请求 + ```Lua + $ curl -i --data "info=b3ZlcmNvbWUud2FuQGdtYWlsLmNvbQ==" http://127.0.0.1/ngx_lua_api_test + HTTP/1.1 200 OK + Server: openresty/1.11.2.1 + Date: Sat, 22 Apr 2017 01:22:07 GMT + Content-Type: application/octet-stream + Transfer-Encoding: chunked + Connection: keep-alive + + {"user_agent":"curl\/7.47.0","info":"overcome.wan@gmail.com","client_id":"127.0.0.1"} + + ``` \ No newline at end of file diff --git a/Openresty/openresty-basic.md b/Openresty/openresty-basic.md new file mode 100644 index 0000000..51d234d --- /dev/null +++ b/Openresty/openresty-basic.md @@ -0,0 +1,62 @@ ++ [安装信息](http://www.cnblogs.com/tinywan/p/6647587.html) ++ [默认配置信息](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Openresty/default-config.md) ++ 开发入门 + + Nginx与Lua的整体目录关系 + ```javascript + . + ├── conf + │ ├── nginx.conf -- Nginx 配置文件 + ├── logs + │ ├── error.log -- Nginx 错误日子 + │ └── nginx.pid + ├── lua + │ ├── m3u8_redis_access.lua -- M3U8地址权限验证文件 + │ ├── business_redis.lua -- 业务 Redis 处理文件 + │ ├── http-lua-test.lua -- http lua demo + │ ├── ... + │ └── resty -- 存放Lua 的所有公共、封装好的库目录 + │ └── redis_iresty.lua -- Redis 接口的二次封装 + │ └── param.lua -- 参数过滤库 + └── sbin + └── nginx + ``` + + 参数总结 + + Lua脚本接受Nginx变量: + > [1] 间接获取:`var = ngx.var `,如接受Nginx的变量` $a = 9`,则`lua_a = ngx.var.a --lua_a = 9` + [2] 直接获取:`var = ngx.var `,如接受Nginx的location的第二个变量890,` http://127.0.0.1/lua_request/123/890 `,则`lua_2 = ngx.var[2] --lua_2 = 890` + + Lua 脚本接受 Nginx 头部 header: + > [1] 返回一个包含所有当前请求标头的Lua表:`local headers = ngx.req.get_headers()` + [2] 获取单个Host:`headers["Host"] 或者 ngx.req.get_headers()["Host"] ` + [3] 获取单个user-agent: + >> [01]`headers["user-agent"]` + [02]`headers.user_agent ` + [03]`ngx.req.get_headers()['user-agent'] ` + + Lua 脚本 Get 获取请求uri参数 + > linux curl Get方式提交数据语法:`curl -G -d "name=value&name2=value2" https://github.com/Tinywan ` + 返回一个包含所有当前请求URL查询参数的Lua表:`local get_args = ngx.req.get_uri_args()` + 请求案例:`curl -G -d "name=Tinywan&age=24" http://127.0.0.1/lua_request/123/789` + Lua Get 方式获取提交的name参数的值:`get_args['name'] 或者 ngx.req.get_uri_args()['name']` + >> [01]`get_args['name']` + [02]`ngx.req.get_uri_args()['name']` + + Lua 脚本 Post 获取请求uri参数 + > linux curl Post方式提交数据语法: + >> [01] `curl -d "name=value&name2=value2" https://github.com/Tinywan ` + [02] `curl -d a=b&c=d&txt@/tmp/txt https://github.com/Tinywan ` + > 返回一个包含所有当前请求URL查询参数的Lua表:`local post_args = ngx.req.get_post_args()` + 请求案例:`curl -d "name=Tinywan&age=24" http://127.0.0.1/lua_request/123/789` + Lua Post 方式获取提交的name参数的值: + >> [01]`post_args['name']` + [02]`ngx.req.get_post_args()['name']` + + Lua 脚本请求的http协议版本:`ngx.req.http_version()` + + Lua 脚本请求方法:`ngx.req.get_method()` + + Lua 脚本原始的请求头内容:`ngx.req.raw_header()` + + Lua 脚本请求的body内容体:`ngx.req.get_body_data()` + + [接收请求:获取如请求参数、请求头、Body体等信息]() + + [接收请求:输出响应需要进行响应状态码、响应头和响应内容体的输出]( ++ luajit 执行文件默认安装路径:`/opt/openresty/luajit/bin/luajit`,这样我们直接可以这样运行一个Lua文件:`luajit test.lua ` + + luajit 运行测试案例: + ```Bash + tinywan@tinywan:~/Lua$ luajit test.lua + The man name is Tinywan + The man name is Phalcon + ``` \ No newline at end of file diff --git a/Openresty/openresty-resty-module.md b/Openresty/openresty-resty-module.md new file mode 100644 index 0000000..45bfe1a --- /dev/null +++ b/Openresty/openresty-resty-module.md @@ -0,0 +1,246 @@ ++ [Lua require 绝对和相对路径(已经解决)](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Openresty/lua-common-package/lua-require.md) ++ [luajit 执行文件默认安装路径](#Openresty_web_knowledge) ++ [lua-resty-redis 扩展](#Openresty_resty-redis) ++ [lua-resty-websocket 扩展](#Openresty_resty-websocket) ++ [lua-cjson 扩展](#Openresty_resty-cjson) ++ [lua-dkjson 扩展](https://github.com/Tinywan/lua_project_v0.01) ++ [Lua 权限验证](#Openresty_resty-access) ++ [lua-resty-string 扩展](#Openresty_resty-string) ++ [lua-resty-http 扩展 ](#Openresty_resty-http) ++ [lua-resty-mysql 扩展](#Openresty_resty-mysql) ++ [lua-resty-shell 扩展](http://www.cnblogs.com/tinywan/p/6809879.html) ++ [lua-resty-template 扩展](https://github.com/Tinywan/lua_project_v0.01) ++ [lua-resty-template 扩展](https://github.com/Tinywan/lua_project_v0.01) ++ [openresty扫描代码全局变量](#Openresty_all-var) ++ [连接数据库](#Openresty_connent_redis) ++ [OpenResty缓存](#Openresty_connent_cache) ++ [lua-resty-upstream-healthcheck 使用](#Openresty_lua_resty_upstream_healthcheck) ++ [Openresty和Nginx_RTMP 模块共存问题](#Openresty_rtmp_share) ++ [Openresty配置RTMP模块的多worker直播流](#Openresty_rtmp_more_worker) ++ [Openresty配置RTMP模块的推流地址鉴权实例](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx-Rtmp/Openresty_rtmp_obs_push.md) ++ [Ngx_lua 写入Redis数据,通过CURL请求](#Ngx_lua_write_Redis) ++ [Nginx编写的Lua接口使用URL过期和签名验证机制过滤非法访问接口](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx-Rtmp/Openresty_rtmp_obs_push_auth.md) ++ [Nginx查看并发连接数](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx-Rtmp/nginx_status.md) +#### lua-resty-redis 扩展 (是openresty下操作redis的模块) ++ 代码引入:`lua_package_path "/opt/openresty/nginx/lua/lua-resty-redis/lib/?.lua;;";` ++ Lua脚本实现一个CDN的反向代理功能(智能查找CDN节点)(测试成功,可上线) + + nginx.conf 配置信息 + ```Lua + http { + lua_package_path "/opt/openresty/nginx/lua/lua-resty-redis/lib/?.lua;;"; + server { + listen 80; + server_name localhost; + location ~ \/.+\/.+\.(m3u8|ts) { + if ($uri ~ \/([a-zA-Z0-9]+)\/([a-zA-Z0-9]+)(|-).*\.(m3u8|ts)) { + set $app_name $1; + set $a $2; + } + set $stream_id ""; + default_type 'text/html'; + rewrite_by_lua_file /opt/openresty/nginx/lua/proxy_pass_cdn.lua; + proxy_connect_timeout 10; + proxy_send_timeout 30; + proxy_read_timeout 30; + proxy_pass $stream_id; + } + + } + } + ``` + + [Lua脚本proxy_pass_cdn.lua](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Openresty/lua-resty-redis/proxy_pass_cdn.lua) + + [lua-nginx-module 贡献代码](https://github.com/openresty/lua-nginx-module/issues/275) + ++ Lua脚本结合 Nginx+Lua+Local Redis+Mysql服务器缓存 + + Nginx+Lua+Local Redis+Mysql集群架构 + + ![Nginx+Lua+Local Redis+Mysql](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Images/Nginx+Lua+Local_Redis+Mysql.png) + + [Lua脚本Nginx+Lua+Redis+Mysql.lua](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Openresty/lua-resty-redis/Nginx+Lua+Redis+Mysql.lua) + + [Nginx.conf配置文件Nginx+Lua+Redis+Mysql.conf](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Openresty/lua-resty-redis/Nginx+Lua+Redis+Mysql.conf) + + [HELP](http://jinnianshilongnian.iteye.com/blog/2188113) + + + Lua脚本结合 Redis 统计直播流播放次数、链接次数等等信息 + + nginx.conf + ```Lua + server { # 配置虚拟服务器80 + listen 80; + server_name 127.0.0.1:8088; + location ~* /live/(\w+)/ { + set $total_numbers ""; + set $stream_name $1; + lua_code_cache off; + rewrite_by_lua_file /opt/openresty/nginx/conf/Lua/total_numbers.lua; + proxy_pass http://127.0.0.1:8088; + } + } + ``` + + 代理服务器 + ```Lua + server { # 配置虚拟服务器8088 + listen 8088; + server_name 127.0.0.1:8088; + location /live { + add_header Cache-Control no-cache; + add_header 'Access-Control-Allow-Origin' '*' always; + add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range'; + add_header 'Access-Control-Allow-Headers' 'Range'; + types{ + application/dash+xml mpd; + application/vnd.apple.mpegurl m3u8; + video/mp2t ts; + } + alias /home/tinywan/HLS/live/; + } + } + + ``` + + CURL请求地址:`http://192.168.18.143/live/tinywan123/index.m3u8` + + [Lua 脚本](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Openresty/lua-resty-redis/Hls-Line-Number-Total.lua) + +#### lua-resty-websocket 扩展 ++ 代码引入:`lua_package_path "/opt/openresty/nginx/lua/lua-resty-websocket/lib/?.lua;;";` ++ **Lua脚本实现一个websocket连接(测试成功,可上线)** + + nginx.conf 配置信息 + ```Lua + http { + lua_package_path "/opt/openresty/nginx/lua/lua-resty-websocket/lib/?.lua;;"; + server { + listen 80 so_keepalive=2s:2s:8; #为了防止半开TCP连接,最好在Nginx监听配置指令中启用TCP keepalive: + server_name localhost; + location /ws { + lua_socket_log_errors off; + lua_check_client_abort on; + lua_code_cache off; # 建议测试的时候最好关闭缓存 + content_by_lua_file /opt/openresty/nginx/conf/Lua/websocket.lua; + } + } + } + ``` + + [WebSockets服务器Lua脚本websocket.lua](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Openresty/lua-resty-websocket/websocket.lua) + + [websockets.html客户端代码,代码路径:/usr/local/openresty/nginx/html](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Openresty/lua-resty-websocket/websocket.html) + + 然后打开启用了WebSocket支持的浏览器,然后打开以下url: + ![websockt-lua](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Images/websocket_lua01.png) +#### lua-cjson 扩展 ++ 基本用法 + + nginx.conf + ```Lua + location /cjson { + content_by_lua_block { + local cjson = require "cjson" + local json = cjson.encode({ + foo = "bar", + some_object = {}, + some_array = cjson.empty_array + }) + ngx.say(json) + } + } + ``` + + curl 请求 + ```Bash + root@tinywan:/opt/openresty/nginx/conf# curl http://127.0.0.1/cjson + {"some_object":{"tel":13669313112,"age":24},"name":"tinywan","some_array":[]} + ``` ++ [lua对象到字符串、字符串到lua对象](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Openresty/lua-cjson/cjson-str-obj.lua) +#### lua-resty-session 扩展 ++ OpenResty 引用第三方 resty 库非常简单,只需要将相应的文件拷贝到 resty 目录下即可 ++ 我服务器OpenResty 的 resty 路径:`/opt/openresty/lualib/resty` ++ 下载第三方 resty 库:git clone lua-resty-session 文件路径以及内容: + ```Bash + tinywan@tinywan:/opt/openresty/nginx/lua/lua-resty-session/lib/resty$ ls + session session.lua + ``` ++ 特别注意:这里拷贝的时候要要把session文件和session.lua 文件同时吧、拷贝过去,否则会报错误: + ```Bash + /opt/openresty/lualib/resty/session.lua:34: in function 'prequire' + /opt/openresty/lualib/resty/session.lua:211: in function 'new' + /opt/openresty/lualib/resty/session.lua:257: in function 'open' + /opt/openresty/lualib/resty/session.lua:320: in function 'start' + ``` ++ 拷贝完毕后`/opt/openresty/lualib/resty` OpenResty 引用第三方 resty 的所有库文件 + ```Bash + tinywan@tinywan:/opt/openresty/lualib/resty$ ls + aes.lua core.lua http_headers.lua lock.lua lrucache.lua memcached.lua random.lua session sha1.lua sha256.lua sha512.lua string.lua upstream + core dns http.lua lrucache md5.lua mysql.lua redis.lua session.lua sha224.lua sha384.lua sha.lua upload.lua websocket + ``` ++ 基本用法 + ```Lua + location /start { + content_by_lua_block { + local session = require "resty.session".start() + session.data.name = "OpenResty Fan Tinywan" + session:save() + ngx.say("Session started. ", + "Check if it is working!") + ngx.say(session.data.name,"Anonymous") + } + } + ``` ++ curl 请求 + ```Bash + tinywan@tinywan:/opt/openresty/nginx/conf$ curl http://192.168.18.143/start + Session started. Check if it is working! + OpenResty Fan Tinywan Anonymous + ``` +#### Lua 权限验证 ++ Lua 一个HLS的简单地址访问权限验证 + + Nginx.conf 配置 + ```Lua + location ^~ /live/ { + add_header Cache-Control no-cache; + add_header 'Access-Control-Allow-Origin' '*' always; + add_header 'Access-Control-Allow-Credentials' 'true'; + add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range'; + add_header 'Access-Control-Allow-Headers' 'Range'; + + types{ + application/dash+xml mpd; + application/vnd.apple.mpegurl m3u8; + video/mp2t ts; + } + if ( $uri ~ \.m3u8 ) { + lua_code_cache off; + access_by_lua_file /opt/openresty/nginx/lua/access.lua; + } + root /home/tinywan/HLS; + } + ``` + + access.lua 文件内容 + ```Lua + if ngx.req.get_uri_args()["wsSecret"] ~= "e65e6a01cf26523e206d5bb0e2a8a95a" then + return ngx.exit(403) + end + ``` +#### lua-resty-string 扩展 ++ MD5加密的简单基本用法 md5.lua + ```Lua + local resty_md5 = require "resty.md5" + local md5 = resty_md5:new() + if not md5 then + ngx.say("failed to create md5 object") + return + end + local ok = md5:update("hello") + if not ok then + ngx.say("failed to add data") + return + end + local digest = md5:final() + -- ngx.say("md5",digest) ---注意:这样直接输出是乱码 + local str = require "resty.string" + ngx.say("md5: ", str.to_hex(digest)) ---注意:必须通过字符串转码方可打印输出 + -- yield "md5: 5d41402abc4b2a76b9719d911017c592" + ``` +#### lua-resty-http 扩展 (ngx_lua的HTTP客户端cosocket驱动程序) ++ [简单测试:lua-http-test.lua](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Openresty/lua-resty-http/lua-http-test.lua) +#### lua-resty-mysql 扩展 ++ [简单测试:lua-msyql-test.lua](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Openresty/lua-resty-mysql/lua-msyql-test.lua) +#### srcache-nginx-module 扩展 ([nginx下的一个缓存模块](https://github.com/openresty/srcache-nginx-module)) ++ [openresty–redis–srcache缓存的应用](http://www.xtgxiso.com/openresty-redis-srcache-nginx-module%e7%bc%93%e5%ad%98%e7%9a%84%e5%ba%94%e7%94%a8/) +#### openresty扫描代码全局变量 ++ 在OpenResty中需要避免全局变量的使用,为此春哥写了一个perl工具,可以扫描openresty lua代码的全局变量 ++ [https://github.com/openresty/openresty-devel-utils/blob/master/lua-releng](https://github.com/openresty/openresty-devel-utils/blob/master/lua-releng) ++ 用法相当简单 + 1. 将代码保存成lua-releng文件 + 2. 更改lua-releng的权限,chmod 777 lua-releng + 3. 假设有一个源码文件为test.lua + 4. 执行./lua-releng test.lua,则会扫描test.lua文件的全局变量,并在屏幕打印结果 \ No newline at end of file diff --git a/Openresty/openresty-rtmp.md b/Openresty/openresty-rtmp.md new file mode 100644 index 0000000..bc96331 --- /dev/null +++ b/Openresty/openresty-rtmp.md @@ -0,0 +1,37 @@ +#### OpenResty缓存 ++ 指令:`lua_shared_dict` + + 纯内存的操作,多个worker之间共享的(比如nginx开启10个Worker,则每个worker之间是共享该内存的) + + 同一份数据在多个worker之间是共享的,只要存储一份数据就可以了 + + 锁的竞争(数据原子性) + +#### Openresty和Nginx_RTMP 模块共存问题 ++ RTMP 流的状态(stat.xsl)不生效Bug 问题 + - 1. 修改完nginx.conf 配置文件 + - 1. ~~执行:`nginx -s reload` 会不起作用~~ + - 2. 一定要执行以下命令:杀掉所有nginx进程`sudo killall nginx ` 重启即可`sbin/nignx` +#### 配置RTMP模块的多worker直播流 ++ 配置文件,[Multi-worker live streaming官方文档](https://github.com/arut/nginx-rtmp-module/wiki/Directives#multi-worker-live-streaming) + ```Shell + user www www; + worker_processes auto; + error_log logs/error.log debug; + + pid /var/run/nginx.pid; + events { + use epoll; + worker_connections 1024; + multi_accept on; + } + + rtmp_auto_push on; + rtmp_auto_push_reconnect 1s; + rtmp_socket_dir /var/sock; + rtmp { + server { + listen 1935; + application live { + live on; + } + } + } + ``` \ No newline at end of file diff --git a/Openresty/test.lua b/Openresty/test.lua new file mode 100644 index 0000000..2656e60 --- /dev/null +++ b/Openresty/test.lua @@ -0,0 +1,48 @@ +local red = redis:new() + +-- set Cache cache_ngx +function set_to_cache(key,value,exptime) + if not exptime then + exptime = 0 + end + local cache_ngx = ngx.shared.cache_ngx + local succ, err, forcible = cache_ngx:set(key,value,exptime) + return succ +end + +--get Cache cache_ngx +function get_from_cache(key) + local cache_ngx = ngx.shared.cache_ngx + local value = cache_ngx:get(key) + if not value then + value = nil + set_to_cache(key, value) + end + return value +end + +function get_from_redis(key) + local res, err = red:get("dog") + if res then + return 'yes' + else + return 'No' + end +end +local res = get_from_cache('dog') +ngx.say(res) + + +local delay = 5 +local handler +handler = function (premature,param) + -- do some routine job in Lua just like a cron job + if premature then + return + end + + ngx.log(ngx.ERR, "param is : ", param) +end + +local ok, err = ngx.timer.at(delay, handler,"Hello Tinywan") + \ No newline at end of file diff --git a/PHP/PHP-FPM/config.md b/PHP/PHP-FPM/config.md new file mode 100644 index 0000000..eddecba --- /dev/null +++ b/PHP/PHP-FPM/config.md @@ -0,0 +1,195 @@ +## PHP7中php.ini、php-fpm和www.conf的配置 ++ 配置文件详解 + ``` + |Project_dir + | + | |----nginx.conf -- 配置文件(PHP版本,适合WordPress、Typecho等) + |--Nginx--|----nginx_pelican.conf -- 配置文件(静态HTML版本,适合Pelican、HEXO等) + | |----nginx -- 服务控制脚本 + | + | |----php.ini -- php运行核心配置文件,文件所在目录:/opt/php-7.0.9/etc/ + | |----php-fpm -- 服务控制脚本,文件所在目录:/opt/php-7.0.9/sbin/ + |---PHP---| + | |----php-fpm.conf -- 是 **php-fpm** 进程服务的配置文件,文件所在目录:/opt/php-7.0.9/etc/ + | |----www.conf -- 是 php-fpm 进程服务的扩展配置文件,文件所在目录:/opt/php-7.0.9/etc/php-fpm.d/ + | + | |----my.cnf -- 配置文件 + |--MySql--| + | |----mysqld -- 服务控制脚本 + | + |--nginx_log_backup.sh -- 每天切割Nginx服务产生的日志文件 + | + |--README.md + ``` ++ PHP + + 启动:`sudo /opt/php-7.0.9/sbin/php-fpm ` + + 重启: + ``` + sudo kill -QUIT `cat /opt/php-7.0.9/var/run/php-fpm.pid` + ``` + + 停止: + ``` + sudo kill -QUIT `cat /opt/php-7.0.9/var/run/php-fpm.pid` + + sudo kill -9 `cat /opt/php-7.0.9/var/run/php-fpm.pid` + ``` + + php-fpm 主进程pid:`cat /opt/php-7.0.9/var/run/php-fpm.pid ` + + php-fpm 错误日志文件:`cat /opt/php-7.0.9/var/log/php-fpm.log ` + + php-fpm 的启动参数 + ``` + #测试php-fpm配置 + /usr/local/php/sbin/php-fpm -t + /usr/local/php/sbin/php-fpm -c /usr/local/php/etc/php.ini -y /usr/local/php/etc/php-fpm.conf -t + + #启动php-fpm + /usr/local/php/sbin/php-fpm + /usr/local/php/sbin/php-fpm -c /usr/local/php/etc/php.ini -y /usr/local/php/etc/php-fpm.conf + + #关闭php-fpm + kill -INT `cat /usr/local/php/var/run/php-fpm.pid` + + #重启php-fpm + kill -USR2 `cat /usr/local/php/var/run/php-fpm.pid` + ``` + + php.ini + > [1] php 运行核心配置文件 + > [2] 文件信息 + + ``` + ######避免PHP信息暴露在http头中 + expose_php = Off + + ######避免暴露php调用mysql的错误信息 + display_errors = Off + + ######在关闭display_errors后开启PHP错误日志(路径在php-fpm.conf中配置) + log_errors = On + + ######设置PHP的扩展库路径 + extension_dir = "/usr/local/php7/lib/php/extensions/no-debug-non-zts-20141001/" + + ######设置PHP的opcache和mysql动态库 + zend_extension=opcache.so + extension=mysqli.so + extension=pdo_mysql.so + + ######设置PHP的时区 + date.timezone = PRC + + ######开启opcache + [opcache] + ; Determines if Zend OPCache is enabled + opcache.enable=1 + + ######设置PHP脚本允许访问的目录(需要根据实际情况配置) + ;open_basedir = /usr/share/nginx/html; + ``` + + + php-fpm.conf + > [1] 是php-fpm进程服务的配置文件 + > [2] 文件信息 + + ``` + pid = run/php-fpm.pid + #pid设置,默认在安装目录中的var/run/php-fpm.pid,建议开启 + + error_log = log/php-fpm.log + #错误日志,默认在安装目录中的var/log/php-fpm.log + + log_level = notice + #错误级别. 可用级别为: alert(必须立即处理), error(错误情况), warning(警告情况), notice(一般重要信息), debug(调试信息). 默认: notice. + + emergency_restart_threshold = 60 + emergency_restart_interval = 60s + #表示在emergency_restart_interval所设值内出现SIGSEGV或者SIGBUS错误的php-cgi进程数如果超过 emergency_restart_threshold个,php-fpm就会优雅重启。这两个选项一般保持默认值。 + + process_control_timeout = 0 + #设置子进程接受主进程复用信号的超时时间. 可用单位: s(秒), m(分), h(小时), 或者 d(天) 默认单位: s(秒). 默认值: 0. + + daemonize = yes + #后台执行fpm,默认值为yes,如果为了调试可以改为no。在FPM中,可以使用不同的设置来运行多个进程池。 这些设置可以针对每个进程池单独设置。 + + listen = 127.0.0.1:9000 + #fpm监听端口,即nginx中php处理的地址,一般默认值即可。可用格式为: 'ip:port', 'port', '/path/to/unix/socket'. 每个进程池都需要设置. + + listen.backlog = -1 + #backlog数,-1表示无限制,由操作系统决定,此行注释掉就行。backlog含义参考:http://www.3gyou.cc/?p=41 + + listen.allowed_clients = 127.0.0.1 + #允许访问FastCGI进程的IP,设置any为不限制IP,如果要设置其他主机的nginx也能访问这台FPM进程,listen处要设置成本地可被访问的IP。默认值是any。每个地址是用逗号分隔. 如果没有设置或者为空,则允许任何服务器请求连接 + + listen.owner = www + listen.group = www + listen.mode = 0666 + #unix socket设置选项,如果使用tcp方式访问,这里注释即可。 + + user = www + group = www + #启动进程的帐户和组 + + pm = dynamic #对于专用服务器,pm可以设置为static。 + #如何控制子进程,选项有static和dynamic。如果选择static,则由pm.max_children指定固定的子进程数。如果选择dynamic,则由下开参数决定: + pm.max_children #,子进程最大数 + pm.start_servers #,启动时的进程数 + pm.min_spare_servers #,保证空闲进程数最小值,如果空闲进程小于此值,则创建新的子进程 + pm.max_spare_servers #,保证空闲进程数最大值,如果空闲进程大于此值,此进行清理 + + pm.max_requests = 1000 + #设置每个子进程重生之前服务的请求数. 对于可能存在内存泄漏的第三方模块来说是非常有用的. 如果设置为 '0' 则一直接受请求. 等同于 PHP_FCGI_MAX_REQUESTS 环境变量. 默认值: 0. + + pm.status_path = /status + #FPM状态页面的网址. 如果没有设置, 则无法访问状态页面. 默认值: none. munin监控会使用到 + + ping.path = /ping + #FPM监控页面的ping网址. 如果没有设置, 则无法访问ping页面. 该页面用于外部检测FPM是否存活并且可以响应请求. 请注意必须以斜线开头 (/)。 + + ping.response = pong + #用于定义ping请求的返回相应. 返回为 HTTP 200 的 text/plain 格式文本. 默认值: pong. + + request_terminate_timeout = 0 + #设置单个请求的超时中止时间. 该选项可能会对php.ini设置中的'max_execution_time'因为某些特殊原因没有中止运行的脚本有用. 设置为 '0' 表示 'Off'.当经常出现502错误时可以尝试更改此选项。 + + request_slowlog_timeout = 10s + #当一个请求该设置的超时时间后,就会将对应的PHP调用堆栈信息完整写入到慢日志中. 设置为 '0' 表示 'Off' + + slowlog = log/$pool.log.slow + #慢请求的记录日志,配合request_slowlog_timeout使用 + + rlimit_files = 1024 + #设置文件打开描述符的rlimit限制. 默认值: 系统定义值默认可打开句柄是1024,可使用 ulimit -n查看,ulimit -n 2048修改。 + + rlimit_core = 0 + #设置核心rlimit最大限制值. 可用值: 'unlimited' 、0或者正整数. 默认值: 系统定义值. + + chroot = + #启动时的Chroot目录. 所定义的目录需要是绝对路径. 如果没有设置, 则chroot不被使用. + + chdir = + #设置启动目录,启动时会自动Chdir到该目录. 所定义的目录需要是绝对路径. 默认值: 当前目录,或者/目录(chroot时) + + catch_workers_output = yes + #重定向运行过程中的stdout和stderr到主要的错误日志文件中. 如果没有设置, stdout 和 stderr 将会根据FastCGI的规则被重定向到 /dev/null . 默认值: 空. + ``` + + + www.conf + > [1] 这是php-fpm进程服务的扩展配置文件 + > [2] 文件信息 + + ``` + ######设置用户和用户组 + user = nginx + group = nginx + + ######根据nginx.conf中的配置fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;设置PHP监听 + ; listen = 127.0.0.1:9000 #####不建议使用 + listen = /var/run/php-fpm/php-fpm.sock + + ######开启慢日志 + slowlog = /var/log/php-fpm/$pool-slow.log + request_slowlog_timeout = 10s + + ######设置php的session目录(所属用户和用户组都是nginx) + php_value[session.save_handler] = files + php_value[session.save_path] = /var/lib/php/session + `` + diff --git a/PHP/PHP-FPM/php-fpm.sh b/PHP/PHP-FPM/php-fpm.sh new file mode 100644 index 0000000..864c87b --- /dev/null +++ b/PHP/PHP-FPM/php-fpm.sh @@ -0,0 +1,134 @@ +#! /bin/sh +### BEGIN INIT INFO +# Provides: php-fpm +# Required-Start: $remote_fs $network +# Required-Stop: $remote_fs $network +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: starts php-fpm +# Description: starts the PHP FastCGI Process Manager daemon +### END INIT INFO + +prefix=/opt/php-7.0.9 # 只需要修改这里就可以里,这里是编译路径 +exec_prefix=${prefix} + +php_fpm_BIN=${exec_prefix}/sbin/php-fpm +php_fpm_CONF=${prefix}/etc/php-fpm.conf +php_fpm_PID=${prefix}/var/run/php-fpm.pid + +php_opts="--fpm-config $php_fpm_CONF --pid $php_fpm_PID" + +wait_for_pid () { + try=0 + + while test $try -lt 35 ; do + + case "$1" in + 'created') + if [ -f "$2" ] ; then + try='' + break + fi + ;; + + 'removed') + if [ ! -f "$2" ] ; then + try='' + break + fi + ;; + esac + + echo -n . + try=`expr $try + 1` + sleep 1 + + done + +} +case "$1" in + start) + echo -n "Starting php-fpm ... " + + $php_fpm_BIN --daemonize $php_opts + + if [ "$?" != 0 ] ; then + echo " failed" + exit 1 + fi + + wait_for_pid created $php_fpm_PID + + if [ -n "$try" ] ; then + echo " failed" + exit 1 + else + echo "[OK]" + fi + ;; + + stop) + echo -n "Gracefully shutting down php-fpm " + + if [ ! -r $php_fpm_PID ] ; then + echo "warning, no pid file found - php-fpm is not running ?" + exit 1 + fi + + kill -QUIT `cat $php_fpm_PID` + + wait_for_pid removed $php_fpm_PID + + if [ -n "$try" ] ; then + echo " failed. Use force-quit" + exit 1 + else + echo "[OK]" + fi + ;; + + force-quit) + echo -n "Terminating php-fpm " + + if [ ! -r $php_fpm_PID ] ; then + echo "warning, no pid file found - php-fpm is not running ?" + exit 1 + fi + + kill -TERM `cat $php_fpm_PID` + + wait_for_pid removed $php_fpm_PID + + if [ -n "$try" ] ; then + echo " failed" + exit 1 + else + echo " [OK]" + fi + ;; + + restart) + $0 stop + $0 start +;; + + reload) + + echo -n "Reload service php-fpm " + + if [ ! -r $php_fpm_PID ] ; then + echo "warning, no pid file found - php-fpm is not running ?" + exit 1 + fi + + kill -USR2 `cat $php_fpm_PID` + + echo "[OK]" + ;; + + *) + echo "Usage: $0 {start|stop|force-quit|restart|reload}" + exit 1 +;; + +esac \ No newline at end of file diff --git a/PHP/Socket/test.php b/PHP/Socket/test.php new file mode 100644 index 0000000..4bb54fc --- /dev/null +++ b/PHP/Socket/test.php @@ -0,0 +1,14 @@ + 'success', + 1 => 'API Sign Error , Please task_id', + 2 => 'Oss File Download Fail', + 3 => 'FFmpeg cut/concat Video Fail', + 4 => 'Rename file error, Disk is full', + 5 => '截取缩略图失败,请检查视频开始、结束、视频截图时间', + ]; + + /** + * 在退出时使用不同的错误码 + */ + public function phpRunShellScript() + { + // 脚本路径 + $scriptPath = $_SERVER['DOCUMENT_ROOT'] . "/shell/php-test.sh"; + $scriptParam = '1 2 8'; + $cmdStr = "{$scriptPath} {$scriptParam}"; + echo $cmdStr; + // 执行 + exec("{$cmdStr}", $output_result, $return_status); + + // 返回码判断 + if($return_status == self::PERMISSION ){ + echo "chmod u+x ".$scriptPath."
      "; + }elseif ($return_status == self::DOS2UNIX){ + echo "需要使用dos2unix命令将文件转换为unix格式
      "; + }else{ + // 这时候要根据脚本返回的第二个返回值判断脚本具体哪里出错误了 + echo "脚本执行异常 MSg :" . $return_status . "
      "; + if ( isset($output_result[1]) && $output_result[1] == 1) { + echo " MSg1 : ".self::OUTPUT_MSG[$output_result[1]]."
      "; + } elseif (isset($output_result[1]) && $output_result[1] == 2){ + echo " MSg2 : ".self::OUTPUT_MSG[$output_result[1]]."
      "; + } elseif (isset($output_result[1]) && $output_result[1] == 3){ + echo " MSg3 : ".self::OUTPUT_MSG[$output_result[1]]."
      "; + } + } + echo 'success'; + } +``` + + diff --git a/README.md b/README.md index e979382..7096053 100644 --- a/README.md +++ b/README.md @@ -1,171 +1,141 @@ - -## 开发过程记录 - -+ [解决 Visual Studio Code 向github提交代码不用输入帐号密码](#githubpush) - -##
      目录 -+ [**agentzh的Nginx教程(版本2016.07.21)**](https://openresty.org/download/agentzh-nginx-tutorials-en.html#00-foreword01) -+ **Lua网络编程基础知识** - * Lua基础 - * Lua进阶 -+ **Nginx开发从入门到精通** - + [Nginx 编译安装以及参数详解](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/nginx-2-config.md) - + NGINX变量详解 - - [x] [nginx变量使用方法详解笔记(1)](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx-Develop/notes-1.md) - - [x] [nginx变量使用方法详解笔记(2)](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx-Develop/notes-2.md) - - [ ] [nginx变量使用方法详解笔记(3)](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/nginx-2-config.md) - - [ ] [nginx变量使用方法详解笔记(4)](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/nginx-2-config.md) - - [ ] [nginx变量使用方法详解笔记(5)](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/nginx-2-config.md) - + Nginx指令执行顺序 - - [x] [Nginx指令执行命令(01)](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx-Develop/notes-1.md) - - [x] [nginx变量使用方法详解笔记(2)](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx-Develop/notes-2.md) - - [ ] [nginx变量使用方法详解笔记(3)](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/nginx-2-config.md) - - [ ] [nginx变量使用方法详解笔记(4)](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/nginx-2-config.md) - - [ ] [nginx变量使用方法详解笔记(5)](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/nginx-2-config.md) -+ **Nginx高性能WEB服务器详解** - + 第一章 初探 - - [x] [Nginx的历史](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/nginx-2-config.md) - + 第二章 安装部署 - - [ ] 测试一 - - [ ] C213 - * 第三章 Nginx初探 - - [ ] 测试一 - * 第四章 Nginx初探 - - [ ] 测试一 - * 第五章 Nginx初探 - - [ ] 测试一 - * 第六章 Nginx初探 - - [ ] 测试一 - * 第七章 Nginx服务器的代理服务** - - [ ] [正向代理和反向代理的概念](#title) - - [ ] [正向代理服务](#title) - - [ ] [反向代理的服务](#title) - - [x] [Nginx日志服务](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/Nginx-Web/Nginx-2-Log.md) - * 负载均衡 - * HTTP负载均衡 - - [x] [负载均衡五个配置实例](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/Nginx-Web/Nginx-7-Proxy.md) - * TCP负载均衡 - - [ ] [负载均衡](#title) - * 第八章 Nginx初探 - - [ ] 测试一 - * 第九章 Nginx初探 - - [ ] 测试一 - * 第十章 Nginx初探 - - [ ] 测试一 -+ Lua脚本开发Nginx - * 普通文本 - * 单行文本2 -* [Lua脚本运行Redis](#line) - -* [PHP脚本运行Redis](#line) - * [PHP 脚本执行一个Redis 订阅功能,用于监听键过期事件,返回一个回调,API接受改事件](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Redis-PHP/Php-Run-Redis-psubscribe/nohupRedisNotify.php) - * 单行文本1 - -+ **openresty** - - [x] 第一章 与location 配合 使用 - - [ ] 第一章 获取 uri 参数 - -## Redis、Lua、Nginx一起工作事迹 -* 解决一个set_by_lua $sum 命令受上下文限制的解决思路,已完美解决 - - [x] [API disabled in the context of set_by_lua](https://github.com/openresty/lua-nginx-module/issues/275) -* 解决2 -* 解决3 - -## Redis执行Lua脚本示例 -### Lua 基本语法 ---- -* Hello, Lua! - - > 我们的第一个Redis Lua 脚本仅仅返回一个字符串,而不会去与redis 以任何有意义的方式交互 - - ``` - local msg = "Hello, world!" - return msg - ``` - - > 这是非常简单的,第一行代码定义了一个本地变量msg存储我们的信息, 第二行代码表示 从redis 服务端返回msg的值给客户端。 保存这个文件到Hello.lua,像这样去运行: - - ``` - www@iZ239kcyg8rZ:~/lua$ redis-cli EVAL "$(cat Hello.lua)" 0 - "Hello, world!" - ``` - - > 运行这段代码会打印"Hello,world!", EVAL在第一个参数是我们的lua脚本, 这我们用cat命令从文件中读取我们的脚本内容。第二个参数是这个脚本需要访问的Redis 的键的数字号。我们简单的 “Hello Script" 不会访问任何键,所以我们使用0 - -### Lua知识 ---- -##### 基本语法 -* redis.call() 与 redis.pcall()的区别 - - * 他们唯一的区别是当redis命令执行结果返回错误时 - * redis.call()将返回给调用者一个错误. - * redis.pcall()会将捕获的错误以Lua表的形式返回. - * redis.call() 和 redis.pcall() 两个函数的参数可以是任意的 Redis 命令 - -* Lua网络编程 - -### Redis执行Lua脚本基本用法 ---- -* 基本语法 - ``` - EVAL script numkeys key [key ...] arg [arg ...] - ``` -* 通过lua脚本获取指定的key的List中的所有数据 - - ``` - local key=KEYS[1] - local list=redis.call("lrange",key,0,-1); - return list; - ``` -* 根据外面传过来的IDList 做“集合去重”的lua脚本逻辑: - ``` - local result={}; - local myperson=KEYS[1]; - local nums=ARGV[1]; - - local myresult =redis.call("hkeys",myperson); - - for i,v in ipairs(myresult) do - local hval= redis.call("hget",myperson,v); - redis.log(redis.LOG_WARNING,hval); - if(tonumber(hval) 解决 Visual Studio Code 向github提交代码不用输入帐号密码 -* 在命令行输入以下命令 - ``` - git config --global credential.helper store - ``` - - > 这一步会在用户目录下的.gitconfig文件最后添加: - - ``` - [credential] - helper = store - ``` -* push 代码 - - > push你的代码 (git push), 这时会让你输入用户名和密码, 这一步输入的用户名密码会被记住, 下次再push代码时就不用输入用户名密码!这一步会在用户目录下生成文件.git-credential记录用户名密码的信息。 - -* Markdown 的超级链接技术 - - > 【1】需要链接的地址: - - ``` - [解决向github提交代码不用输入帐号密码](#githubpush) - ``` - - > 【2】要链接到的地方: - - ``` - 解决向github提交代码不用输入帐号密码 - ``` - - > 通过【1】和【2】可以很完美的实现一个连接哦! - - - +[![996.icu](https://img.shields.io/badge/link-996.icu-red.svg)](https://996.icu) + +Nginx与Lua编写脚本的基本构建块是指令执行顺序的图 + +![Nginx编译安装](Images/Nginx-Phase.png) + +## Nginx 教程 + +#### 基础 +* [Nginx编译安装](/Nginx/nginx-install.md) +* [Nginx.conf详解](/Nginx/nginx-base-config.md) +* [Location 详解](/docs/Nginx/location-detail.md) +* [Nginx基础知识](/Nginx/nginx-basic.md) +* [Nginx高性能WEB服务器详解](/Nginx/nginx-high-basic.md) +* [Nginx高并发系统内核优化和PHP7配置文件优化](/Nginx/nginx-parameter-config.md) +* [Nginx和PHP-FPM启动脚本](/Nginx/nginx-start-script.md) +* [Nginx的11个Phases](/Nginx/nginx-phases.md) +* [agentzh 的 Nginx 教程](https://openresty.org/download/agentzh-nginx-tutorials-zhcn.html) +* [Nginx 陷阱和常见错误](h/Nginx/nginx-1-config.md) +* [TCP和UDP负载平衡官方参考文档](https://www.cnblogs.com/tinywan/p/6586053.html) +* [Nginx 高并发系统内核优化](/Nginx/nginx-parameter-config.md) +* [nginx 并发数问题思考:worker_connections,worker_processes与 max clients](http://liuqunying.blog.51cto.com/3984207/1420556?utm_source=tuicool) +* [如何在工作中提高Ngixn服务器性能?达到高效](https://juejin.im/post/5adb45e96fb9a07ab773c767?utm_source=gold_browser_extension) +* [并发 = 同步/异步/阻塞/非阻塞/进程/线程](https://juejin.im/post/5bc69ecee51d45395d4f4072) +* [The Complete NGINX Cookbook](docs/Complete-NGINX-Cookbook-2019.pdf) +* [如何改进 NGINX 配置文件节省带宽?](/Nginx/nginx-config-bandwidth.md) +* [Nginx官方文档中文版](https://www.bookstack.cn/read/nginx-official-doc) +* [一个能够快速验证 Nginx 配置的在线演示工具](https://nginx-playground.wizardzines.com/) + +#### 案例 +* [Nginx 同一个IP上配置多个HTTPS主机](/Nginx/more-domain-config.md) +* [Nginx 如何配置一个安全的HTTPS网站服务器](http://www.cnblogs.com/tinywan/p/7542629.html) +* [Nginx 配置启用 HTTP/2](http://www.cnblogs.com/tinywan/p/7860774.html) +* [申请Let's Encrypt通配符HTTPS证书](https://www.cnblogs.com/tinywan/p/8573169.html) +* [如何配置proxy_cache模块](/Nginx/Nginx-Web/Nginx-8-proxy_cache.md) +* [负载均衡五个配置实例](/Nginx/Nginx-Web/Nginx-7-Proxy.md) + +#### 模块 +* [nginx-vod-module](http://www.cnblogs.com/tinywan/p/7879559.html) +* [nginx-module-vts](http://www.cnblogs.com/tinywan/p/7872366.html) +* [ngx_cache_purge](/Nginx/Nginx-Web/Nginx-8-proxy_cache.md) +* [lua-nginx-module](http://www.cnblogs.com/tinywan/p/6538006.html) +* [nginx-rtmp-module](http://www.cnblogs.com/tinywan/p/6639360.html) +* [ngx_http_proxy_module和ngx_http_upstream_module](/Nginx/Nginx-Web/ngx_http_upstream_module.md) + +#### 好文 +* [Nginx入门到实战(1)基础篇](https://segmentfault.com/a/1190000014893012) +* [Nginx入门到实战(2) 场景实现篇](https://mp.weixin.qq.com/s/RDIhU2pd37ecmKjgCtiZGQ) +* [nginx从入门到实践](http://fanqieto.top/2017/11/29/nginx%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%AE%9E%E8%B7%B5/) +* [Nginx中文文档](http://www.nginx.cn/doc/) +* [Nginx开发从入门到精通](http://tengine.taobao.org/book/) +* [关于一些对location认识的误区](http://www.cnblogs.com/lidabo/p/4169396.html) +* [location总结及rewrite规则写法](https://segmentfault.com/a/1190000002797606) +* [How to Compile Nginx From Source on Ubuntu 16.04](https://www.vultr.com/docs/how-to-compile-nginx-from-source-on-ubuntu-16-04) +* [详解:Nginx 反向代理、后端检测模块](https://mp.weixin.qq.com/s/wGOQkAPif3buhezOQhbx5A) +* [Nginx的负载均衡 - 加权轮询 (Weighted Round Robin)](https://blog.csdn.net/zhangskd/article/details/50194069) + +#### 流媒体 +* [Setup Nginx on Ubuntu to Stream Live HLS Video](https://www.vultr.com/docs/setup-nginx-on-ubuntu-to-stream-live-hls-video) +* [Setup Nginx-RTMP on Ubuntu 14.04](https://www.vultr.com/docs/setup-nginx-rtmp-on-ubuntu-14-04) +* [利用nginx搭建RTMP视频点播、直播、HLS服务器](https://blog.csdn.net/kingroc/article/details/50839994) +* [实时流(直播流)播放、上墙(大屏播放)解决方案](https://www.cnblogs.com/xiaozhi_5638/p/8664841.html) +* [Nginx配置Rtmp支持Hls的直播和点播功能](/Nginx-Rtmp/HLS-live-vod.md) +* [HLS视频直播和点播的Nginx的Location的配置信息(成功)](/Nginx-Rtmp/HLS-live-vod-locatiuon-config.md) + +#### 其他 +* [Nginx和PHP-FPM的开机自动启动脚本](/PHP/PHP-FPM/config.md) +* [php-fpm/www.conf 的配置 ](/PHP/PHP-FPM/config.md) +* [深入剖析 Web 服务器与 PHP 应用的通信机制 - 掌握 CGI 和 FastCGI 协议的运行原理](https://mp.weixin.qq.com/s/6Kyfvc_N7PhBtFPstgt3MA) +* [PHP-FPM 与 Nginx 的通信机制总结](https://segmentfault.com/a/1190000018464303) + +## Lua 教程 +* [Lua 基础语法](/Lua-Script/lua-basic.md) +* [luajit 执行文件默认安装路径](#Nginx_base_knowledge) +* [lua中self_indes详解](/Lua-Script/oop/self__index.md) + +## Redis 教程 +* [Redis 安装](/Redis/redis-install.md) +* [Redis 配置详解](/Redis/redis-config.md) +* [Redis 基础知识](#Redis_base_knowledge) +* [Redis 开发与运维](#Redis-DevOps) +* [Redis 执行Lua脚本基本用法](/Redis/redis-lua.md) +* [Redis 漏洞如何防止被黑客攻击](/Redis/redis-safety.md) +* [如何在Ubuntu 16.04上安装和配置Redis](https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-redis-on-ubuntu-16-04) +* [Redis协议规范(译文)](http://www.hchstudio.cn/article/2018/e687/) +* [负载均衡中使用Redis实现共享Session](https://segmentfault.com/a/1190000011558000) +* [Redis 设计与实现](https://github.com/huangz1990/redis-3.0-annotated) +* [为什么分布式一定要有Redis?](https://mp.weixin.qq.com/s/8uii1BzfVfChbH_t5Gk_8Q) +* [使用Lua脚本实现分布式锁](https://www.cnblogs.com/tinywan/p/9643022.html) +* [PHP 脚本执行一个Redis 订阅功能,用于监听键过期事件,返回一个回调,API接受改事件](/Redis-PHP/Php-Run-Redis-psubscribe/nohupRedisNotify.php) +* [阿里云Redis开发规范](https://yq.aliyun.com/articles/531067) +* [高可用Redis服务架构分析与搭建](https://mp.weixin.qq.com/s/DA4uhPULaXI-KDKwvLzb8Q) +* [如何轻松搭建电商秒杀系统](https://yq.aliyun.com/articles/277885) +* [如何在Ubuntu 16.04上安装和配置Redis(推荐)](https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-redis-on-ubuntu-16-04) +* [Codis集群演化与Redis异步迁移](/Redis/Codis集群演化与Redis异步迁移.pdf) +* [如何利用Redisson分布式化传统Web项目](/Redis/如何利用Redisson分布式化传统Web项目.pdf) +* [redis设计与实现(第二版).pdf](/Redis/redis设计与实现(第二版).pdf) +* [深入剖析 Redis5.0 全新数据结构 Streams(消息队列的新选择)](http://www.vlambda.com/wz_wNrPVgCQ43.html) +* [Redis5 配置及优化总结](/Redis/redis5-config.md) + +## Openresty 教程 + +* [安装默认配置信息](/Openresty/openresty-basic.md) +* [OpenResty 最佳实践](https://moonbingbing.gitbooks.io/openresty-best-practices/content/index.html) +* [如何编译一个高性能 OpenResty](https://yq.aliyun.com/articles/228399) +* [ngx_lua APi 方法和常量](/Openresty/openresty-api.md) +* [ngx_lua 扩展模块学习](/Openresty/openresty-resty-module.md) +* [lua-resty-upstream-healthcheck使用](/Openresty/lua-resty-upstream-healthcheck.md) +* [Openresty与Nginx_RTMP](/Openresty/openresty-rtmp.md) +* [自己写的一个简单项目lua_project_v0.01](https://github.com/Tinywan/lua_project_v0.01) +* [如何在openresty里解析域名](http://www.jkeabc.com/181587.html) +* [谈谈基于 OpenResty 的接口网关设计](https://www.zybuluo.com/yishuailuo/note/844059) +* [跟我学OpenResty(Nginx+Lua)开发](http://jinnianshilongnian.iteye.com/blog/2190344) +* [openresty 专栏](https://blog.csdn.net/qq362228416/article/category/6558114) +* [openresty 视频](http://i.youku.com/i/UMTM2NTgyMDEyMA==/videos?q=openresty) +* [模仿京东使用Openresty+Redis做读服务](https://my.oschina.net/zjzhai/blog/759719) +* [Openresty-Lua动态修改upstream后端服务](Nginx/Nginx-Web/openresty-nginx-lua-Proxy.md) +* [Openresty编程.pdf](/Openresty/Openresty编程.pdf) + +## Linux 教程 + +* [命令篇](http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-commands.html) +* [实战篇](http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-part-two.html) +* [定时器教程](http://www.ruanyifeng.com/blog/2018/03/systemd-timer.html) +* [linux 进程间通信之管道](https://juejin.im/post/5bc735d85188255c57216d7e) +* [鸟哥的Linux私房菜服务器架设篇(第三版).pdf](Linux/鸟哥的Linux私房菜服务器架设篇(第三版).pdf) +* [鸟哥的Linux私房菜-基础学习篇(第四版).pdf](Linux/鸟哥的Linux私房菜-基础学习篇(第四版).pdf) + +## Shell 教程 +* [编写快速安全Bash脚本的建议](https://www.oschina.net/translate/bash-scripting-quirks-safety-tips) +* [写好shell脚本的13个技巧](https://mp.weixin.qq.com/s/f3xDHZ7dCQr7sHJ9KDvuyQ) +* [shell脚本实现分日志级别记录日志](/Nginx-Rtmp/Shell_Log.sh) +* [Nginx日志定时备份和删除](/Nginx-Rtmp/Shell_Nginx_Log_cut.sh) +* [SHELL脚本小技巧](/Nginx-Rtmp/Shell_script.md) +* [Mysql 自动备份脚本安全加锁机制](/Nginx-Rtmp/backup_mysql.sh) +* [PHP和Shell 脚本如何很好的搭配](/PHP/php-shell_run.md) +* [通过FTP备份MySQL数据库](/Shell/Backup-MySQL-FTP.md) + +## 微信公众号 + +![Nginx编译安装](Images/tinywan-wechat.jpg) \ No newline at end of file diff --git a/README_BAK.md b/README_BAK.md new file mode 100644 index 0000000..288d3f5 --- /dev/null +++ b/README_BAK.md @@ -0,0 +1,1961 @@ +## 目录 ++ [Nginx基础知识](#Nginx_base_knowledge) ++ [Linux基础知识](#Linux_base_knowledge) ++ [Redis基础知识](#Redis_base_knowledge) + + [Redis 简易安装教程](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Redis/redis-install.md) + + [Redis执行Lua脚本基本用法](#Redis_Run_Lua) ++ [PHP脚本](#PHP_base_knowledge) + + [PHP脚本运行Redis](#PHP_Run_Redis) ++ [Shell脚本](#Shell_base_knowledge) + - [x] 编写快速安全Bash脚本的建议 + - [x] Shell脚本实现分日志级别记录日志 + - [x] Nginx日志定时备份和删除 + - [x] SHELL脚本小技巧 + - [x] Mysql 自动备份脚本安全加锁机制 ++ [Lua基础知识](#Lua_base_knowledge) + + [Lua 基础语法](#Lua-base) + + [luajit 执行文件默认安装路径](#Nginx_base_knowledge) + + [Table 操作常用的方法](#Lua_table) + - [x] table.concat() + - [x] table.insert() + - [x] table.maxn() + - [x] table.concat() + + [Lua 模块与包](#Lua_module_package) + + [lua中self.__index = self 详解](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Lua-Script/oop/self__index.md) ++ [流媒体视频直播、点播](#live_base_knowledge) ++ [Nginx高性能WEB服务器详解](#Nginx_Web_knowledge) + + [第一章 初探 ](#Nginx_Web1_knowledge) + + [第二章 安装部署](#Nginx_Web2_knowledge) + + [第三章 架构初探 ](#Nginx_Web3_knowledge) + + [第四章 高级配置 ](#Nginx_Web4_knowledge) + + [第五章 Gzip压缩](#Nginx_Web5_knowledge) + + [第六章 Rewrite 功能](#Nginx_Web6_knowledge) + + [第七章 代理服务 ](#Nginx_Web7_knowledge) + + [第八章 缓存机制 ](#Nginx_Web8_knowledge) ++ [Openresty 学习](#Openresty_web_knowledge) + + [安装](#Openresty_install_knowledge) + + [默认配置信息](#Openresty_config_knowledge) + + [Lua require 绝对和相对路径(已经解决)](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Openresty/lua-common-package/lua-require.md) + + [luajit 执行文件默认安装路径](#Openresty_web_knowledge) + + [lua-resty-redis 扩展](#Openresty_resty-redis) + + [lua-resty-websocket 扩展](#Openresty_resty-websocket) + + [lua-cjson 扩展](#Openresty_resty-cjson) + + [lua-dkjson 扩展](https://github.com/Tinywan/lua_project_v0.01) + + [Lua 权限验证](#Openresty_resty-access) + + [lua-resty-string 扩展](#Openresty_resty-string) + + [lua-resty-http 扩展 ](#Openresty_resty-http) + + [lua-resty-mysql 扩展](#Openresty_resty-mysql) + + [lua-resty-shell 扩展](http://www.cnblogs.com/tinywan/p/6809879.html) + + [lua-resty-template 扩展](https://github.com/Tinywan/lua_project_v0.01) + + [lua-resty-template 扩展](https://github.com/Tinywan/lua_project_v0.01) + + [openresty扫描代码全局变量](#Openresty_all-var) + + [ngx Lua APi 方法和常量](#Openresty_http_status_constants) + + ngx_lua 核心常量 + + ngx_lua 方法 + + Lua HTTP状态常量 + + 错误日志级别常量 + + API中的常用方法 + + [ngx Lua APi 介绍使用](#Openresty_ngx_api_used) + + [连接数据库](#Openresty_connent_redis) + + [OpenResty缓存](#Openresty_connent_cache) + + [lua-resty-upstream-healthcheck 使用](#Openresty_lua_resty_upstream_healthcheck) + + [Openresty和Nginx_RTMP 模块共存问题](#Openresty_rtmp_share) + + [Openresty配置RTMP模块的多worker直播流](#Openresty_rtmp_more_worker) + + [Openresty配置RTMP模块的推流地址鉴权实例](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx-Rtmp/Openresty_rtmp_obs_push.md) + + [Ngx_lua 写入Redis数据,通过CURL请求](#Ngx_lua_write_Redis) + + [Nginx编写的Lua接口使用URL过期和签名验证机制过滤非法访问接口](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx-Rtmp/Openresty_rtmp_obs_push_auth.md) + + [Nginx查看并发连接数](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx-Rtmp/nginx_status.md) ++ [Redis 开发与运维](#Redis-DevOps) + - [ ] [第一章 初始Redis ](#Redis-DevOps-1) + - [ ] [第二章 API的理解和使用](#Redis-DevOps-2) + - [ ] [第三章 小功能大用处 ](#Redis-DevOps-3) + - [ ] [第四章 客户端 ](#Redis-DevOps-4) + - [ ] [第五章 持久化](#Redis-DevOps-5) + - [ ] [第六章 复制](#Redis-DevOps-6) + - [ ] [第七章 Redis 的恶魔 ](#Redis-DevOps-7) + - [ ] [第八章 理解内存 ](#Redis-DevOps-8) ++ [Copyright and License](#Copyright_and_License) +## 开发过程记录 ++ [解决 Visual Studio Code 向github提交代码不用输入帐号密码](#githubpush) ++ phase的意义:就是几个MR的一个集合,不定数目的MR job视为一个phase。一个请求经过nginx处理的过程中,会经过一系列的阶段(phases) +## Nginx基础知识 ++ [NGINX 所有 Modules](https://www.nginx.com/resources/wiki/modules/) ++ [Nginx 配置文件 nginx.conf 详解](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/nginx-base-config.md) +#### agentzh的Nginx教程(版本2016.07.21) ++ [agentzh的Nginx教程地址](https://openresty.org/download/agentzh-nginx-tutorials-zhcn.html) ++ Nginx 变量漫谈(一) + + Nginx 变量的值只有一种类型,那就是字符串 + + Nginx “变量插值” + ```bash + location /test { + set $first "hello "; + echo "${first}world"; + } + ``` + + set 指令(以及前面提到的 geo 指令)不仅有赋值的功能,它还有创建 Nginx 变量的副作用,即当作为赋值对象的变量尚不存在时 + + Nginx 变量一旦创建,其变量名的可见范围就是整个 Nginx 配置,甚至可以跨越不同虚拟主机的 server 配置块 + + Nginx 变量的生命期是不可能跨越请求边界的 ++ Nginx 变量漫谈(二) + + 跳转 + + 内部跳转:就是在处理请求的过程中,于服务器内部,从一个 location 跳转到另一个 location 的过程。 + + 外部跳转: HTTP 状态码 301 和 302 所进行的“外部跳转” + + 标准 ngx_rewrite 模块的 rewrite 配置指令其实也可以发起“内部跳转” + + Nginx 核心和各个 Nginx 模块提供的“预定义变量” + + Nginx 会在匹配参数名之前,自动把原始请求中的参数名调整为全部小写的形式 + + 如果你尝试改写另外一些只读的内建变量,比如 $arg_XXX 变量,在某些 Nginx 的版本中甚至可能导致进程崩溃。 ++ Nginx 变量漫谈(四) + + map 指令:用于定义两个 Nginx 变量之间的映射关系,或者说是函数关系 + + map 指令只能在 http 块中使用 + + map 配置指令的工作原理是为用户变量注册 “取处理程序”,并且实际的映射计算是在“取处理程序”中完成的,而“取处理程序”只有在该用户变量被实际读取时才会执行(当然,因为缓存的存在,只在请求生命期中的第一次读取中才被执行),所以对于那些根本没有用到相关变量的请求来说,就根本不会执行任何的无用计算。 ++ Nginx 变量漫谈(四) ++ [Nginx的11个Phases](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/nginx-phases.md) ++ [Nginx 陷阱和常见错误](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/nginx-1-config.md) ++ [Nginx 高并发系统内核优化](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/nginx-parameter-config.md) + +## Redis基础知识 ++ [Redis 简易安装教程](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Redis/redis-install.md) +## PHP脚本 ++ [PHP7中php.ini/php-fpm/www.conf的配置,Nginx和PHP-FPM的开机自动启动脚本](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/PHP/PHP-FPM/config.md) +## Shell脚本 ++ [编写快速安全Bash脚本的建议](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Shell/write-shell-suggestions.md) ++ [shell脚本实现分日志级别记录日志](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx-Rtmp/Shell_Log.sh) ++ [Nginx日志定时备份和删除](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx-Rtmp/Shell_Nginx_Log_cut.sh) ++ [SHELL脚本小技巧](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx-Rtmp/Shell_script.md) ++ [Mysql 自动备份脚本安全加锁机制](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx-Rtmp/backup_mysql.sh) +## Lua基础知识 +#### Lua 基础语法 ++ 删除一个全局变量,只要将变量值赋值为nil:`a = nil`,当且仅当一个变量不为nil 时,这个变量存在 ++ `Boolean`类型:在控制条件中除了`false`和`nil` 为假,其他值都为真,所以lua认为0和空字符串也是真 ++ `String`类型: + + 字符串替换:`string.gsub()` + ```lua + a = 'one HELLO' + b = string.gsub(a,'one','two') + print(a) -- one HELLO + print(b) -- two HELLO + ``` + + 字符串和数字 + ```lua + print("10" + 1) -- 11 + print("10" + "1") -- 11 + print("10 + 1") -- 10 + 1 + print("hello " .. " world") -- hello world + --print("hello" + 1) -- 错误写法 + ``` + + 字符串和数字转换 + ```lua + a = 10 + print(tostring(a)) -- 10 + b = "20" + print(tonumber(b)) -- "20" + + print(tostring(10) == "10") -- true + print(10 .. "" == "10") -- true + ``` ++ 表达式 + + 如果两个值类型不相等,Lua认为两者不同 + + nil 只和自己相等 + + 逻辑运算符 + ```lua + -- a and b -- 如果a为false,则返回a ,否则返回b + -- a or b -- 如果a为true,则返回a ,否则返回b + print(4 and 5) -- 5 + print(nil and 12 ) -- nill + print(false and 12) -- false + print(4 or 5) -- 4 + print(false or 5) -- 5 + ``` + + 注意:`and`的优先级比`or`高 + + Lua 三元运算符:`( a and b) or c` ++ 变量 + + 赋值语句 + ```lua + x = 20 + y = 30 + x,y = y,x + print(x,y) -- 30 20 + + a,b,c = 10,20 + print(a,b,c) --10 20 nil + + x,y,z = 10 + print(x,y,z) -- 10 nil nil + ``` + + 局部变量与代码块 + + 代码块:指一个控制结构内,一个函数体,或者一个chunk(变量被声明的哪个文件或者文本串) + + ```lua + a = 12 + if a>10 then + local i = 19 + print(i) -- 19 + end + print(i) -- nil + ``` ++ 控制语句 + ```lua + members = { Tom = 10, Jake = 11, Dodo = 12, Jhon = 16 } + + for k, v in pairs(members) do + if v == 10 then + print(k, 'is 10 years old') -- Tom is 10 years old + elseif v == 11 then + print(k, 'is 11 years old') -- Jake is 11 years old + elseif v == 12 then + print(k, 'is 12 years old') -- Dodo is 12 years old + else + print(k, "is not 10,11,12 years old") -- Jhon is not 10,11,12 years old + end + end + ``` ++ 函数 + + 单个返回值 + ```lua + function max(a,b) + if a > b then + return a + else + return b + end + end + print(max(10,20)) -- 20 + ``` + + 多个返回值 + ```lua + function more() + return 10 , 20 ,30 + end + a , b , c = more() + print(a,b,c) -- 10 20 30 + ``` + + 可变数目的参数 + ```lua + function more() + return 10 , 20 ,30 + end + -- 当函数位于最后一位的时候,返回全部值,否则值返回一个数值 + a , b , c ,d = 100, more() + print(a,b,c,d) -- 100 10 20 30 + ``` + + 闭合函数 + ```lua + function count() + -- i属于一个非局部变量,因为它既不是全局变量,也不是单纯的局部变量(因为另外一个函数可以访问到它) + local i = 0 + return function() + i =i +1 + return i + end + end + -- 以上 count()函数里的那个函数,加上一个非全局变量i,就构成一个闭合函数 + -- 所以每次调用闭合函数,非局部变量的值都不会被重置 + local func = count() + print(func()) -- 1 + print(func()) -- 2 + ``` + + 非全局函数,在定义函数的使用要注意定义函数的顺序 + ```lua + local eat + local drink + eat = function() + print("eat") + return drink() -- 这里的drink()属于尾调用 + end + drink = function() + print("drink") + end + eat() + ``` ++ table 使用 + + Lua table 第一个索引为1 + + 简单 + ```lua + a = {} + a.x = 100 + a.y = 200 + a["z"] = 300 -- a.z = 300 + print(a.x) -- 100 + print(a.y) -- 200 + print(a.z) -- 300 + ``` ++ 泛型迭代器 + + 标准库迭代器包括: + + 迭代文件每行:`io.lines` + + 迭代table元素:`pairs` + - [x] 可以遍历表中的所有key + - [x] 并且除了迭代器本身以及遍历表本身,还可以返回nil + + 迭代数组元素:`ipairs` + - [x] ipairs不能返回nil,只能返回数字0,如果遇到nil则退出 + - [x] 只能遍历表中出现的第一个不是整数的key + + 泛型迭代器 + ```lua + config = {host = '127.0.0.1',port = '3306', dbname = 'LuaDB' } + config.redis_host = "192.168.1.1" + config.redis_port = "6379" + config.redis_db = "12" + print(config['redis_host']) -- 192.168.1.1 + print(config.redis_port) -- 6379 + print(config.dbname) -- LuaDB + + for k, v in pairs(config) do + print(k,v) + end + + --[[ + host 127.0.0.1 + dbname LuaDB + redis_host 192.168.1.1 + redis_db 12 + redis_port 6379 + port 3306 + -- ]] + ``` + + 迭代table元素 + ```lua + arr = {} + for var = 1,100 do -- for 循环 + table.insert(arr,1,var) + end + + for k, v in pairs(arr) do -- 遍历表 + print(k,v) + end + --[[ 打印结果 + 1 100 + 2 99 + ... ... + 99 2 + 100 1 + + -- ]] + print(table.maxn(arr)) -- table长度 100 + print(#arr) -- table长度(快捷方式) 100 + ``` + + 迭代数组元素:`ipairs` + ```lua + arr = {host = '127.0.0.1',port = '3306','Tinywan'} + -- 如果没有找到下标为整数的则直接退出,是整数的则直接输出,如上面的'Tinywan' + for k, v in ipairs(arr) do -- 只能遍历key 为整数的下标 + print(k,v) -- 1 Tinywan + end + ``` + + 循环迭代table元素(如:lua-resty-mysql 扩展查询的数据) + + 查询 :`res, err, errcode, sqlstate = db:query("select * from tb_ngx_test order by id asc", 10)` + + 转换成JSON结果集输出2条记录: + ```lua + ngx.say("result: ", cjson.encode(res)) + result: [{"age":"24123","name":"tinywan123","address":"China","id":"1"},{"age":"24","name":"tinywan","address":"China","id":"2"}] + ``` + + 遍历该结果集: + ```lua + res, err, errcode, sqlstate = db:query("select * from tb_ngx_test order by id asc", 10) + if not res then + ngx.say("bad result: ", err, ": ", errcode, ": ", sqlstate, ".") + return + end + + for k, v in pairs(res) do + if type(v) == "table" then + for new_table_index, new_table_value in pairs(v) do + ngx.say(new_table_index.." = "..new_table_value) + end + else + ngx.say(k,v) + end + end + + --[[ 打印结果 + age = 24123 + name = tinywan123 + address = China + id = 1 + age = 24 + name = tinywan + address = China + id = 2 + ]] + ``` + + json 和 lua table 转换 + + [1] 将 json 转换成 lua table + ```lua + local json_str = '{"is_male":"nan","name":"zhangsan","id":1}' + local t = json.decode(json_str) + ngx.say(format_table(t)) + ``` + + [2] 将 lua table 转换成 json 字符串 + ```lua + local t = [[{key="table key",value="table value"}]] + local json_str = json.encode(t) + ngx.say(json_str) -- "{key=\"table key\",value=\"table value\"}" + ``` + + [3] 将lua table转换成 json 数组 (lua 两个大括号表示一个数组) + ```lua + local t = {keys={"list1","list2","list3"},num=1} + local str = json.encode(t) + ngx.say(str) -- {"keys":["list1","list2","list3"],"num":1} + ``` ++ 编译执行与错误 + + error 错误 + ```lua + local name = "Lua1" + if name ~= "Lua" + then + error("this is not Lua "); + end + ``` + + assert 错误:`assert(name~="Lua"," this is not Lua")` + + pcall 捕获错误代码 + ```lua + function test() + print(a[1]) + end + -- pcall 除了会返回true或者false外,还能返回函数的错误信息。 + -- 如果没有错误信息,err 会返回一个nil + local status,err = pcall(test) + if status then + print('success') + else + print('函数执行出错了') + print('错误信息:',err) + end + ``` ++ Lua面向对象(重点) + + [博客详细地址描述](http://www.cnblogs.com/tinywan/p/6940784.html) + + :white_check_mark: `__add` 元方法 #demo1 + ```lua + local mt = {} + mt.__add = function(t1, t2) + print("两个Table 相加的时候会调用我") + end + local t1 = {} + local t2 = {} + -- 给两个table 设置新的元表,一个元表就是一个table的值 + setmetatable(t1, mt) -- meta:元素 + setmetatable(t2, mt) + -- 进行相加操作 + local t = t1 + t2 + print(t) + + --[[输出结果 + 两个Table 相加的时候会调用我 + nil + --]] + ``` + + :white_check_mark: `__add` 元方法 #demo2 + ```lua + -- 创建一个元表 (是创建一个类吗?) + local mt = {} + mt.__add = function(s1, s2) + local result = "" + if s1.sex == "boy" and s2.sex == "girl" then + result = "一个男孩和一个女孩的家庭" + elseif s1.sex == "girl" and s2.sex == "girl" then + result = "两个女孩的家庭" + else + result = "未知孩子的家庭" + end + return result + end + -- 创建两个table,可以想象成是两个类的对象(实例化两个类) + local s1 = { name = "Per1", sex = "boy" } + local s2 = { name = "Per2", sex = "girl" } + -- 给两个table 设置新的元表,一个元表就是一个table的值 + setmetatable(s1, mt) + setmetatable(s2, mt) + -- 进行加法操作 + local result = s1 + s2 + print(result) + + -- 输出结果 一个男孩和一个女孩的家庭 + ``` + + :white_check_mark: `__index` 元方法 #demo1 + ```lua + local t = { + name = "Tinywan" + } + local mt = { + __index = function(table, key) + print("虽然你调用了我不存在的字段和方法,不过没关系,我能检测出来" .. key) + end + } + setmetatable(t, mt) + print(t.name) + print(t.age) + + --[[输出结果 + -- Tinywan + -- 虽然你调用了我不存在的字段和方法,不过没关系,我能检测出来age + -- nil + ---- ]] + ``` + + :white_check_mark: `__index` 元方法 #demo2 + ```lua + local t = { + name = "Tinywan" + } + local mt = { + money = 808080 + } + + mt.__index = mt + setmetatable(t, mt) + print(t.money) + -- 输出结果 808080 + ``` + + :white_check_mark: `__index` 元方法 #demo3 + ```lua + local t = { + name = "Tinywan" + } + local mt = { + __index = { + money = 909090 + } + } + setmetatable(t, mt) + print(t.money) + -- 输出结果 909090 + ``` + + :white_check_mark: `__index` 元方法 #demo4 + ```lua + local smartMan = { + name = "Tinywan", + age = 26, + money = 800000, + say_fun = function() + print("Tinywan say 大家好") + end + } + + local t1 = {} + local t2 = {} + local mt = { __index = smartMan } -- __index 可以是一个表,也可以是一个函数 + setmetatable(t1, mt) + setmetatable(t2, mt) + print(t1.money) + t2.say_fun() + --- 输出结果 + -- 800000 + -- Tinywan say 大家好 + ``` + + Lua面向对象1 + + Lua面向对象1 + + Lua面向对象3 更新中... ++ Lua 排序算法 + + [Lua 排序算法 - 选择排序](https://www.openresty.com.cn/ms2008-select-sort.html#section-1) + + 选择排序 + ```lua + local function selectionSort(arr) + for i = 1,#arr-1 do + local idx = i + -- 迭代剩下的元素,寻找最小的元素 + for j = i+1,#arr do + if arr[j] < arr[idx] then + idx = j + end + end + -- + arr[i],arr[idx]= arr[idx],arr[i] + end + end + + local list = { + -81, -93, -36.85, -53, -31, 79, 45.94, 36, 94, -95.03, 11, 56, 23, -39, + 14, 1, -20.1, -21, 91, 31, 91, -23, 36.5, 44, 82, -30, 51, 96, 64, -41 + } + + selectionSort(list) + print(table.concat( list, ", ")) + ``` +#### 控制结构 ++ [if-elseif-end 语句](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Lua-Script/chapter-one/if-else-example.lua) ++ [for 语句](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Lua-Script/chapter-one/for-example.lua) ++ [Lua 只有一个容器,那就是table](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Lua-Script/chapter-one/container-table.lua) +#### [Lua 实现简单封装](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Lua-Script/function1.lua) +#### Table 操作常用的方法 ++ table.concat (table [, sep [, start [, end]]]) + + concat是concatenate(连锁, 连接)的缩写. table.concat()函数列出参数中指定table的数组部分从start位置到end位置的所有元素, 元素间以指定的分隔符(sep)隔开 + + demo + ```lua + fruits = {"banana","orange","apple"} + -- 返回 table 连接后的字符串 value = banana orange apple + print("连接后的字符串 ",table.concat(fruits)) + -- 指定连接字符 value = banana, orange, apple + print("指定连接字符连接后的字符串 ",table.concat(fruits,", ")) + ``` ++ table.insert (table, [pos,] value): + + 在table的数组部分指定位置(pos)插入值为value的一个元素. pos参数可选, 默认为数组部分末尾 + + demo + ```lua + fruits = {"banana","orange","apple"} + + -- 在末尾插入 + table.insert(fruits,"Tinywan4") + print("索引为 4 的元素为 ",fruits[4]) -- 索引为 4 的元素为 Tinywan + + -- 在索引为 2 的键处插入 + table.insert(fruits,2,'Tinywan2') + print("索引为 2 的元素为 ",fruits[2]) -- 索引为 2 的元素为 Tinywan + + print("最后一个元素为 ",fruits[5]) -- 最后一个元素为 Tinywan4 + table.remove(fruits) + print("移除后最后一个元素为 ",fruits[5]) -- 移除后最后一个元素为 nil + ``` ++ table.sort (table [, comp]) + + 对给定的table进行升序排序 +#### Lua 模块与包 ++ 定义:Lua 的模块是由变量、函数等已知元素组成的 table,因此创建一个模块很简单,就是创建一个 table,然后把需要导出的常量、函数放入其中,最后返回这个 table 就行. +## 流媒体视频直播、点播 ++ [Nginx配置Rtmp支持Hls的直播和点播功能](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx-Rtmp/HLS-live-vod.md) ++ [HLS视频直播和点播的Nginx的Location的配置信息(成功)](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx-Rtmp/HLS-live-vod-locatiuon-config.md) + +## Nginx高性能WEB服务器详解 +#### 第一章 初探 ++ [Nginx 编译安装以及参数详解](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/nginx-2-config.md) ++ NGINX变量详解 + - [x] [nginx变量使用方法详解笔记(1)](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx-Develop/notes-1.md) + - [x] [nginx变量使用方法详解笔记(2)](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx-Develop/notes-2.md) + - [x] [nginx变量使用方法详解笔记(3)](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/nginx-2-config.md) ++ Nginx指令执行顺序 + - [x] [Nginx指令执行命令(01)](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx-Develop/command-order-01.md) +#### 第二章 安装部署 ++ 启动错误:`Nginx [emerg]: bind() to 0.0.0.0:80 failed (98: Address already in use)`,执行:`sudo fuser -k 80/tcp` ++ [基于域名、IP的虚拟主机配置](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/Nginx-Web/Nginx-2-4-all-config.md) ++ [完整、标准配置实际示列](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/Nginx-Web/Nginx-2-4-basic-config.md) ++ [日志文件配置与切割](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/Nginx-Web/Nginx-2-4-log-cut.md) ++ alias 和 root 在location 下的应用 + - 通过alias 实现别名功能 + ``` + location /live { + alias /home/tinywan/HLS/; + } + ``` + - curl 请求结果 + ``` + tinywan@tinywan:~/HLS$ cat index.html + alias /home/tinywan/HLS/index.html + tinywan@tinywan:~/HLS$ curl http://127.0.0.1/live/index.html + alias /home/tinywan/HLS/index.html + ``` + - 结论: + 1. cul 请求 `/live/index.html`,那么Nginx将会在服务器上查找`/home/tinywan/HLS/index.html` 文件 + 1. 请求的`url` 中的`location`后面的部分会被追加到`alias `指定的目录后面,而`location`后面的`/live`路径将会别自动抛弃 + - 类似案例[2]: + - config配置信息 + ``` + location ~ ^/live/(.*)$ { + alias /home/tinywan/HLS/$1; + } + ``` + - curl 请求结果 + ``` + tinywan@tinywan:~/HLS$ pwd + /home/tinywan/HLS + tinywan@tinywan:~/HLS$ cat txt.txt + txt file + tinywan@tinywan:~/HLS$ curl http://127.0.0.1/live/txt.txt + txt file + ``` + - 如果url请求`/live/txt.txt`那么Nginx将会在服务器上查找`/home/tinywan/HLS/txt.txt` 文件 + - **与root 功能的差别**: + - config配置信息,注意:一下的`alias` 换成 `root ` + ``` + location ~ ^/live/(.*)$ { + root /home/tinywan/HLS/$1; + } + ``` + - curl 请求结果 + ``` + tinywan@tinywan:~/HLS$ curl http://127.0.0.1/live/txt.txt + + 404 Not Found + +

      404 Not Found

      +
      openresty/1.11.2.1
      + + + ``` + - 日志文件信息(打开Nginx的rewrite日志:rewrite_log on;): + ``` + /home/tinywan/HLS/txt.txt/live/txt.txt + ``` + - **二者的区别** + 1. `alias` 指定的目录是当前目录 + 1. `root` 指定的是根目录 + 1. 一般建议的`location /`中通过`root`命令配置目录,其他目录匹配的位置使用`alias`命令 + - 案例[3]: + - config配置信息 + ``` + location ~ ^/live/(\w+)/(.*) { + alias /home/tinywan/HLS/live/$1/$2; + } + ``` + - curl 请求结果 + ``` + tinywan@tinywan:~/HLS/live/stream123$ pwd + /home/tinywan/HLS/live/stream123 + tinywan@tinywan:~/HLS/live/stream123$ cat index.m3u8 + 12312312312 + tinywan@tinywan:~/HLS/live/stream123$ curl "http://127.0.0.1/live/stream123/index.m3u8?token=1234&api=009132" + 12312312312 + ``` +####
      第三章 架构初探 +- [ ] 测试一 +#### 第四章 高级配置 ++ 基本语法:location [=|~|~*|^~] /uri/ { … } + 1. `= `:严格匹配。如果这个查询匹配,那么将停止搜索并立即处理此请求。 + 2. `~ `:为区分大小写匹配(可用正则表达式) + 3. `!~ `:为区分大小写不匹配 + 4. `!~*`:为不区分大小写不匹配 + 5. ` ^~ `:如果把这个前缀用于一个常规字符串,那么告诉nginx 如果路径匹配那么不测试正则表达式 ++ [Perl 正则表达式参考](http://www.runoob.com/perl/perl-regular-expressions.html) ++ 正则中需要转义的特殊字符小结 + - [1] ` $` 匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 ‘\n' 或 ‘\r'。要匹配 $ 字符本身,请使用 \$。 + - [2] ` ( )` 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 和。 + - [3] ` * ` 匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 \*。 + - [4] ` +` 匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 \+。 + - [5] ` . ` 匹配除换行符 \n之外的任何单字符。要匹配 .,请使用 \。 + - [6] ` [ ]` 标记一个中括号表达式的开始。要匹配 [,请使用 \[。 + - [7] ` ? ` 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用 \?。 + - [8] ` \ ` 将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如, ‘n' 匹配字符 ‘n'。'\n' 匹配换行符。序列 ‘\\' 匹配 “\”,而 ‘\(' 则匹配 “(”。 + - [9] ` ^ ` 匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合。要匹配 ^ 字符本身,请使用 \^。 + - [10] ` { }` 标记限定符表达式的开始。要匹配 {,请使用 \{。 + - [11] ` | ` 指明两项之间的一个选择。要匹配 |,请使用 \|。 + ++ 正则表达式 (Regular expression) 匹配location + - [1] `location ~* \.(gif|jpg|jpeg)$ { }`:匹配所有以 gif,jpg或jpeg 结尾的请求 + - [2] `location ~ /documents/Abc { }`:匹配任何以 /documents/ 开头的地址,匹配符合以后,还要继续往下搜索 + - [3] **目录匹配:** + 1. 可以匹配静态文件目录`(static/lib)` + 2. HLS直播目录`(/home/HLS/stream123/index.m3u8)` + 3. HLS/MP4/FLV点播视频目录`(/home/HLS/stream123.m3u8)` + 4. 匹配URL地址:`http://127.0.0.1/live/stream123/index.m3u8` + 5. nginx.conf 配置信息 + ``` + # 匹配任何以/live/ 开头的任何查询并且停止搜索。任何正则表达式将不会被测试 + location ^~ /live/ { + root /home/tinywan/HLS/; + } + # 以上匹配成功后的组合:/home/tinywan/HLS/live/.... + ``` ++ 后缀匹配 + 1. 匹配任何后缀文件名`gif|jpg|jpeg|png|css|js|ico|m3u8|ts` 结尾的请求 + 2. TS 文件匹配`http://127.0.0.1/live/stream123/11.ts` + 3. M3U8 文件匹配`http://127.0.0.1/live/stream123/index.m3u8` + 4. 匹配URL地址:`http://127.0.0.1/hls/123.m3u8` + 5. nginx.conf 配置信息 + ``` + location ~* \.(gif|jpg|jpeg|png|css|js|ico|m3u8|ts)$ { + root /home/tinywan/HLS/; + } + ``` ++ HSL直播目录匹配实际案例(请测试上线) + 1. 可以后缀文件名:`http://127.0.0.1/live/stream123/index.m3u8` + ``` + location ^~ /live/ { + root /home/tinywan/HLS/; + } + ``` + ++ [nginx配置location总结及rewrite规则写法](http://seanlook.com/2015/05/17/nginx-location-rewrite/) +#### 第五章 Gzip压缩 ++ 测试一 +#### 第六章 Rewrite 功能 ++ Rewrite 常用全局变量 + + 请求案例: `curl -G -d "name=Tinywan&age=24" http://127.0.0.1/rewrite_var/1192/index.m3u8` + + 接受结果: + + | 变量 | 值 |描述 | + | --------- | ----------- |----------- | + | $args | name=Tinywan&age=24 |存放URL 请求的指令 | + | $content_length | 0 | 请求头中的Content-length字段| + | $content_type | 0 |请求头中的Content-Type字段 | + | $document_root | /opt/openresty/nginx/html | 当前请求在root指令中指定的值 | + | $document_uri | /rewrite_var/1192/index.m3u8 | 与$uri相同 | + | $host | 127.0.0.1 |请求主机头字段,否则为服务器名称 | + | $http_user_agent | curl/7.47.0 | 客户端agent信息| + | $http_cookie | 0 | COOKIE变量的值| + | $limit_rate | 0 | 限制连接速率| + | $request_body_file | null | 客户端请求主体信息的临时文件名| + | $request_method | GET | 客户端请求的动作,通常为GET或POST | + | $remote_addr | 127.0.0.1 |客户端的IP地址 | + | $remote_port | 33516 |客户端端口| + | $remote_user | 0 | 已经经过Auth Basic Module验证的用户名| + | $request_filename | /opt/openresty/nginx/html/rewrite_var/1192/index.m3u8 |当前请求的文件路径 | + | $request_uri | /rewrite_var/1192/index.m3u8?name=Tinywan&age=24 |包含请求参数的原始URI,不包含主机名 | + | $query_string | name=Tinywan&age=24 | 与$args相同| + | $scheme | http |HTTP方法(如http,https | + | $server_protocol | HTTP/1.1 |请求使用的协议,通常是HTTP/1.0或HTTP/1.1 | + | $server_addr | 127.0.0.1 |服务器地址 | + | $server_name | localhost | 服务器名称| + | $server_port | 80 |请求到达服务器的端口号 | + | $uri | /rewrite_var/1192/index.m3u8 | 不带请求参数的当前URI| + | $binary_remote_addr | 乱码 | 二进制格式的客户端地址| + + + uri 介绍 **(Nginx中的URI是相对的URI)** + + URL:`https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/config.md` + + 绝对URI:`https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/config.md` + + 相对URI:`/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/config.md` + ![Markdown](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Images/URI-URL-Image.jpg) + ++ Rewrite 正则匹配` uri `参数接收 + 1. 请求案例:`curl http://192.168.18.143/live/tinywan123/index.m3u8` + 2. Nginx.conf配置文件 + ```Lua + location ~* ^/live/(\w+)/(\D+)\.(m3u8|ts)$ { + set $num $2; + set $arg1 $1; + echo "args === ${arg1}"; + echo "1==$1 2==$2 3==$3"; + echo "Total_numbser :: $num"; + echo "URI $uri"; + } + + ``` + 3. 输出结果 + ``` + args === tinywan123 + $1==tinywan123 $2==index $3==m3u8 + Total_numbser :: index + URI /live/tinywan123/index.m3u8 + Total_numbser :: + ``` + 4. $1为正则匹配多个英文字母或数字的字符串 `(\w+)` + $2 为正则匹配多个非数字 `(\D+)` + $3 为正则匹配的第一个值 `(m3u8|ts)` + `.` 需要用转义字符转义`\.` +## 第七章 代理服务 ++ [正向代理和反向代理的概念](#title) ++ [正向代理服务](#title) ++ [反向代理的服务](#title) ++ [Nginx日志服务](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/Nginx-Web/Nginx-2-Log.md) ++ 负载均衡 ++ HTTP负载均衡 + - [x] [简单的负载平衡](http://nginx.org/en/docs/http/ngx_http_core_module.html?&_ga=1.179030369.49817296.1480411319#http) + - [x] [简单的负载平衡](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/Nginx-Web/Nginx-7-Proxy-1.md) + - [x] [负载均衡五个配置实例](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/Nginx-Web/Nginx-7-Proxy.md) + - [x] [Openresty-Lua动态修改upstream后端服务](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/Nginx-Web/openresty-nginx-lua-Proxy.md) ++ TCP负载均衡 + - [x] [Module ngx_stream_core_module](http://nginx.org/en/docs/stream/ngx_stream_core_module.html#stream) + - [x] [负载均衡](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/Nginx-Web/Nginx-8-tcp-Proxy.md) ++ proxy_pass 代理的URL总结 + + 在nginx中配置proxy_pass时,当在后面的url加上了/,相当于是绝对根路径,则nginx不会把location中匹配的路径部分代理走;如果没有/,则会把匹配的路径部分也给代理走。 + + 将url中以/wap/开头的请求转发到后台对应的某台server上,注意最后的?$args,表明把原始url最后的get参数也给代理到后台 + ```bash + location ~* /wap/(\d+)/(.+) + { + proxy_pass http://mx$1.test.com:6601/$2?$args; + } + ``` + + 第一种配置,访问:`http://127.0.0.1/proxy/index.html` 会被代理到:`http://127.0.0.1:8000/index.html` + ```bash + location /proxy/ { + proxy_pass http://127.0.0.1:8000/; + } + ``` + + 第二种配置,访问:`http://127.0.0.1/proxy/index.html` 会被代理到:`http://127.0.0.1:8000/proxy/index.html` + ```bash + location /proxy/ { + proxy_pass http://127.0.0.1:8000; + } + ``` + + 第三种配置,访问:`http://127.0.0.1/proxy/index.html` 会被代理到:`http://127.0.0.1:8000/video/index.html` + ```bash + location /proxy/ { + proxy_pass http://127.0.0.1:8000/video/; + } + ``` + + 第四种配置,访问:`http://127.0.0.1/proxy/index.html` 会被代理到:`http://127.0.0.1:8000/videoindex.html` + ```bash + location /proxy/ { + proxy_pass http://127.0.0.1:8000/video; + } + ``` ++ location 直接访问: + + 以下配置,当访问:`http://127.0.0.1:8000/proxy/index.html` 会被匹配到:`/usr/local/nginx/html/proxy/index.html` + ```bash + location /proxy/ { + root /usr/local/nginx/html; + index index.html index.htm; + } + ``` + +## 第八章 缓存机制 ++ 测试一 +## 第九章 Nginx初探1 ++ 测试一 +## 第十章 Nginx初探1 ++ 测试一 +## PHP脚本运行Redis] ++ [PHP 脚本执行一个Redis 订阅功能,用于监听键过期事件,返回一个回调,API接受改事件](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Redis-PHP/Php-Run-Redis-psubscribe/nohupRedisNotify.php) ++ 单行文本1 +## Openresty 学习 ++ [安装信息](http://www.cnblogs.com/tinywan/p/6647587.html) ++ [默认配置信息](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Openresty/default-config.md) ++ 开发入门 + + Nginx与Lua的整体目录关系 + ```javascript + . + ├── conf + │ ├── nginx.conf -- Nginx 配置文件 + ├── logs + │ ├── error.log -- Nginx 错误日子 + │ └── nginx.pid + ├── lua + │ ├── m3u8_redis_access.lua -- M3U8地址权限验证文件 + │ ├── business_redis.lua -- 业务 Redis 处理文件 + │ ├── http-lua-test.lua -- http lua demo + │ ├── ... + │ └── resty -- 存放Lua 的所有公共、封装好的库目录 + │ └── redis_iresty.lua -- Redis 接口的二次封装 + │ └── param.lua -- 参数过滤库 + └── sbin + └── nginx + ``` + + 参数总结 + + Lua脚本接受Nginx变量: + > [1] 间接获取:`var = ngx.var `,如接受Nginx的变量` $a = 9`,则`lua_a = ngx.var.a --lua_a = 9` + [2] 直接获取:`var = ngx.var `,如接受Nginx的location的第二个变量890,` http://127.0.0.1/lua_request/123/890 `,则`lua_2 = ngx.var[2] --lua_2 = 890` + + Lua 脚本接受 Nginx 头部 header: + > [1] 返回一个包含所有当前请求标头的Lua表:`local headers = ngx.req.get_headers()` + [2] 获取单个Host:`headers["Host"] 或者 ngx.req.get_headers()["Host"] ` + [3] 获取单个user-agent: + >> [01]`headers["user-agent"]` + [02]`headers.user_agent ` + [03]`ngx.req.get_headers()['user-agent'] ` + + Lua 脚本 Get 获取请求uri参数 + > linux curl Get方式提交数据语法:`curl -G -d "name=value&name2=value2" https://github.com/Tinywan ` + 返回一个包含所有当前请求URL查询参数的Lua表:`local get_args = ngx.req.get_uri_args()` + 请求案例:`curl -G -d "name=Tinywan&age=24" http://127.0.0.1/lua_request/123/789` + Lua Get 方式获取提交的name参数的值:`get_args['name'] 或者 ngx.req.get_uri_args()['name']` + >> [01]`get_args['name']` + [02]`ngx.req.get_uri_args()['name']` + + Lua 脚本 Post 获取请求uri参数 + > linux curl Post方式提交数据语法: + >> [01] `curl -d "name=value&name2=value2" https://github.com/Tinywan ` + [02] `curl -d a=b&c=d&txt@/tmp/txt https://github.com/Tinywan ` + > 返回一个包含所有当前请求URL查询参数的Lua表:`local post_args = ngx.req.get_post_args()` + 请求案例:`curl -d "name=Tinywan&age=24" http://127.0.0.1/lua_request/123/789` + Lua Post 方式获取提交的name参数的值: + >> [01]`post_args['name']` + [02]`ngx.req.get_post_args()['name']` + + Lua 脚本请求的http协议版本:`ngx.req.http_version()` + + Lua 脚本请求方法:`ngx.req.get_method()` + + Lua 脚本原始的请求头内容:`ngx.req.raw_header()` + + Lua 脚本请求的body内容体:`ngx.req.get_body_data()` + + [接收请求:获取如请求参数、请求头、Body体等信息]() + + [接收请求:输出响应需要进行响应状态码、响应头和响应内容体的输出]( ++ luajit 执行文件默认安装路径:`/opt/openresty/luajit/bin/luajit`,这样我们直接可以这样运行一个Lua文件:`luajit test.lua ` + + luajit 运行测试案例: + ```Bash + tinywan@tinywan:~/Lua$ luajit test.lua + The man name is Tinywan + The man name is Phalcon + ``` +#### lua-resty-redis 扩展 (是openresty下操作redis的模块) ++ 代码引入:`lua_package_path "/opt/openresty/nginx/lua/lua-resty-redis/lib/?.lua;;";` ++ Lua脚本实现一个CDN的反向代理功能(智能查找CDN节点)(测试成功,可上线) + + nginx.conf 配置信息 + ```Lua + http { + lua_package_path "/opt/openresty/nginx/lua/lua-resty-redis/lib/?.lua;;"; + server { + listen 80; + server_name localhost; + location ~ \/.+\/.+\.(m3u8|ts) { + if ($uri ~ \/([a-zA-Z0-9]+)\/([a-zA-Z0-9]+)(|-).*\.(m3u8|ts)) { + set $app_name $1; + set $a $2; + } + set $stream_id ""; + default_type 'text/html'; + rewrite_by_lua_file /opt/openresty/nginx/lua/proxy_pass_cdn.lua; + proxy_connect_timeout 10; + proxy_send_timeout 30; + proxy_read_timeout 30; + proxy_pass $stream_id; + } + + } + } + ``` + + [Lua脚本proxy_pass_cdn.lua](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Openresty/lua-resty-redis/proxy_pass_cdn.lua) + + [lua-nginx-module 贡献代码](https://github.com/openresty/lua-nginx-module/issues/275) + ++ Lua脚本结合 Nginx+Lua+Local Redis+Mysql服务器缓存 + + Nginx+Lua+Local Redis+Mysql集群架构 + + ![Nginx+Lua+Local Redis+Mysql](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Images/Nginx+Lua+Local_Redis+Mysql.png) + + [Lua脚本Nginx+Lua+Redis+Mysql.lua](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Openresty/lua-resty-redis/Nginx+Lua+Redis+Mysql.lua) + + [Nginx.conf配置文件Nginx+Lua+Redis+Mysql.conf](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Openresty/lua-resty-redis/Nginx+Lua+Redis+Mysql.conf) + + [HELP](http://jinnianshilongnian.iteye.com/blog/2188113) + + + Lua脚本结合 Redis 统计直播流播放次数、链接次数等等信息 + + nginx.conf + ```Lua + server { # 配置虚拟服务器80 + listen 80; + server_name 127.0.0.1:8088; + location ~* /live/(\w+)/ { + set $total_numbers ""; + set $stream_name $1; + lua_code_cache off; + rewrite_by_lua_file /opt/openresty/nginx/conf/Lua/total_numbers.lua; + proxy_pass http://127.0.0.1:8088; + } + } + ``` + + 代理服务器 + ```Lua + server { # 配置虚拟服务器8088 + listen 8088; + server_name 127.0.0.1:8088; + location /live { + add_header Cache-Control no-cache; + add_header 'Access-Control-Allow-Origin' '*' always; + add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range'; + add_header 'Access-Control-Allow-Headers' 'Range'; + types{ + application/dash+xml mpd; + application/vnd.apple.mpegurl m3u8; + video/mp2t ts; + } + alias /home/tinywan/HLS/live/; + } + } + + ``` + + CURL请求地址:`http://192.168.18.143/live/tinywan123/index.m3u8` + + [Lua 脚本](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Openresty/lua-resty-redis/Hls-Line-Number-Total.lua) + +#### lua-resty-websocket 扩展 ++ 代码引入:`lua_package_path "/opt/openresty/nginx/lua/lua-resty-websocket/lib/?.lua;;";` ++ **Lua脚本实现一个websocket连接(测试成功,可上线)** + + nginx.conf 配置信息 + ```Lua + http { + lua_package_path "/opt/openresty/nginx/lua/lua-resty-websocket/lib/?.lua;;"; + server { + listen 80 so_keepalive=2s:2s:8; #为了防止半开TCP连接,最好在Nginx监听配置指令中启用TCP keepalive: + server_name localhost; + location /ws { + lua_socket_log_errors off; + lua_check_client_abort on; + lua_code_cache off; # 建议测试的时候最好关闭缓存 + content_by_lua_file /opt/openresty/nginx/conf/Lua/websocket.lua; + } + } + } + ``` + + [WebSockets服务器Lua脚本websocket.lua](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Openresty/lua-resty-websocket/websocket.lua) + + [websockets.html客户端代码,代码路径:/usr/local/openresty/nginx/html](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Openresty/lua-resty-websocket/websocket.html) + + 然后打开启用了WebSocket支持的浏览器,然后打开以下url: + ![websockt-lua](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Images/websocket_lua01.png) +#### lua-cjson 扩展 ++ 基本用法 + + nginx.conf + ```Lua + location /cjson { + content_by_lua_block { + local cjson = require "cjson" + local json = cjson.encode({ + foo = "bar", + some_object = {}, + some_array = cjson.empty_array + }) + ngx.say(json) + } + } + ``` + + curl 请求 + ```Bash + root@tinywan:/opt/openresty/nginx/conf# curl http://127.0.0.1/cjson + {"some_object":{"tel":13669313112,"age":24},"name":"tinywan","some_array":[]} + ``` ++ [lua对象到字符串、字符串到lua对象](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Openresty/lua-cjson/cjson-str-obj.lua) +#### lua-resty-session 扩展 ++ OpenResty 引用第三方 resty 库非常简单,只需要将相应的文件拷贝到 resty 目录下即可 ++ 我服务器OpenResty 的 resty 路径:`/opt/openresty/lualib/resty` ++ 下载第三方 resty 库:git clone lua-resty-session 文件路径以及内容: + ```Bash + tinywan@tinywan:/opt/openresty/nginx/lua/lua-resty-session/lib/resty$ ls + session session.lua + ``` ++ 特别注意:这里拷贝的时候要要把session文件和session.lua 文件同时吧、拷贝过去,否则会报错误: + ```Bash + /opt/openresty/lualib/resty/session.lua:34: in function 'prequire' + /opt/openresty/lualib/resty/session.lua:211: in function 'new' + /opt/openresty/lualib/resty/session.lua:257: in function 'open' + /opt/openresty/lualib/resty/session.lua:320: in function 'start' + ``` ++ 拷贝完毕后`/opt/openresty/lualib/resty` OpenResty 引用第三方 resty 的所有库文件 + ```Bash + tinywan@tinywan:/opt/openresty/lualib/resty$ ls + aes.lua core.lua http_headers.lua lock.lua lrucache.lua memcached.lua random.lua session sha1.lua sha256.lua sha512.lua string.lua upstream + core dns http.lua lrucache md5.lua mysql.lua redis.lua session.lua sha224.lua sha384.lua sha.lua upload.lua websocket + ``` ++ 基本用法 + ```Lua + location /start { + content_by_lua_block { + local session = require "resty.session".start() + session.data.name = "OpenResty Fan Tinywan" + session:save() + ngx.say("Session started. ", + "Check if it is working!") + ngx.say(session.data.name,"Anonymous") + } + } + ``` ++ curl 请求 + ```Bash + tinywan@tinywan:/opt/openresty/nginx/conf$ curl http://192.168.18.143/start + Session started. Check if it is working! + OpenResty Fan Tinywan Anonymous + ``` +#### Lua 权限验证 ++ Lua 一个HLS的简单地址访问权限验证 + + Nginx.conf 配置 + ```Lua + location ^~ /live/ { + add_header Cache-Control no-cache; + add_header 'Access-Control-Allow-Origin' '*' always; + add_header 'Access-Control-Allow-Credentials' 'true'; + add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range'; + add_header 'Access-Control-Allow-Headers' 'Range'; + + types{ + application/dash+xml mpd; + application/vnd.apple.mpegurl m3u8; + video/mp2t ts; + } + if ( $uri ~ \.m3u8 ) { + lua_code_cache off; + access_by_lua_file /opt/openresty/nginx/lua/access.lua; + } + root /home/tinywan/HLS; + } + ``` + + access.lua 文件内容 + ```Lua + if ngx.req.get_uri_args()["wsSecret"] ~= "e65e6a01cf26523e206d5bb0e2a8a95a" then + return ngx.exit(403) + end + ``` +#### lua-resty-string 扩展 ++ MD5加密的简单基本用法 md5.lua + ```Lua + local resty_md5 = require "resty.md5" + local md5 = resty_md5:new() + if not md5 then + ngx.say("failed to create md5 object") + return + end + local ok = md5:update("hello") + if not ok then + ngx.say("failed to add data") + return + end + local digest = md5:final() + -- ngx.say("md5",digest) ---注意:这样直接输出是乱码 + local str = require "resty.string" + ngx.say("md5: ", str.to_hex(digest)) ---注意:必须通过字符串转码方可打印输出 + -- yield "md5: 5d41402abc4b2a76b9719d911017c592" + ``` +#### lua-resty-http 扩展 (ngx_lua的HTTP客户端cosocket驱动程序) ++ [简单测试:lua-http-test.lua](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Openresty/lua-resty-http/lua-http-test.lua) +#### lua-resty-mysql 扩展 ++ [简单测试:lua-msyql-test.lua](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Openresty/lua-resty-mysql/lua-msyql-test.lua) +#### srcache-nginx-module 扩展 ([nginx下的一个缓存模块](https://github.com/openresty/srcache-nginx-module)) ++ [openresty–redis–srcache缓存的应用](http://www.xtgxiso.com/openresty-redis-srcache-nginx-module%e7%bc%93%e5%ad%98%e7%9a%84%e5%ba%94%e7%94%a8/) +#### openresty扫描代码全局变量 ++ 在OpenResty中需要避免全局变量的使用,为此春哥写了一个perl工具,可以扫描openresty lua代码的全局变量 ++ [https://github.com/openresty/openresty-devel-utils/blob/master/lua-releng](https://github.com/openresty/openresty-devel-utils/blob/master/lua-releng) ++ 用法相当简单 + 1. 将代码保存成lua-releng文件 + 2. 更改lua-releng的权限,chmod 777 lua-releng + 3. 假设有一个源码文件为test.lua + 4. 执行./lua-releng test.lua,则会扫描test.lua文件的全局变量,并在屏幕打印结果 +#### ngx Lua APi 方法和常量 ++ ngx_lua 核心常量 + ```lua + ngx.OK (0) + ngx.ERROR (-1) + ngx.AGAIN (-2) + ngx.DONE (-4) + ngx.DECLINED (-5) + ngx.nil + -- 指令常量 + ngx.arg[index] #ngx指令参数,当这个变量在set_by_lua或者set_by_lua_file内使用的时候是只读的,指的是在配置指令输入的参数. + ngx.var.varname #读写NGINX变量的值,最好在lua脚本里缓存变量值,避免在当前请求的生命周期内内存的泄漏 + ngx.config.ngx_lua_version #当前ngx_lua模块版本号 + ngx.config.nginx_version #nginx版本 + ngx.worker.exiting #当前worker进程是否正在关闭 + ngx.worker.pid #当前worker进程的PID + ngx.config.nginx_configure #编译时的./configure命令选项 + ngx.config.prefix #编译时的prefix选项 + ``` ++ ngx_lua 方法 (#经常在ngx.location.catpure和ngx.location.capture_multi方法中被调用.) + ```lua + ngx.HTTP_GET + ngx.HTTP_HEAD + ngx.HTTP_PUT + ngx.HTTP_POST + ngx.HTTP_DELETE + ngx.HTTP_OPTIONS + ngx.HTTP_MKCOL + ngx.HTTP_COPY + ngx.HTTP_MOVE + ngx.HTTP_PROPFIND + ngx.HTTP_PROPPATCH + ngx.HTTP_LOCK + ngx.HTTP_UNLOCK + ngx.HTTP_PATCH + ngx.HTTP_TRACE + ``` ++ 错误日志级别常量 + ```lua + ngx.STDERR + ngx.EMERG + ngx.ALERT + ngx.CRIT + ngx.ERR + ngx.WARN + ngx.NOTICE + ngx.INFO + ngx.DEBUG + ``` ++ API中的常用方法 + ```lua + print() #与 ngx.print()方法有区别,print() 相当于ngx.log() + ngx.ctx #这是一个lua的table,用于保存ngx上下文的变量,在整个请求的生命周期内都有效,详细参考官方 + ngx.location.capture() #发出一个子请求,详细用法参考官方文档。 + ngx.location.capture_multi() #发出多个子请求,详细用法参考官方文档。 + ngx.status #读或者写当前请求的相应状态. 必须在输出相应头之前被调用. + ngx.header.HEADER #访问或设置http header头信息,详细参考官方文档。 + ngx.req.set_uri() #设置当前请求的URI,详细参考官方文档 + ngx.set_uri_args(args) #根据args参数重新定义当前请求的URI参数. + ngx.req.get_uri_args() #返回一个LUA TABLE,包含当前请求的全部的URL参数 + ngx.req.get_post_args() #返回一个LUA TABLE,包括所有当前请求的POST参数 + ngx.req.get_headers() #返回一个包含当前请求头信息的lua table. + ngx.req.set_header() #设置当前请求头header某字段值.当前请求的子请求不会受到影响. + ngx.req.read_body() #在不阻塞ngnix其他事件的情况下同步读取客户端的body信息.[详细] + ngx.req.discard_body() #明确丢弃客户端请求的body + ngx.req.get_body_data() #以字符串的形式获得客户端的请求body内容 + ngx.req.get_body_file() #当发送文件请求的时候,获得文件的名字 + ngx.req.set_body_data() #设置客户端请求的BODY + ngx.req.set_body_file() #通过filename来指定当前请求的file data。 + ngx.req.clear_header() #清求某个请求头 + ngx.exec(uri,args) #执行内部跳转,根据uri和请求参数 + ngx.redirect(uri, status) #执行301或者302的重定向。 + ngx.send_headers() #发送指定的响应头 + ngx.headers_sent #判断头部是否发送给客户端ngx.headers_sent=true + ngx.print(str) #发送给客户端的响应页面 + ngx.say() #作用类似ngx.print,不过say方法输出后会换行 + ngx.log(log.level,...) #写入nginx日志 + ngx.flush() #将缓冲区内容输出到页面(刷新响应) + ngx.exit(http-status) #结束请求并输出状态码 + ngx.eof() #明确指定关闭结束输出流 + ngx.escape_uri() #URI编码(本函数对逗号,不编码,而php的urlencode会编码) + ngx.unescape_uri() #uri解码 + ngx.encode_args(table) #将tabel解析成url参数 + ngx.decode_args(uri) #将参数字符串编码为一个table + ngx.encode_base64(str) #BASE64编码 + ngx.decode_base64(str) #BASE64解码 + ngx.crc32_short(str) #字符串的crs32_short哈希 + ngx.crc32_long(str) #字符串的crs32_long哈希 + ngx.hmac_sha1(str) #字符串的hmac_sha1哈希 + ngx.md5(str) #返回16进制MD5 + ngx.md5_bin(str) #返回2进制MD5 + ngx.today() #返回当前日期yyyy-mm-dd + ngx.time() #返回当前时间戳 + ngx.now() #返回当前时间 + ngx.update_time() #刷新后返回 + ngx.localtime() #返回 yyyy-mm-dd hh:ii:ss + ngx.utctime() #返回yyyy-mm-dd hh:ii:ss格式的utc时间 + ngx.cookie_time(sec) #返回用于COOKIE使用的时间 + ngx.http_time(sec) #返回可用于http header使用的时间 + ngx.parse_http_time(str) #解析HTTP头的时间 + ngx.is_subrequest #是否子请求(值为 true or false) + ngx.re.match(subject,regex,options,ctx) #ngx正则表达式匹配,详细参考官网 + ngx.re.gmatch(subject,regex,opt) #全局正则匹配 + ngx.re.sub(sub,reg,opt) #匹配和替换(未知) + ngx.re.gsub() #未知 + ngx.shared.DICT #ngx.shared.DICT是一个table 里面存储了所有的全局内存共享变量 + ngx.shared.DICT.get + ngx.shared.DICT.get_stale + ngx.shared.DICT.set + ngx.shared.DICT.safe_set + ngx.shared.DICT.add + ngx.shared.DICT.safe_add + ngx.shared.DICT.replace + ngx.shared.DICT.delete + ngx.shared.DICT.incr + ngx.shared.DICT.flush_all + ngx.shared.DICT.flush_expired + ngx.shared.DICT.get_keys + ndk.set_var.DIRECTIVE + ``` ++ Lua HTTP状态常量 + ```Lua + value = ngx.HTTP_CONTINUE (100) (first added in the v0.9.20 release) + value = ngx.HTTP_SWITCHING_PROTOCOLS (101) (first added in the v0.9.20 release) + value = ngx.HTTP_OK (200) + value = ngx.HTTP_CREATED (201) + value = ngx.HTTP_ACCEPTED (202) (first added in the v0.9.20 release) + value = ngx.HTTP_NO_CONTENT (204) (first added in the v0.9.20 release) + value = ngx.HTTP_PARTIAL_CONTENT (206) (first added in the v0.9.20 release) + value = ngx.HTTP_SPECIAL_RESPONSE (300) + value = ngx.HTTP_MOVED_PERMANENTLY (301) + value = ngx.HTTP_MOVED_TEMPORARILY (302) + value = ngx.HTTP_SEE_OTHER (303) + value = ngx.HTTP_NOT_MODIFIED (304) + value = ngx.HTTP_TEMPORARY_REDIRECT (307) (first added in the v0.9.20 release) + value = ngx.HTTP_BAD_REQUEST (400) + value = ngx.HTTP_UNAUTHORIZED (401) + value = ngx.HTTP_PAYMENT_REQUIRED (402) (first added in the v0.9.20 release) + value = ngx.HTTP_FORBIDDEN (403) + value = ngx.HTTP_NOT_FOUND (404) + value = ngx.HTTP_NOT_ALLOWED (405) + value = ngx.HTTP_NOT_ACCEPTABLE (406) (first added in the v0.9.20 release) + value = ngx.HTTP_REQUEST_TIMEOUT (408) (first added in the v0.9.20 release) + value = ngx.HTTP_CONFLICT (409) (first added in the v0.9.20 release) + value = ngx.HTTP_GONE (410) + value = ngx.HTTP_UPGRADE_REQUIRED (426) (first added in the v0.9.20 release) + value = ngx.HTTP_TOO_MANY_REQUESTS (429) (first added in the v0.9.20 release) + value = ngx.HTTP_CLOSE (444) (first added in the v0.9.20 release) + value = ngx.HTTP_ILLEGAL (451) (first added in the v0.9.20 release) + value = ngx.HTTP_INTERNAL_SERVER_ERROR (500) + value = ngx.HTTP_METHOD_NOT_IMPLEMENTED (501) + value = ngx.HTTP_BAD_GATEWAY (502) (first added in the v0.9.20 release) + value = ngx.HTTP_SERVICE_UNAVAILABLE (503) + value = ngx.HTTP_GATEWAY_TIMEOUT (504) (first added in the v0.3.1rc38 release) + value = ngx.HTTP_VERSION_NOT_SUPPORTED (505) (first added in the v0.9.20 release) + value = ngx.HTTP_INSUFFICIENT_STORAGE (507) (first added in the v0.9.20 release) + ``` ++ 案列使用,get_string_md5.lua: + ```Lua + local args = ngx.req.get_uri_args() + local salt = args.salt + if not salt then + ngx.say(ngx.HTTP_BAD_REQUEST) + end + local string = ngx.md5(ngx.time()..salt) + ngx.say(string) + + ``` ++ curl 请求(-i 参数,输出时包括protocol头信息): + ```Bash + tinywan@tinywan:$ curl -i http://127.0.0.1/get_rand_string?salt=tinywan123 + HTTP/1.1 200 OK + Server: openresty/1.11.2.1 + Date: Fri, 21 Apr 2017 14:27:16 GMT + Content-Type: application/octet-stream + Transfer-Encoding: chunked + Connection: keep-alive + ``` +#### ngx Lua APi 介绍使用 ++ 强烈建议使用ngx Lua APi 接口`(非阻塞的)`,而不是Lua自身的API`(阻塞的)`,Lua 自身API会阻塞掉的 ++ ngx_lua_api_test.lua + ```Lua + local json = require "cjson" -- 引入cjson 扩展 + + -- 同步读取客户端请求正文,而不会阻止Nginx事件循环 + ngx.req.read_body() + local args = ngx.req.get_post_args() + + if not args or not args.info then + ngx.say(ngx.HTTP_BAD_REQUEST) -- ngx.HTTP_BAD_REQUEST (400) + end + + local client_id = ngx.var.remote_addr + local user_agent = ngx.req.get_headers()['user-agent'] or "" + local info = ngx.decode_base64(args.info) + + local response = {} + response.info = info + response.client_id = client_id + response.user_agent = user_agent + + ngx.say(json.encode(response)) + + ``` ++ CURL Post 请求 + ```Lua + $ curl -i --data "info=b3ZlcmNvbWUud2FuQGdtYWlsLmNvbQ==" http://127.0.0.1/ngx_lua_api_test + HTTP/1.1 200 OK + Server: openresty/1.11.2.1 + Date: Sat, 22 Apr 2017 01:22:07 GMT + Content-Type: application/octet-stream + Transfer-Encoding: chunked + Connection: keep-alive + + {"user_agent":"curl\/7.47.0","info":"overcome.wan@gmail.com","client_id":"127.0.0.1"} + + ``` +#### OpenResty缓存 ++ 指令:`lua_shared_dict` + + 纯内存的操作,多个worker之间共享的(比如nginx开启10个Worker,则每个worker之间是共享该内存的) + + 同一份数据在多个worker之间是共享的,只要存储一份数据就可以了 + + 锁的竞争(数据原子性) +#### lua-resty-upstream-healthcheck使用 ++ health.txt 在每个upstream 服务器组的root 目录下创建这个文件,目录结构如下所示 + ```javascript + ├── html + │   ├── 50x.html + │   ├── index.html + │   ├── websocket001.html + │   └── websocket02.html + ├── html81 + │   ├── 50x.html + │   ├── health.txt + │   └── index.html + ├── html82 + │   ├── 50x.html + │   ├── health.txt + │   └── index.html + ├── html83 + │   ├── 50x.html + │   ├── health.txt + │   └── index.html + ├── html84 + │   ├── 50x.html + │   └── index.html + ``` ++ nginx.conf + ```Lua + worker_processes 8; + + error_log logs/error.log; + pid logs/nginx.pid; + + events { + use epoll; + worker_connections 1024; + } + + http { + include mime.types; + default_type text/html; + #lua模块路径,其中”;;”表示默认搜索路径,默认到/usr/servers/nginx下找 + lua_package_path "/home/tinywan/Openresty_Protect/First_Protect/lualib/?.lua;;"; #lua 模块 + lua_package_cpath "/home/tinywan/Openresty_Protect/First_Protect/lualib/?.so;;"; #c模块 + include /home/tinywan/Openresty_Protect/First_Protect/nginx_first.conf; + } + + ``` ++ nginx_first.conf + ```Lua + upstream tomcat { + server 127.0.0.1:8081; + server 127.0.0.1:8082; + server 127.0.0.1:8083; + server 127.0.0.1:8084 backup; + } + + lua_shared_dict healthcheck 1m; + + lua_socket_log_errors off; + + init_worker_by_lua_block { + local hc = require "resty.upstream.healthcheck" + local ok, err = hc.spawn_checker{ + shm = "healthcheck", -- defined by "lua_shared_dict" + upstream = "tomcat", -- defined by "upstream" + type = "http", + + http_req = "GET /health.txt HTTP/1.0\r\nHost: tomcat\r\n\r\n", + -- raw HTTP request for checking + + interval = 2000, -- run the check cycle every 2 sec + timeout = 1000, -- 1 sec is the timeout for network operations + fall = 3, -- # of successive failures before turning a peer down + rise = 2, -- # of successive successes before turning a peer up + valid_statuses = {200, 302}, -- a list valid HTTP status code + concurrency = 10, -- concurrency level for test requests + } + } + + server { + listen 80; + server_name localhost; + + location / { + proxy_pass http://tomcat; + } + + location /server/status { + access_log off; + allow 127.0.0.1; + + default_type text/plain; + content_by_lua_block { + local hc = require "resty.upstream.healthcheck" + ngx.say("Nginx Worker PID: ", ngx.worker.pid()) + ngx.print(hc.status_page()) + } + } + } + + server { + listen 8081; + server_name localhost; + + location / { + root html81; + index index.html index.htm; + } + + location /health_status { + + } + } + + server { + listen 8082; + server_name localhost; + + location / { + root html82; + index index.html index.htm; + } + + location /health_status { + + } + } + + server { + listen 8083; + server_name localhost; + + location / { + root html83; + index index.html index.htm; + } + + location /health_status { + + } + } + + server { + listen 8084; + server_name localhost; + + location / { + root html84; + index index.html index.htm; + } + } + + ``` ++ 状态查看,通过访问:`http://127.0.0.1/server/status` + ![Markdown](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Images/Openresty_lua-resty-upstream-healthcheck.png) +#### Openresty和Nginx_RTMP 模块共存问题 ++ RTMP 流的状态(stat.xsl)不生效Bug 问题 + - 1. 修改完nginx.conf 配置文件 + - 1. ~~执行:`nginx -s reload` 会不起作用~~ + - 2. 一定要执行以下命令:杀掉所有nginx进程`sudo killall nginx ` 重启即可`sbin/nignx` +#### 配置RTMP模块的多worker直播流 ++ 配置文件,[Multi-worker live streaming官方文档](https://github.com/arut/nginx-rtmp-module/wiki/Directives#multi-worker-live-streaming) + ```Shell + user www www; + worker_processes auto; + error_log logs/error.log debug; + + pid /var/run/nginx.pid; + events { + use epoll; + worker_connections 1024; + multi_accept on; + } + + rtmp_auto_push on; + rtmp_auto_push_reconnect 1s; + rtmp_socket_dir /var/sock; + rtmp { + server { + listen 1935; + application live { + live on; + } + } + } + ``` ++ [nginx 并发数问题思考:worker_connections,worker_processes与 max clients](http://liuqunying.blog.51cto.com/3984207/1420556?utm_source=tuicool) + + 从用户的角度,http 1.1协议下,由于浏览器默认使用两个并发连接,因此计算方法: + 1. nginx作为http服务器的时候: + `max_clients = worker_processes * worker_connections/2` + 1. nginx作为反向代理服务器的时候: + `max_clients = worker_processes * worker_connections/4` + + 从一般建立连接的角度,客户并发连接为1: + 1. nginx作为http服务器的时候: + `max_clients = worker_processes * worker_connections` + 1. nginx作为反向代理服务器的时候: + `max_clients = worker_processes * worker_connections/2` + + nginx做反向代理时,和客户端之间保持一个连接,和后端服务器保持一个连接 + + clients与用户数 + 同一时间的clients(客户端数)和用户数还是有区别的,当一个用户请求发送一个连接时这两个是相等的,但是当一个用户默认发送多个连接请求的时候,clients数就是用户数*默认发送的连接并发数了。 + + +## Redis、Lua、Nginx一起工作事迹 ++ 解决一个set_by_lua $sum 命令受上下文限制的解决思路,已完美解决 ++ - [x] [API disabled in the context of set_by_lua](https://github.com/openresty/lua-nginx-module/issues/275) ++ 解决2 ++ 解决3 +## Redis执行Lua脚本 +## Lua 基本语法 +--- ++ Hello, Lua! + + > 我们的第一个Redis Lua 脚本仅仅返回一个字符串,而不会去与redis 以任何有意义的方式交互 + + ```Lua + local msg = "Hello, world!" + return msg + ``` + + > 这是非常简单的,第一行代码定义了一个本地变量msg存储我们的信息, 第二行代码表示 从redis 服务端返回msg的值给客户端。 保存这个文件到Hello.lua,像这样去运行: + + ```Bash + www@iZ239kcyg8rZ:~/lua$ redis-cli EVAL "$(cat Hello.lua)" 0 + "Hello, world!" + ``` + + > 运行这段代码会打印"Hello,world!", EVAL在第一个参数是我们的lua脚本, 这我们用cat命令从文件中读取我们的脚本内容。第二个参数是这个脚本需要访问的Redis 的键的数字号。我们简单的 “Hello Script" 不会访问任何键,所以我们使用0 + ++ redis.call() 与 redis.pcall()的区别 + + * 他们唯一的区别是当redis命令执行结果返回错误时 + * redis.call()将返回给调用者一个错误. + * redis.pcall()会将捕获的错误以Lua表的形式返回. + * redis.call() 和 redis.pcall() 两个函数的参数可以是任意的 Redis 命令 ++ Lua网络编程 +## Lua 脚本 ++ Lua 实现简单封装 + + man.lua + ```Lua + local _name = "Tinywan" + local man = {} + + function man.GetName() + return _name + end + + function man.SetName(name) + _name = name + end + + return man + ``` + + 测试封装,test.lua + + ```Lua + local man = require('man') + print("The man name is "..man.GetName()) + man.SetName("Phalcon") + print("The man name is "..man.GetName()) + ``` +#### Redis执行Lua脚本基本用法 ++ EVAL命令格式 + + 基本语法 + ``` + EVAL script numkeys key [key ...] arg [arg ...] + ``` + + 语义 + 1. script即为lua脚本或lua脚本文件 + 1. key一般指lua脚本操作的键,在lua脚本文件中,通过KEYS[i]获取 + 1. arg指外部传递给lua脚本的参数,可以通过ARGV[i]获取 + + eval命令的用法 + ``` + 127.0.0.1:6379> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second + 1) "key1" + 2) "key2" + 3) "first" + 4) "second" + ``` + > 这个示例中lua脚本为一个return语句,返回了lua一个数组,这个数组四个元素分别是通过外部传入lua脚本。 + 因为redis内嵌了Lua虚拟机,因此redis接收到这个lua脚本之后,然后交给lua虚拟机执行。 + 当lua虚拟机执行结束,即将执行结果返回给redis,redis将结果按自己的协议转换为返回给客户端的回复,最后再通过TCP将回复发回给客户端 + + lua脚本参数的接受 + + 键值:`KEYS[i]` 来获取外部传入的键值 + + 参数:`ARGV[i]` 来获取外部传入的参数 ++ 案例介绍 + + 通过Redis命令`EVAL`执行一个简单的Lua脚本文件 + + 给Redis添加测试数据(通过有序集合和哈希对应的集合信息) + ```javascript + 127.0.0.1:6379> ZADD WEB 1 google + (integer) 1 + 127.0.0.1:6379> ZADD WEB 2 apple + (integer) 1 + 127.0.0.1:6379> ZADD WEB 3 baidu + (integer) 1 + 127.0.0.1:6379> ZRANGE WEB 0 3 + 1) "google" + 2) "apple" + 3) "baidu" + 127.0.0.1:6379> hmset google domain_name www.google.com ip 192.168.1.100 + OK + 127.0.0.1:6379> hmset baidu domain_name www.baidu.com ip 192.168.1.200 + OK + 127.0.0.1:6379> hmset apple domain_name www.apple.com ip 192.168.1.300 + OK + 127.0.0.1:6379> hgetall google + 1) "domain_name" + 2) "www.google.com" + 3) "ip" + 4) "192.168.1.100" + 127.0.0.1:6379> hgetall apple + 1) "domain_name" + 2) "www.apple.com" + 3) "ip" + 4) "192.168.1.300" + 127.0.0.1:6379> hgetall baidu + 1) "domain_name" + 2) "www.baidu.com" + 3) "ip" + 4) "192.168.1.200" + ``` + + Lua脚本,lua_get_redis.lua 文件 + ```Lua + -- 获取键值/参数 + local key,offset,limit = KEYS[1], ARGV[1], ARGV[2] + -- 通过ZRANGE获取键为key的有序集合元素,偏移量为offset,个数为limit,即所有WEB信息 + local names = redis.call('ZRANGE', key, offset, limit) + -- infos table 存储所有WEB信息 + local infos = {} + -- 遍历所有WEB信息 + for i=1,#names do + local ck = names[i] + -- 通过HGETALL命令获取每WEB的信息 + local info = redis.call('HGETALL',ck) + -- 并且在WEB信息中插入对应的集合信息 + table.insert(info,'HOST_NAME') + table.insert(info,names[i]) + --table.insert(info,'author',"Tinywan") + -- 插入infos中 + infos[i] = info + end + -- 将结果返回给redis + return infos + ``` + 1. redis.call() 函数的参数可以是任意的 Redis 命令 + 1. table.insert(table, pos, value) + > [1]table.insert()函数在table的数组部分指定位置(pos)插入值为value的一个元素. pos参数可选, 默认为数组部分末尾 + + 执行结果: + ``` + tinywan@:~/Lua$ sudo redis-cli --eval /home/tinywan/Lua/lua_get_redis.lua WEB , 0 2 + 1) 1) "domain_name" + 2) "www.google.com" + 3) "ip" + 4) "192.168.1.100" + 5) "HOST_NAME" + 6) "google" + 2) 1) "domain_name" + 2) "www.apple.com" + 3) "ip" + 4) "192.168.1.300" + 5) "HOST_NAME" + 6) "apple" + 3) 1) "domain_name" + 2) "www.baidu.com" + 3) "ip" + 4) "192.168.1.200" + 5) "HOST_NAME" + 6) "baidu" + ``` + + 注意:`lua_get_redis.lua WEB , 0 2` 之间的空格,不然会提示错误 + + 错误:`(error) ERR Error running script command arguments must be strings or integers` + + Ngx_lua 写入Redis数据,通过CURL请求 + + curl_get_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 key = ngx.var[1] + local new_timer = ngx.localtime() -- 本地时间:2017-04-16 15:56:59 + local value = key.."::"..new_timer + local ok , err = red:set(key,value) + if not ok then + ngx.say("failed to set "..key, err) + return + end + ngx.say("set result: ", ok) + + local res, err = red:get(key) + if not res then + ngx.say("get from redis error : ", err) + return + end + if res == ngx.null then + ngx.say(key.."not found.") + return + end + red:close() + ngx.say("Success get Redis Data",json.encode({content=res})) + ``` + + nginx.conf + ```Lua + location ~* /curl_insert_redis/(\w+)$ { + default_type 'text/html'; + lua_code_cache off; + content_by_lua_file /opt/openresty/nginx/conf/Lua/curl_get_redis.lua; + } + ``` + + curl 和浏览器请求结果(查询Redis数据库,数据已经插入成功) + ```Lua + root@tinywan:# curl http://127.0.0.1/curl_insert_redis/Tinywan1227 + set result: OK + Success get Redis Data{"content":"Tinywan1227::2017-04-16 15:57:56"} + ``` + + 通过lua脚本获取指定的key的List中的所有数据 + ```Lua + local key=KEYS[1] + local list=redis.call("lrange",key,0,-1); + return list; + ``` + + 根据外面传过来的IDList 做“集合去重”的lua脚本逻辑: + ```Lua + local result={}; + local myperson=KEYS[1]; + local nums=ARGV[1]; + + local myresult =redis.call("hkeys",myperson); + + for i,v in ipairs(myresult) do + local hval= redis.call("hget",myperson,v); + redis.log(redis.LOG_WARNING,hval); + if(tonumber(hval) Visual Studio Code 向github提交代码不用输入帐号密码 ++ 在命令行输入以下命令 + ``` + git config --global credential.helper store + ``` + + > 这一步会在用户目录下的.gitconfig文件最后添加: + + ``` + [credential] + helper = store + ``` ++ push 代码 + + > push你的代码 (git push), 这时会让你输入用户名和密码, 这一步输入的用户名密码会被记住, 下次再push代码时就不用输入用户名密码!这一步会在用户目录下生成文件.git-credential记录用户名密码的信息。 + ++ Markdown 的超级链接技术 + + > 【1】需要链接的地址: + + ``` + [解决向github提交代码不用输入帐号密码](#githubpush) + ``` + + > 【2】要链接到的地方: + + ``` + 解决向github提交代码不用输入帐号密码 + ``` + + > 通过【1】和【2】可以很完美的实现一个连接哦! + +## Linux 基础知识 ++ 检查网卡是否正确工作 + 1. 检查系统路由表信息是否正确 + 1. [Linux route命令详解和使用示例](http://www.jb51.net/LINUXjishu/152385.html) + 1. 案例介绍: + ``` + www@ubuntu1:/var/log$ route + Kernel IP routing table + Destination Gateway Genmask Flags Metric Ref Use Iface + default 122.11.11.161 0.0.0.0 UG 0 0 0 em1 + 10.10.101.0 * 255.255.255.0 U 0 0 0 em2 + 122.11.11.160 * 255.255.255.224 U 0 0 0 em1 + ``` + + 默认路由为:`122.11.11.161`,绑定在`em1` 网卡上 + + 而`10.10.101.0` 段的IP仅供局域网主机之间共享数据,没对外连接访问权限,因而外界是没办法通过`10`段网络连接到服务器的 + + 如果需要`10.10.101.0` 段可以让外网放完的,则需要删除`122.11.11.161` 的默认路由,需要在`em2`网卡上添加`10`段的默认路由即可 + + 具体步骤: + ``` + www@ubuntu1:/var/log$ route delete defaul + www@ubuntu1:/var/log$ route add defaul gw 10.10.101.1 + ``` + + 此时外界就可以通过`ssh www@10.10.101.2`连接到服务器了 ++ find 命令 + + 查找超出7天前的flv的文件进行删除: + + 命令: + ```Bash + find ./ -mindepth 1 -maxdepth 3 -type f -name "*.flv" -mmin +10080 | xargs rm -rf + ``` + + `-type f` 按类型查找 + + `-mmin +10080` 7天之前的文件 + + xargs与-exec功能类似,` find ~ -type f | xargs ls -l ` + + -r 就是向下递归,不管有多少级目录,一并删除 + + -f 就是直接强行删除,不作任何提示的意思 + + 查找当前目录下.p文件中,最近30分钟内修改过的文件: + + `find . -name '*.p' -type f -mmin -30` + + 查找当前目录下.phtml文件中,最近30分钟内修改过的文件,的详细de情况加上ls: + + `find . -name '*.phtml' -type f -mmin -30 -ls` + + 查找当前目录下,最近1天内修改过的常规文件:`find . -type f -mtime -1` + + 查找当前目录下,最近1天前(2天内)修改过的常规文件:`find . -type f -mtime +1` +# 掘金爬虫 + +![Markdown](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Images/github_good1.png) + +# Lua-Ngx +![Markdown](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Images/Nginx-Phase.png) +# Live demo + +Changes are automatically rendered as you type. + +* Follows the [CommonMark](http://commonmark.org/) spec +* Renders actual, "native" React DOM elements +* Allows you to escape or skip HTML (try toggling the checkboxes above) +* If you escape or skip the HTML, no `dangerouslySetInnerHTML` is used! Yay! + +## HTML block below + +
      + This blockquote will change based on the HTML settings above. +
      + +## How about some code? +```js +var React = require('react'); +var Markdown = require('react-markdown'); + +React.render( + , + document.getElementById('content') +); +``` + +Pretty neat, eh? + +## More info? + +Read usage information and more on [GitHub](//github.com/rexxars/react-markdown) + +--------------- + +A component by [VaffelNinja](http://vaffel.ninja) / Espen Hovlandsdal + +##
      Copyright and License + +This module is licensed under the BSD license + +Copyright (C) 2017, by Wanshaobo "Tinywan". \ No newline at end of file diff --git a/Redis-PHP/Php-Run-Redis-psubscribe/nohupRedisNotify.php b/Redis-PHP/Php-Run-Redis-psubscribe/nohupRedisNotify.php index 0a3618b..8926003 100644 --- a/Redis-PHP/Php-Run-Redis-psubscribe/nohupRedisNotify.php +++ b/Redis-PHP/Php-Run-Redis-psubscribe/nohupRedisNotify.php @@ -1,34 +1,26 @@ - #! /usr/bin/php setOption(\Redis::OPT_READ_TIMEOUT,-1); RedisInstance::getInstance()->psubscribe(array('__keyevent@0__:expired'), 'psCallback'); -// 回调函数,这里写处理逻辑 -function psCallback($redis, $pattern, $chan, $msg) -{ - $messageId = explode(':',$msg); - //print_r($msg); - //echo $messageId[0]."
      "; - //echo $messageId[1]; - //die; - //RedisInstance::getInstance()->set("channelXode",$messageId); - switch($messageId[0]){ - case '4001': - curlPost('http://127.0.0.1',array('id'=>$messageId[1])); - break; - case '4002': - curlPost('http://127.0.0.1',array('id'=>$messageId[1])); - break; - case '4003': - curlPost('http://127.0.0.1',array('id'=>$messageId[1])); - break; - default: - curlPost('http://127.0.0.1',array('id'=>$msg)); - break; - } -} +RedisInstance::getInstance()->psubscribe(array('__keyevent@0__:expired'),function ($redis, $pattern, $chan, $msg){ + // 回调函数,这里写处理逻辑 + $messageId = explode(':',$msg); + switch($messageId[0]){ + case '4001': + curlPost('http://127.0.0.1',array('id'=>$messageId[1])); + break; + case '4002': + curlPost('http://127.0.0.1',array('id'=>$messageId[1])); + break; + case '4003': + curlPost('http://127.0.0.1',array('id'=>$messageId[1])); + break; + default: + curlPost('http://127.0.0.1',array('id'=>$msg)); + break; + } +}); function curlPost($url, $curlPost) { diff --git "a/Redis/Codis\351\233\206\347\276\244\346\274\224\345\214\226\344\270\216Redis\345\274\202\346\255\245\350\277\201\347\247\273.pdf" "b/Redis/Codis\351\233\206\347\276\244\346\274\224\345\214\226\344\270\216Redis\345\274\202\346\255\245\350\277\201\347\247\273.pdf" new file mode 100644 index 0000000..ae8b0d6 Binary files /dev/null and "b/Redis/Codis\351\233\206\347\276\244\346\274\224\345\214\226\344\270\216Redis\345\274\202\346\255\245\350\277\201\347\247\273.pdf" differ diff --git a/Redis/redis-config-client.png b/Redis/redis-config-client.png new file mode 100644 index 0000000..cbd21e3 Binary files /dev/null and b/Redis/redis-config-client.png differ diff --git a/Redis/redis-config.md b/Redis/redis-config.md new file mode 100644 index 0000000..a0254f9 --- /dev/null +++ b/Redis/redis-config.md @@ -0,0 +1,397 @@ +## redis.conf配置详解 +#### 基本配置 +```bash +# 当配置中需要配置内存大小时,可以使用 1k, 5GB, 4M 等类似的格式,其转换方式如下(不区分大小写) +# +# 1k => 1000 bytes +# 1kb => 1024 bytes +# 1m => 1000000 bytes +# 1mb => 1024*1024 bytes +# 1g => 1000000000 bytes +# 1gb => 1024*1024*1024 bytes +# +# 内存配置大小写是一样的.比如 1gb 1Gb 1GB 1gB + +# daemonize no 默认情况下,redis不是在后台运行的,如果需要在后台运行,把该项的值更改为yes +daemonize yes + +# 当redis在后台运行的时候,Redis默认会把pid文件放在/var/run/redis.pid,你可以配置到其他地址。 +# 当运行多个redis服务时,需要指定不同的pid文件和端口 +pidfile /var/run/redis.pid + +# 指定redis运行的端口,默认是6379 +port 6379 + +# 指定redis只接收来自于该IP地址的请求,如果不进行设置,那么将处理所有请求, +# 在生产环境中最好设置该项 +# bind 127.0.0.1 + +# Specify the path for the unix socket that will be used to listen for +# incoming connections. There is no default, so Redis will not listen +# on a unix socket when not specified. +# +# unixsocket /tmp/redis.sock +# unixsocketperm 755 + +# 设置客户端连接时的超时时间,单位为秒。当客户端在这段时间内没有发出任何指令,那么关闭该连接 +# 0是关闭此设置 +timeout 0 + +# 指定日志记录级别 +# Redis总共支持四个级别:debug、verbose、notice、warning,默认为verbose +# debug 记录很多信息,用于开发和测试 +# varbose 有用的信息,不像debug会记录那么多 +# notice 普通的verbose,常用于生产环境 +# warning 只有非常重要或者严重的信息会记录到日志 +loglevel debug + +# 配置log文件地址 +# 默认值为stdout,标准输出,若后台模式会输出到/dev/null +#logfile stdout +logfile /var/log/redis/redis.log + +# To enable logging to the system logger, just set 'syslog-enabled' to yes, +# and optionally update the other syslog parameters to suit your needs. +# syslog-enabled no + +# Specify the syslog identity. +# syslog-ident redis + +# Specify the syslog facility. Must be USER or between LOCAL0-LOCAL7. +# syslog-facility local0 + +# 可用数据库数 +# 默认值为16,默认数据库为0,数据库范围在0-(database-1)之间 +databases 16 + +################################ 快照 ################################# +# +# 保存数据到磁盘,格式如下: +# +# save +# +# 指出在多长时间内,有多少次更新操作,就将数据同步到数据文件rdb。 +# 相当于条件触发抓取快照,这个可以多个条件配合 +# +# 比如默认配置文件中的设置,就设置了三个条件 +# +# save 900 1 900秒内至少有1个key被改变 +# save 300 10 300秒内至少有300个key被改变 +# save 60 10000 60秒内至少有10000个key被改变 + +save 900 1 +save 300 10 +save 60 10000 + +# 存储至本地数据库时(持久化到rdb文件)是否压缩数据,默认为yes +rdbcompression yes + + +# 本地持久化数据库文件名,默认值为dump.rdb +dbfilename dump.rdb + +# 工作目录 +# +# 数据库镜像备份的文件放置的路径。 +# 这里的路径跟文件名要分开配置是因为redis在进行备份时,先会将当前数据库的状态写入到一个临时文件中,等备份完成时, +# 再把该该临时文件替换为上面所指定的文件,而这里的临时文件和上面所配置的备份文件都会放在这个指定的路径当中。 +# +# AOF文件也会存放在这个目录下面 +# +# 注意这里必须制定一个目录而不是文件 +dir ./ + +################################# 复制 ################################# +# 主从复制. 设置该数据库为其他数据库的从数据库. +# 设置当本机为slav服务时,设置master服务的IP地址及端口,在Redis启动时,它会自动从master进行数据同步 +# +# slaveof + +# 当master服务设置了密码保护时(用requirepass制定的密码) +# slav服务连接master的密码 +# +# masterauth + + +# 当从库同主机失去连接或者复制正在进行,从机库有两种运行方式: +# +# 1) 如果slave-serve-stale-data设置为yes(默认设置),从库会继续相应客户端的请求 +# +# 2) 如果slave-serve-stale-data是指为no,出去INFO和SLAVOF命令之外的任何请求都会返回一个 +# 错误"SYNC with master in progress" +# +slave-serve-stale-data yes + +# 从库会按照一个时间间隔向主库发送PINGs.可以通过repl-ping-slave-period设置这个时间间隔,默认是10秒 +# +# repl-ping-slave-period 10 + +# repl-timeout 设置主库批量数据传输时间或者ping回复时间间隔,默认值是60秒 +# 一定要确保repl-timeout大于repl-ping-slave-period +# repl-timeout 60 + +################################## 安全 ################################### +# 设置客户端连接后进行任何其他指定前需要使用的密码。 +# 警告:因为redis速度相当快,所以在一台比较好的服务器下,一个外部的用户可以在一秒钟进行150K次的密码尝试,这意味着你需要指定非常非常强大的密码来防止暴力破解 +# +# requirepass foobared + +# 命令重命名. +# +# 在一个共享环境下可以重命名相对危险的命令。比如把CONFIG重名为一个不容易猜测的字符。 +# +# 举例: +# +# rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52 +# +# 如果想删除一个命令,直接把它重命名为一个空字符""即可,如下: +# +# rename-command CONFIG "" + +################################### 约束 #################################### +# 设置同一时间最大客户端连接数,默认无限制,Redis可以同时打开的客户端连接数为Redis进程可以打开的最大文件描述符数, +# 如果设置 maxclients 0,表示不作限制。 +# 当客户端连接数到达限制时,Redis会关闭新的连接并向客户端返回max number of clients reached错误信息 +# +# maxclients 128 + +# 指定Redis最大内存限制,Redis在启动时会把数据加载到内存中,达到最大内存后,Redis会先尝试清除已到期或即将到期的Key +# Redis同时也会移除空的list对象 +# +# 当此方法处理后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作 +# +# 注意:Redis新的vm机制,会把Key存放内存,Value会存放在swap区 +# +# maxmemory的设置比较适合于把redis当作于类似memcached的缓存来使用,而不适合当做一个真实的DB。 +# 当把Redis当做一个真实的数据库使用的时候,内存使用将是一个很大的开销 +# maxmemory + +# 当内存达到最大值的时候Redis会选择删除哪些数据?有五种方式可供选择 +# +# volatile-lru -> 利用LRU算法移除设置过过期时间的key (LRU:最近使用 Least Recently Used ) +# allkeys-lru -> 利用LRU算法移除任何key +# volatile-random -> 移除设置过过期时间的随机key +# allkeys->random -> remove a random key, any key +# volatile-ttl -> 移除即将过期的key(minor TTL) +# noeviction -> 不移除任何可以,只是返回一个写错误 +# +# 注意:对于上面的策略,如果没有合适的key可以移除,当写的时候Redis会返回一个错误 +# +# 写命令包括: set setnx setex append +# incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd +# sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby +# zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby +# getset mset msetnx exec sort +# +# 默认是: +# +# maxmemory-policy volatile-lru + +# LRU 和 minimal TTL 算法都不是精准的算法,但是相对精确的算法(为了节省内存),随意你可以选择样本大小进行检测。 +# Redis默认的灰选择3个样本进行检测,你可以通过maxmemory-samples进行设置 +# +# maxmemory-samples 3 + +############################## AOF ############################### +# 默认情况下,redis会在后台异步的把数据库镜像备份到磁盘,但是该备份是非常耗时的,而且备份也不能很频繁,如果发生诸如拉闸限电、拔插头等状况,那么将造成比较大范围的数据丢失。 +# 所以redis提供了另外一种更加高效的数据库备份及灾难恢复方式。 +# 开启append only模式之后,redis会把所接收到的每一次写操作请求都追加到appendonly.aof文件中,当redis重新启动时,会从该文件恢复出之前的状态。 +# 但是这样会造成appendonly.aof文件过大,所以redis还支持了BGREWRITEAOF指令,对appendonly.aof 进行重新整理。 +# 你可以同时开启asynchronous dumps 和 AOF + +appendonly no + +# AOF文件名称 (默认: "appendonly.aof") +# appendfilename appendonly.aof + +# Redis支持三种同步AOF文件的策略: +# +# no: 不进行同步,系统去操作 . Faster. +# always: always表示每次有写操作都进行同步. Slow, Safest. +# everysec: 表示对写操作进行累积,每秒同步一次. Compromise. +# +# 默认是"everysec",按照速度和安全折中这是最好的。 +# 如果想让Redis能更高效的运行,你也可以设置为"no",让操作系统决定什么时候去执行 +# 或者相反想让数据更安全你也可以设置为"always" +# +# 如果不确定就用 "everysec". + +# appendfsync always +appendfsync everysec +# appendfsync no + +# AOF策略设置为always或者everysec时,后台处理进程(后台保存或者AOF日志重写)会执行大量的I/O操作 +# 在某些Linux配置中会阻止过长的fsync()请求。注意现在没有任何修复,即使fsync在另外一个线程进行处理 +# +# 为了减缓这个问题,可以设置下面这个参数no-appendfsync-on-rewrite +# +# This means that while another child is saving the durability of Redis is +# the same as "appendfsync none", that in pratical terms means that it is +# possible to lost up to 30 seconds of log in the worst scenario (with the +# default Linux settings). +# +# If you have latency problems turn this to "yes". Otherwise leave it as +# "no" that is the safest pick from the point of view of durability. +no-appendfsync-on-rewrite no + +# Automatic rewrite of the append only file. +# AOF 自动重写 +# 当AOF文件增长到一定大小的时候Redis能够调用 BGREWRITEAOF 对日志文件进行重写 +# +# 它是这样工作的:Redis会记住上次进行些日志后文件的大小(如果从开机以来还没进行过重写,那日子大小在开机的时候确定) +# +# 基础大小会同现在的大小进行比较。如果现在的大小比基础大小大制定的百分比,重写功能将启动 +# 同时需要指定一个最小大小用于AOF重写,这个用于阻止即使文件很小但是增长幅度很大也去重写AOF文件的情况 +# 设置 percentage 为0就关闭这个特性 +auto-aof-rewrite-percentage 100 +auto-aof-rewrite-min-size 64mb + +################################## SLOW LOG ################################### +# Redis Slow Log 记录超过特定执行时间的命令。执行时间不包括I/O计算比如连接客户端,返回结果等,只是命令执行时间 +# +# 可以通过两个参数设置slow log:一个是告诉Redis执行超过多少时间被记录的参数slowlog-log-slower-than(微妙), +# 另一个是slow log 的长度。当一个新命令被记录的时候最早的命令将被从队列中移除 + + +# 下面的时间以微妙微单位,因此1000000代表一分钟。 +# 注意制定一个负数将关闭慢日志,而设置为0将强制每个命令都会记录 +slowlog-log-slower-than 10000 + + +# 对日志长度没有限制,只是要注意它会消耗内存 +# 可以通过 SLOWLOG RESET 回收被慢日志消耗的内存 +slowlog-max-len 1024 + +################################ VM ############################### +### WARNING! Virtual Memory is deprecated in Redis 2.4 +### The use of Virtual Memory is strongly discouraged. + +# Virtual Memory allows Redis to work with datasets bigger than the actual +# amount of RAM needed to hold the whole dataset in memory. +# In order to do so very used keys are taken in memory while the other keys +# are swapped into a swap file, similarly to what operating systems do +# with memory pages. +# +# To enable VM just set 'vm-enabled' to yes, and set the following three +# VM parameters accordingly to your needs. + +vm-enabled no +# vm-enabled yes + +# This is the path of the Redis swap file. As you can guess, swap files +# can't be shared by different Redis instances, so make sure to use a swap +# file for every redis process you are running. Redis will complain if the +# swap file is already in use. +# +# The best kind of storage for the Redis swap file (that's accessed at random) +# is a Solid State Disk (SSD). +# +# *** WARNING *** if you are using a shared hosting the default of putting +# the swap file under /tmp is not secure. Create a dir with access granted +# only to Redis user and configure Redis to create the swap file there. +vm-swap-file /tmp/redis.swap + +# vm-max-memory configures the VM to use at max the specified amount of +# RAM. Everything that deos not fit will be swapped on disk *if* possible, that +# is, if there is still enough contiguous space in the swap file. +# +# With vm-max-memory 0 the system will swap everything it can. Not a good +# default, just specify the max amount of RAM you can in bytes, but it's +# better to leave some margin. For instance specify an amount of RAM +# that's more or less between 60 and 80% of your free RAM. +vm-max-memory 0 + +# Redis swap files is split into pages. An object can be saved using multiple +# contiguous pages, but pages can't be shared between different objects. +# So if your page is too big, small objects swapped out on disk will waste +# a lot of space. If you page is too small, there is less space in the swap +# file (assuming you configured the same number of total swap file pages). +# +# If you use a lot of small objects, use a page size of 64 or 32 bytes. +# If you use a lot of big objects, use a bigger page size. +# If unsure, use the default :) +vm-page-size 32 + +# Number of total memory pages in the swap file. +# Given that the page table (a bitmap of free/used pages) is taken in memory, +# every 8 pages on disk will consume 1 byte of RAM. +# +# The total swap size is vm-page-size * vm-pages +# +# With the default of 32-bytes memory pages and 134217728 pages Redis will +# use a 4 GB swap file, that will use 16 MB of RAM for the page table. +# +# It's better to use the smallest acceptable value for your application, +# but the default is large in order to work in most conditions. +vm-pages 134217728 + +# Max number of VM I/O threads running at the same time. +# This threads are used to read/write data from/to swap file, since they +# also encode and decode objects from disk to memory or the reverse, a bigger +# number of threads can help with big objects even if they can't help with +# I/O itself as the physical device may not be able to couple with many +# reads/writes operations at the same time. +# +# The special value of 0 turn off threaded I/O and enables the blocking +# Virtual Memory implementation. +vm-max-threads 4 + +############################### ADVANCED CONFIG ############################### + +# 当hash中包含超过指定元素个数并且最大的元素没有超过临界时, +# hash将以一种特殊的编码方式(大大减少内存使用)来存储,这里可以设置这两个临界值 +# Redis Hash对应Value内部实际就是一个HashMap,实际这里会有2种不同实现, +# 这个Hash的成员比较少时Redis为了节省内存会采用类似一维数组的方式来紧凑存储,而不会采用真正的HashMap结构,对应的value redisObject的encoding为zipmap, +# 当成员数量增大时会自动转成真正的HashMap,此时encoding为ht。 +hash-max-zipmap-entries 512 +hash-max-zipmap-value 64 + +# list数据类型多少节点以下会采用去指针的紧凑存储格式。 +# list数据类型节点值大小小于多少字节会采用紧凑存储格式。 +list-max-ziplist-entries 512 +list-max-ziplist-value 64 + +# set数据类型内部数据如果全部是数值型,且包含多少节点以下会采用紧凑格式存储。 +set-max-intset-entries 512 + +# zsort数据类型多少节点以下会采用去指针的紧凑存储格式。 +# zsort数据类型节点值大小小于多少字节会采用紧凑存储格式。 +zset-max-ziplist-entries 128 +zset-max-ziplist-value 64 + + +# Redis将在每100毫秒时使用1毫秒的CPU时间来对redis的hash表进行重新hash,可以降低内存的使用 +# +# 当你的使用场景中,有非常严格的实时性需要,不能够接受Redis时不时的对请求有2毫秒的延迟的话,把这项配置为no。 +# +# 如果没有这么严格的实时性要求,可以设置为yes,以便能够尽可能快的释放内存 +activerehashing yes + +################################## INCLUDES ################################### + +# 指定包含其它的配置文件,可以在同一主机上多个Redis实例之间使用同一份配置文件,而同时各个实例又拥有自己的特定配置文件 + +# include /path/to/local.conf +# include /path/to/other.conf +``` + +#### HELP ++ [http://www.2cto.com/database/201307/225113.html](http://www.2cto.com/database/201307/225113.html) ++ [一头坑进Redis之持久化Snapshot和AOF说明](http://www.2cto.com/database/201708/670448.html) +#### 名词解释 ++ redis支持两种持久化方式: + + 一种是 Snapshot(RDB)<二进制文件> 也是默认方式 + > 即按照一定的策略周期性的将数据保存到磁盘。对应产生的数据文件为dump.rdb, + 通过配置文件中的save参数来定义快照的周期。Redis支持将当前数据的快照存成一个数据文件的持久化机制。 + 而一个持续写入的数据库如何生成快照呢。Redis借助了fork命令的copy on write机制。 + 在生成快照时,将当前进程fork出一个子进程,然后在子进程中循环所有的数据,将数据写成为RDB文件。 + + 另一种是Append only file(AOF)的方式 + + + + + + + + + diff --git a/Redis/redis-install.md b/Redis/redis-install.md new file mode 100644 index 0000000..53b30bd --- /dev/null +++ b/Redis/redis-install.md @@ -0,0 +1,223 @@ +## Redis 简易安装教程 +#### 一、编译安装 ++ 下载、解压 + + ```javascript + wget http://download.redis.io/releases/redis-3.2.8.tar.gz + tar -zxvf redis-3.2.8.tar.gz + cd redis-3.2.8 + ``` ++ make 编译 + + 编译之前 + + ```lua + 00-RELEASENOTES BUGS CONTRIBUTING COPYING deps INSTALL Makefile MANIFESTO README.md redis.conf runtest + runtest-cluster runtest-sentinel sentinel.conf src tests utils + ``` + + 编译完成之后,可以看到解压文件redis-3.0.7 中会有对应的src、conf等文件 + + 这和windows下安装解压的文件一样,大部分安装包都会有对应的类文件、配置文件和一些命令文件。 ++ 进入src文件夹,执行make install进行Redis安装 + + ```bash + tinywan@tinywan:~/redis-3.2.8/src$ sudo make install + [sudo] tinywan 的密码: + + Hint: It's a good idea to run 'make test' ;) + + INSTALL install + INSTALL install + INSTALL install + INSTALL install + INSTALL install + ``` +#### 二、部署文件结构 ++ 首先为了方便管理,将Redis文件中的conf配置文件和常用命令移动到统一文件中 ++ 创建以下文件目录 + + ```bash + ~/redis-3.2.8/src$ sudo mkdir -p /usr/local/redis/bin + ~/redis-3.2.8/src$ sudo mkdir -p /usr/local/redis/etc + ``` ++ 切换到`redis-3.2.8`目录,移动`redis.conf`配置文件: + + ```javascript + ~/redis-3.2.8/src$ cd .. + ~/redis-3.2.8$ sudo mv /home/tinywan/redis-3.2.8/redis.conf /usr/local/redis/etc + ``` ++ 继续进入到`src`目录执行其他文件移动: + + ```javascript + ~/redis-3.2.8$ cd src/ + sudo mv mkreleasehdr.sh redis-benchmark redis-check-aof redis-check-rdb redis-cli + redis-sentinel redis-server redis-trib.rb /usr/local/redis/bin + ``` +#### 三、配置和启动redis服务 ++ 编辑`redis.conf` + + ``` + cd /usr/local/redis/etc + vi redis.conf + ``` ++ 需要修改的参数 + + ```lua + --后台运行 + daemonize yes + + --端口号 + port 63700 + + --和哪个网卡绑定,和客户端是什么网段没有关系,这里我绑定的是内网网卡, + bind 10.10.101.127 + + -- AES("https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Redis/redis-install.md") 加密 + -- 结果:b6Pbc42gP8hXPNLzZaDnhREijtn1BSVSIYTkhTXw8SuPGpWZvN5kVpVeEVBdEQDw7M/+EZuDS6FxTOtgD2QrPe6014LPEdv2DY+YSUQZ4cE= + + requirepass b6Pbc42gP8hXPNLzZaDnhREijtn1BSVSIYTkhTXw8SuPGpWZvN5kVpVeEVBdEQDw7M/+EZuDS6FxTOtgD2QrPe6014LPEdv2DY+YSUQZ4cE= + + -- db文件名 + dbfilename dump63700.rdb + + -- log 日志文件路径 + logfile "/usr/local/redis/etc/redis_63700.log" + + -- 安全考虑,rename-command 配置以下命令 + rename-command FLUSHALL "tinywangithubFLUSHALL" + + rename-command CONFIG "tinywangithubCONFIG" + + rename-command SHUTDOWN "tinywangithubSHUTDOWN" + + rename-command DEBUG "tinywangithubDEBUG" + ``` + 启动redis服务,并指定启动服务配置文件,检测运行端口,为了安全,请不要使用root用户去启动 + ```java + $ sudo chown -R www:www /usr/local/redis/ //赋予指定该用户组,而非root账号 + $ /usr/local/redis/bin/redis-server /usr/local/redis/etc/redis63700.conf + $ ps -aux | grep redis + www 70764 0.6 0.1 38160 0:00 /usr/local/redis/bin/redis-server 127.0.0.1:63700 + www 70768 0.0 0.0 15984 0:00 grep --color=auto redis + ``` ++ redis-cli启动、检测重置命令是否生效(结果:配置文件已经OK) + ```lua + $ redis-cli -h 127.0.0.1 -p 63700 -a b6Pbc42gP8hXPNLzZaDnhREijtn1BSVSIYTkhTXw8SuPGpWZvN5kVpVeEVBdEQDw7M/+EZuDS6FxTOtgD2QrPe6014LPEdv2DY+YSUQZ4cE= + 127.0.0.1:63700> set username tinywan + OK + 127.0.0.1:63700> get username + "tinywan" + 127.0.0.1:63700> SHUTDOWN + (error) ERR unknown command 'SHUTDOWN' + 127.0.0.1:63700> FLUSHALL + (error) ERR unknown command 'FLUSHALL' + 127.0.0.1:63700> tinywangithubFLUSHALL + OK + 127.0.0.1:63700> get username + (nil) + 127.0.0.1:63700> + ``` ++ 远程链接出现的错误: + + 错误信息 + + ```lua + DENIED Redis is running in protected mode because protected mode is enabled, + no bind address was specified, no authentication password is requested to clients....... + ``` + + 修改配置文件:`protected-mode yes` 修改为`protected-mode no ` ++ 查看远程Redis服务器的版本 `redis-cli -h 192.168.1.3 info | grep 'redis_version'` + +#### 四、Redis开机启动的方法 ++ [Linux中设置Redis开机启动的方法](http://www.jb51.net/article/110286.htm) ++ 环境:`Ubuntu 16.04.2 LTS` ++ 编辑脚本:`vim /etc/init.d/redis ` + + ```javascript + #!/bin/sh + # + # Simple Redis init.d script conceived to work on Linux systems + # as it does use of the /proc filesystem. + ### BEGIN INIT INFO + # Provides: redis6379 + # Required-Start: $local_fs $network + # Required-Stop: $local_fs + # Default-Start: 2 3 4 5 + # Default-Stop: 0 1 6 + # Short-Description: redis6379 + # Description: penavico redis 6379 + ### END INIT INFO + + REDISPORT=6379 # 【1】修改一 + EXEC=/usr/local/bin/redis-server + CLIEXEC=/usr/local/bin/redis-cli + + PIDFILE=/var/run/redis_${REDISPORT}.pid + CONF="/usr/local/redis/etc/redis_6379.conf" # 【2】修改二 + + case "$1" in + start) + if [ -f $PIDFILE ] + then + echo "$PIDFILE exists, process is already running or crashed" + else + echo "Starting Redis server..." + $EXEC $CONF + fi + ;; + stop) + if [ ! -f $PIDFILE ] + then + echo "$PIDFILE does not exist, process is not running" + else + PID=$(cat $PIDFILE) + echo "Stopping ..." + $CLIEXEC -p $REDISPORT shutdown + while [ -x /proc/${PID} ] + do + echo "Waiting for Redis to shutdown ..." + sleep 1 + done + echo "Redis stopped" + fi + ;; + *) + echo "Please use start or stop as first argument" + ;; + esac + ``` ++ 注册事件,开机启动:`update-rc.d redis defaults` ++ 启动服务:`sudo systemctl start redis` ++ 停止服务:`sudo systemctl stop redis` ++ 查看服务是否启动: + + ```javascript + www@Tinywan:~/redis-4.0.0/utils$ ps -aux | grep redis + root 1722 0.0 0.8 44752 8300 ? Ssl 13:08 0:00 /usr/local/bin/redis-server 127.0.0.1:6379 + www 1730 0.0 0.1 14224 1024 pts/0 S+ 13:08 0:00 grep --color=auto redis + + ``` +#### 五、Redis关闭 +* 命令方式关闭 + ``` + $ redis-cli -h 127.0.0.1 -p 6379 + 127.0.0.1:6379> shutdown + ``` +* 进程号杀掉redis + ``` + ps -ef | grep redis + kill -9 XXX + ``` +#### 六、Redis数据迁移 ++ 查找RDB文件: + + ```sudo find / -name dump.rdb``` ++ 进行远程拷贝备份文件: + + ```scp ./dump.rdb www@192.168.1.18:/home/www/redis/``` ++ 数据迁移步骤如下 + + (1)关闭目标Redis服务; + + (2)将相应的RDB文件或者AOF文件复制过去; + + (3)设置REDIS的DIR或者开启AOF功能; + + (4)启动目标REDIS服务; + + + + diff --git a/Redis/redis-lua.md b/Redis/redis-lua.md new file mode 100644 index 0000000..cd2d277 --- /dev/null +++ b/Redis/redis-lua.md @@ -0,0 +1,210 @@ +####
      Redis执行Lua脚本基本用法 +## 基本语法 +--- ++ Hello, Lua! + + > 我们的第一个Redis Lua 脚本仅仅返回一个字符串,而不会去与redis 以任何有意义的方式交互 + + ```Lua + local msg = "Hello, world!" + return msg + ``` + > 这是非常简单的,第一行代码定义了一个本地变量msg存储我们的信息, 第二行代码表示 从redis 服务端返回msg的值给客户端。 保存这个文件到Hello.lua,像这样去运行: + + ```Bash + www@iZ239kcyg8rZ:~/lua$ redis-cli EVAL "$(cat Hello.lua)" 0 + "Hello, world!" + ``` + > 运行这段代码会打印"Hello,world!", EVAL在第一个参数是我们的lua脚本, 这我们用cat命令从文件中读取我们的脚本内容。第二个参数是这个脚本需要访问的Redis 的键的数字号。我们简单的 “Hello Script" 不会访问任何键,所以我们使用0 + ++ redis.call() 与 redis.pcall()的区别 + + * 他们唯一的区别是当redis命令执行结果返回错误时 + * redis.call()将返回给调用者一个错误. + * redis.pcall()会将捕获的错误以Lua表的形式返回. + * redis.call() 和 redis.pcall() 两个函数的参数可以是任意的 Redis 命令 ++ EVAL命令格式 + + 基本语法 + ``` + EVAL script numkeys key [key ...] arg [arg ...] + ``` + + 语义 + 1. script即为lua脚本或lua脚本文件 + 1. key一般指lua脚本操作的键,在lua脚本文件中,通过KEYS[i]获取 + 1. arg指外部传递给lua脚本的参数,可以通过ARGV[i]获取 + + eval命令的用法 + ``` + 127.0.0.1:6379> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second + 1) "key1" + 2) "key2" + 3) "first" + 4) "second" + ``` + > 这个示例中lua脚本为一个return语句,返回了lua一个数组,这个数组四个元素分别是通过外部传入lua脚本。 + 因为redis内嵌了Lua虚拟机,因此redis接收到这个lua脚本之后,然后交给lua虚拟机执行。 + 当lua虚拟机执行结束,即将执行结果返回给redis,redis将结果按自己的协议转换为返回给客户端的回复,最后再通过TCP将回复发回给客户端 + + lua脚本参数的接受 + + 键值:`KEYS[i]` 来获取外部传入的键值 + + 参数:`ARGV[i]` 来获取外部传入的参数 ++ 案例介绍 + + 通过Redis命令`EVAL`执行一个简单的Lua脚本文件 + + 给Redis添加测试数据(通过有序集合和哈希对应的集合信息) + ```javascript + 127.0.0.1:6379> ZADD WEB 1 google + (integer) 1 + 127.0.0.1:6379> ZADD WEB 2 apple + (integer) 1 + 127.0.0.1:6379> ZADD WEB 3 baidu + (integer) 1 + 127.0.0.1:6379> ZRANGE WEB 0 3 + 1) "google" + 2) "apple" + 3) "baidu" + 127.0.0.1:6379> hmset google domain_name www.google.com ip 192.168.1.100 + OK + 127.0.0.1:6379> hmset baidu domain_name www.baidu.com ip 192.168.1.200 + OK + 127.0.0.1:6379> hmset apple domain_name www.apple.com ip 192.168.1.300 + OK + 127.0.0.1:6379> hgetall google + 1) "domain_name" + 2) "www.google.com" + 3) "ip" + 4) "192.168.1.100" + 127.0.0.1:6379> hgetall apple + 1) "domain_name" + 2) "www.apple.com" + 3) "ip" + 4) "192.168.1.300" + 127.0.0.1:6379> hgetall baidu + 1) "domain_name" + 2) "www.baidu.com" + 3) "ip" + 4) "192.168.1.200" + ``` + + Lua脚本,lua_get_redis.lua 文件 + ```Lua + -- 获取键值/参数 + local key,offset,limit = KEYS[1], ARGV[1], ARGV[2] + -- 通过ZRANGE获取键为key的有序集合元素,偏移量为offset,个数为limit,即所有WEB信息 + local names = redis.call('ZRANGE', key, offset, limit) + -- infos table 存储所有WEB信息 + local infos = {} + -- 遍历所有WEB信息 + for i=1,#names do + local ck = names[i] + -- 通过HGETALL命令获取每WEB的信息 + local info = redis.call('HGETALL',ck) + -- 并且在WEB信息中插入对应的集合信息 + table.insert(info,'HOST_NAME') + table.insert(info,names[i]) + --table.insert(info,'author',"Tinywan") + -- 插入infos中 + infos[i] = info + end + -- 将结果返回给redis + return infos + ``` + 1. redis.call() 函数的参数可以是任意的 Redis 命令 + 1. table.insert(table, pos, value) + > [1]table.insert()函数在table的数组部分指定位置(pos)插入值为value的一个元素. pos参数可选, 默认为数组部分末尾 + + 执行结果: + ``` + tinywan@:~/Lua$ sudo redis-cli --eval /home/tinywan/Lua/lua_get_redis.lua WEB , 0 2 + 1) 1) "domain_name" + 2) "www.google.com" + 3) "ip" + 4) "192.168.1.100" + 5) "HOST_NAME" + 6) "google" + 2) 1) "domain_name" + 2) "www.apple.com" + 3) "ip" + 4) "192.168.1.300" + 5) "HOST_NAME" + 6) "apple" + 3) 1) "domain_name" + 2) "www.baidu.com" + 3) "ip" + 4) "192.168.1.200" + 5) "HOST_NAME" + 6) "baidu" + ``` + + 注意:`lua_get_redis.lua WEB , 0 2` 之间的空格,不然会提示错误 + + 错误:`(error) ERR Error running script command arguments must be strings or integers` + + Ngx_lua 写入Redis数据,通过CURL请求 + + curl_get_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 key = ngx.var[1] + local new_timer = ngx.localtime() -- 本地时间:2017-04-16 15:56:59 + local value = key.."::"..new_timer + local ok , err = red:set(key,value) + if not ok then + ngx.say("failed to set "..key, err) + return + end + ngx.say("set result: ", ok) + + local res, err = red:get(key) + if not res then + ngx.say("get from redis error : ", err) + return + end + if res == ngx.null then + ngx.say(key.."not found.") + return + end + red:close() + ngx.say("Success get Redis Data",json.encode({content=res})) + ``` + + nginx.conf + ```Lua + location ~* /curl_insert_redis/(\w+)$ { + default_type 'text/html'; + lua_code_cache off; + content_by_lua_file /opt/openresty/nginx/conf/Lua/curl_get_redis.lua; + } + ``` + + curl 和浏览器请求结果(查询Redis数据库,数据已经插入成功) + ```Lua + root@tinywan:# curl http://127.0.0.1/curl_insert_redis/Tinywan1227 + set result: OK + Success get Redis Data{"content":"Tinywan1227::2017-04-16 15:57:56"} + ``` + + 通过lua脚本获取指定的key的List中的所有数据 + ```Lua + local key=KEYS[1] + local list=redis.call("lrange",key,0,-1); + return list; + ``` + + 根据外面传过来的IDList 做“集合去重”的lua脚本逻辑: + ```Lua + local result={}; + local myperson=KEYS[1]; + local nums=ARGV[1]; + + local myresult =redis.call("hkeys",myperson); + + for i,v in ipairs(myresult) do + local hval= redis.call("hget",myperson,v); + redis.log(redis.LOG_WARNING,hval); + if(tonumber(hval) dsfds + > fsdfds + +* 修改用户组:`sudo usermod -g www redis` +* 设置账号密码:`passwd redis` +* 使用低权限账号` redis` 启动redis服务 +```bash +sudo -u redis ../bin/redis-server ./redis.conf +``` +* 查看redis启动用户信息 +```bash +ps -axu | grep redis +redis 2495 0.0 0.4 38472 4624 ? Ssl May29 0:22 /redis-server *:63009 +redis 2510 0.0 0.3 36424 3528 ? Ssl May29 0:22 /redis-server 172.19.230.35:6379 +``` +> 启动两个服务,一个是通过公网可以访问,而另外一个是和内网绑定的 + +* 可能会遇到权限问题 + 提示:`Failed opening the RDB file dump.rdb (in server root dir /home) for saving: Permission denied`,出现上面的问题原因适用于以前使用`root`账号启动服务,现在使用级别较低的`redis`启动服务,导致redis服务没办法加载db文件导致的 ,所以赋予该db文件为redis用户所属:`chown redis:www 63789-dump.rdb` + +* redis 参数优化配置 + * 编辑 `vim /etc/sysctl.conf`文件,添加内容:`'vm.overcommit_memory = 1'`并且使用root执行`sysctl vm.overcommit_memory=1`是能够生效 + * 使用root执行命令:`echo never > /sys/kernel/mm/transparent_hugepage/enabled` + +* 禁止`redis`运行账号登录 `passwd -l redis`,这就话的意思是锁定`redis`用户,这样该用户就不能登录了。 + +##### 参考建议 + +日志文件和数据库文件最好使用绝对路径 + +* 日志文件路径:`logfile "/home/redis/log/6379.log"` +* 数据库文件路径:`dir "/home/redis/data"` + +#### 使用xshell连接阿里云服务器登陆时密码框为灰色,无法输入密码解决办法 + +* 使用阿里云`[远程连接]` +* 编辑文件:`vi/etc/ssh/sshd_config` +* 修改最后一项为yes:`PasswordAuthentication yes` +* 保存退出,重启`sshd`服务`systemctl restart sshd.service`然后重新登陆,此时,一切就OK啦 + +#### 参考文献 +* [redis crackit入侵事件总结](https://blog.csdn.net/u012573259/article/details/51803447) +* [Redis 配置不当致使 root 被提权漏洞](https://help.aliyun.com/knowledge_detail/37433.html) +* [linux 新建用户、用户组 以及为新用户分配权限](https://www.cnblogs.com/mingforyou/archive/2012/06/19/2555045.html) + + + + + + + diff --git a/Redis/redis5-config.md b/Redis/redis5-config.md new file mode 100644 index 0000000..14fc427 --- /dev/null +++ b/Redis/redis5-config.md @@ -0,0 +1,503 @@ +# Redis5 配置及优化总结 + +1. 配置文件中单元大小的解释,不区分大小写 + + ``` + # Note on units: when memory size is needed, it is possible to specify + # it in the usual form of 1k 5GB 4M and so forth: + # + # 1k => 1000 bytes + # 1kb => 1024 bytes + # 1m => 1000000 bytes + # 1mb => 1024*1024 bytes + # 1g => 1000000000 bytes + # 1gb => 1024*1024*1024 bytes + # + # units are case insensitive so 1GB 1Gb 1gB are all the same. + ``` + +2. 配置模板,针对个性化配置 + + ``` + # 假如说你有一个可用于所有的 redis server 的标准配置模板, + # 但针对某些 server 又需要一些个性化的设置, + # 你可以使用 include 来包含一些其他的配置文件,这对你来说是非常有用的。 + # include /path/to/local.conf + # include /path/to/other.conf + ``` + +3. 加载模块 + + ``` + 模块有两种加载方式, + 一是在配置文件redis.conf中使用loadmodule /path/to/mymodule.so在Redis启动时加载。 + 另一种方式在运行时使用命令MODULE LOAD /path/to/mymodule.so加载。加载的模块可以使用命令MODULE LIST查看,使用MODULE UNLOAD mymodule卸载。 + + 在载入的模块的时候可以传入参数,如:loadmodule mymodule.so foo bar 123456,参数会被传入模块的OnLoad方法中。 + # loadmodule /path/to/my_module.so + # loadmodule /path/to/other_module.so + ``` + +4. 安全相关bind/protected-mode + + ``` + 默认设置yes, 禁止公网访问redis cache,加强redis安全 + 它启用的条件,有两个: + 1) 没有bind IP + 2) 没有设置访问密码 + 配置redis的sentinel集群时,哨兵之间不能通信,不能进行主结节客观下线的判断,以及failover,解决呢办法sentinel.conf中加入了protected-mode no + 详情说明可以见:acceptCommonHandler源码说明 + + 关于bind设置说明 + bind配置了什么ip,别人就得访问bind里面配置的ip才访问到redis服务。 + bind配置的ip必须是本机的ip,一台机器可以对应多个ip地址,配置非本机的ip地址会报错,如果配置为 + bind 127.0.0.1 表明只能本机访问, + bind 0.0.0.0等价于 不配置 bind 即注释掉bind + + 想限制只有指定的主机可以连接到redis中,我们只能通过防火墙来控制,而不能通过redis中的bind参数来限制 + 很容易让人误解的一个配置 + ``` + +5. 访问端口 + + ``` + port 6379 默认值 选择这个端口号完全就是作者为了diss一个妹子,程序员真的是一个很屌丝的职业。。。 + 详情可见:http://oldblog.antirez.com/post/redis-as-LRU-cache.html + ``` + +6. TCP 监听的最大容纳数量 + + ``` + tcp-backlog 511 + //在高并发的环境下,你需要把这个值调高以避免客户端连接缓慢的问题。 + //值的大小是受somaxconn影响,调大需要修改内核的somaxconn值,实际应该是tcp过程中accept queue队列的最大值 + ``` + +7. unix socket方式来接收请求 + + ``` + # unixsocket /tmp/redis.sock + # unixsocketperm 700 + //通过unixsocket配置项来指定unix socket文件的路径,并通过unixsocketperm来指定文件的权限 + ``` + +8. redis-client连接断开时间 + + ``` + timeout 0 + redis-client一直没有请求发向server端,那么server端有权主动关闭这个连接,可以通过timeout来设置“空闲超时时限”,0表示永不关闭,推荐配置为300 + ``` + +9. tcp-keepalive 300 //tcp保持连接的时长 + +10. 守护进程 + + ``` + daemonize no 默认为no //进程pid号写入至redis.conf选项pidfile设置的文件中 + ``` + +11. supervised no + + ``` + 通过其他的守护进程upstart和systemd管理Redis守护进程,这个参数是和具体的操作系统相关的。 + ``` + +12. pidfile /var/run/redis_6379.pid + + ``` + 配置pid文件路径。当redis以守护模式启动时,如果没有配置pidfile,pidfile默认值是/var/run/redis.pid + ``` + +13. 日志等级/路径 + + ``` + debug(记录大量日志信息,适用于开发、测试阶段); verbose(较多日志信息); notice(适量日志信息,使用于生产环境);warning(仅有部分重要、关键信息才会被记录)。 + 日志文件的位置,当指定为空字符串时,为标准输出,如果redis已守护进程模式运行,那么日志将会输出到 /dev/null + #syslog-enabled设置为yes会把日志输出到系统日志,默认是no + #syslog-enabled no + #指定syslog的标示符,如果syslog-enabled是no,则这个选项无效。 + # syslog-ident redis + syslog-facility local0 若启用日志记录,则需要设置日志facility,可取值范围为local0~local7,表示不同的日志级别 + ``` + +14. databases 16 //默认数据库的数量 + +1. always-show-logo yes //设置是否在控制台显示redis logo图标 + +2. 快照备份相关设置 + + ``` + save 900 1 + save 300 10 + save 60 10000 + save + //指定在多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合 + stop-writes-on-bgsave-error yes //后台存储存储发生错误时禁止写入,默认为yes + rdbcompression yes //启动rdb文件压缩,耗费CPU资源,默认为yes,建议开启,压缩率高 + rdbchecksum yes //对rdb数据进行校验,耗费CPU资源,默认为yes + dbfilename dump.rdb //rdb文件名称 + dir ./ //路径 + ``` + +1. 主从复制相关配置 + + ``` + # replicaof 主节点ip和端口 + # masterauth 访问密码 + replica-serve-stale-data yes + replica-serve-stale-data yes,即使主从断了,从依然响应客户端的请求。 + replica-serve-stale-data no,主从断开了,则从会提示客户端"SYNC with master in progress",但有些指令还可以使用 INFO, replicaOF, AUTH, PING, SHUTDOWN, REPLCONF, ROLE, CONFIG,SUBSCRIBE, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PUBLISH, PUBSUB, COMMAND, POST, HOST: and LATENCY + replica-read-only yes //配置从节点是否只读 + repl-diskless-sync no //以往主从复制是生成rdb文件,然后传输给从节点,配置成yes后可以不进行写磁盘直接进行复制,适用于磁盘慢网络带宽大的场景 + repl-diskless-sync-delay 5//让主节点等待更多从节点来同时复制,设置过小,复制时来的从节点必须等待下一次rdb transfer 单位秒,如果小于0则启动失败 + # repl-ping-replica-period 10 //从发给主的心跳周期,如果小于0则启动失败 + # repl-timeout 60 多少秒没收到心跳的响应认为超时,最好设置的比 repl-ping-slave-period/repl-ping-replica-period大如果小于0则启动失败 + repl-disable-tcp-nodelay no //如果设置yes,会导致主从同步有40ms滞后(linux默认),如果no,则主从同步更及时 + # repl-backlog-size 1mb 复制积压大小,解决复制过程中从节点重连后不需要full sync,这个值越大,那么从节点断开到重连的时间就可以更长 + # repl-backlog-ttl 3600 //复制积压的生命期,超过多长时间从节点还没重连,则释放内存 + replica-priority 100 //给众多的从Redis设置优先级,在主Redis持续工作不正常的情况,优先级高的从Redis将会升级为主Redis。而编号越小,优先级越高。 比如一个主Redis有三个从Redis,优先级编号分别为10、100、25,那么编号为10的从Redis将会被首先选中升级为主Redis。 当优先级被设置为0时,这个从Redis将永远也不会被选中,默认的优先级为100 + # min-replicas-to-write 3 + # min-replicas-max-lag 10 + //假如有大于等于3个从Redis的连接延迟大于10秒,那么主Redis就不再接受外部的写请求。 上述两个配置中有一个被置为0,则这个特性将被关闭。默认情况下min-slaves-to-write为0,而min-slaves-max-lag为10 + + # replica-announce-ip 5.5.5.5 + # replica-announce-port 1234 + Redis master 可以通过不同方式列出连接上来的 replicas 节点的地址和端口。 如 Redis Sentinel 等会使用 “INFO replication” 命令来获取 replica 实例信息。 master 的 “ROLE“ 命令也会提供此信息。 + + 这个信息一般来说是通过 replica 节点通过一下方式获取然后报告上来的: + + IP:通过自动识别连接到 Socket 的信息自动获取 + Port:一般来说这个值就是 replicas 节点用来接受客户端的连接的监听端口 + 但是,若启用了端口转发或者 NAT,可能需要其他地址和端口才能连接到 replicas 节点。 这种情况下,需要设置这两个选项,这样 replicas 就会用这两个选项设置的值覆盖默认行为获取的值,然后报告给 master 节点。 根据实际情况,你可以只设置其中某个选项,而不用两个选项都设置。 + ``` + +1. 安全 + + ``` + # requirepass foobared + 设置Redis连接密码,如果配置了连接密码,客户端在连接Redis时需要通过AUTH 命令提供密码,默认关闭 + ps:由于 Redis 非常快,外部用户若进行暴力破解,每秒能尝试超过 150k 个密码。因此你需要使用强度非常高的密码才能保证密码不容易被暴力破解掉。 + rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52 + 在共享环境下,可以通过 rename-command 命令将一些危险的命令进行重命名。 如上所示将 CONFIG 命令重命名为难以猜测的命令。 + 可以通过将命令重命名为空字符串从而将其完全 ”kill“ 掉,如 + rename-command CONFIG "" + rename-command 命令会被记录到日志和 AOF 文件中或者传输到 replicas 节点,而这可能会是一个安全问题。 + ``` + +1. 客户端数量 + + ``` + # maxclients 10000 + 设置同一时间最大客户端连接数,默认无限制,Redis可以同时打开的客户端连接数为Redis进程可以打开的最大文件描述符数,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis会关闭新的连接并向客户端返回max number of clients reached错误信息,上限值和系统的句柄连接数相关。 + ``` + +2. 内存设置相关 + + ``` + # maxmemory + // 指定Redis最大内存限制,Redis新的vm机制,会把Key存放内存,Value会存放在swap区,意义不大。 + redis内存管理策略,默认为noeviction + 1.volatile-lru(least recently used):最近最少使用算法,从设置了过期时间的键中选择空转时间最长的键值对清除掉; + + 2.volatile-lfu(least frequently used):最近最不经常使用算法,从设置了过期时间的键中选择某段时间之内使用频次最小的键值对清除掉; + + 3.volatile-ttl:从设置了过期时间的键中选择过期时间最早的键值对清除; + + 4.volatile-random:从设置了过期时间的键中,随机选择键进行清除; + + 5.allkeys-lru:最近最少使用算法,从所有的键中选择空转时间最长的键值对清除; + + 6.allkeys-lfu:最近最不经常使用算法,从所有的键中选择某段时间之内使用频次最少的键值对清除; + + 7.allkeys-random:所有的键中,随机选择键进行删除; + + 8.noeviction:不做任何的清理工作,在redis的内存超过限制之后,所有的写入操作都会返回错误;但是读操作都能正常的进行; + + # maxmemory-policy noeviction + + maxmemory-samples 5个key作为样本池进行抽样清理,默认值5为最优 + # replica-ignore-maxmemory yes replica 节点会忽略 maxmemory 设置 + ``` + +1. 惰性删除 + + ``` + lazyfree-lazy-eviction no + lazyfree-lazy-expire no + lazyfree-lazy-server-del no + replica-lazy-flush no + 默认设置为no,个人感觉应该全部设置yes,代码会根据实际情况进行判断,是否进行惰性删除。 + ``` + +2. aof持久化配置 + + ``` + appendonly no + appendfilename "appendonly.aof" + + # appendfsync always + appendfsync everysec + # appendfsync no + + 1、appendfsync no + 当设置appendfsync为no的时候,Redis不会主动调用fsync去将AOF日志内容同步到磁盘,所以这一切就完全依赖于操作系统的调试了。对大多数Linux操作系统,是每30秒进行一次fsync,将缓冲区中的数据写到磁盘上。 + 2、appendfsync everysec + 当设置appendfsync为everysec的时候,Redis会默认每隔一秒进行一次fsync调用,将缓冲区中的数据写到磁盘。但是当这一 次的fsync调用时长超过1秒时。Redis会采取延迟fsync的策略,再等一秒钟。也就是在两秒后再进行fsync,这一次的fsync就不管会执行多长时间都会进行。这时候由于在fsync时文件描述符会被阻塞,所以当前的写操作就会阻塞。 + 所以,结论就是:在绝大多数情况下,Redis会每隔一秒进行一次fsync。在最坏的情况下,两秒钟会进行一次fsync操作,掉数据也就1-2秒 + 这一操作在大多数数据库系统中被称为group commit,就是组合多次写操作的数据,一次性将日志写到磁盘。 + 3、appednfsync always + 当设置appendfsync为always时,每一次写操作都会调用一次fsync,这时数据是最安全的,当然,由于每次都会执行fsync,所以其性能也会受到影响。 + /////////////////////////////// + no-appendfsync-on-rewrite no + bgrewriteaof机制,在一个子进程中进行aof的重写,从而不阻塞主进程对其余命令的处理,同时解决了aof文件过大问题。 + 现在问题出现了,同时在执行bgrewriteaof操作和主进程写aof文件的操作,两者都会操作磁盘,而bgrewriteaof往往会涉及大量磁盘操作,这样就会造成主进程在写aof文件的时候出现阻塞的情形,现在no-appendfsync-on-rewrite参数出场了。如果该参数设置为no,是最安全的方式,不会丢失数据,但是要忍受阻塞的问题。如果设置为yes呢?这就相当于将appendfsync设置为no,这说明并没有执行磁盘操作,只是写入了缓冲区,因此这样并不会造成阻塞(因为没有竞争磁盘),但是如果这个时候redis挂掉,就会丢失数据。丢失多少数据呢?在linux的操作系统的默认设置下,最多会丢失30s的数据。 + + 因此,如果应用系统无法忍受延迟,而可以容忍少量的数据丢失,则设置为yes。如果应用系统无法忍受数据丢失,则设置为no。 + + /////////////// rewrite执行临界值 + auto-aof-rewrite-percentage 100 + auto-aof-rewrite-min-size 64mb + 自动重写 append only file 选项。 当 AOF 文件增长到一定比例后,Redis 可以自动通过隐式调用 BGREWRITEAOF 命令来重写 AOF 文件。 + Redis 会记录上一次 rewrite 的 AOF 文件大小(若从未进行 rewrite,Redis 会使用启动时的 AOF 文件大小)作为基准大小。 + Redis 会比较当前大小和基准大小,若当前大小大于一定比例则触发 rewrite。 为了防止增长比例到了但是总数据量还是非常小的情况就触发 rewrite,你还需要指定一个 AOF rewritten 的最小大小。 + 通过将 auto-aof-rewrite-percentage 设置为 0 可以禁用此功能。 + + /////////aof文件不完整 + aof-load-truncated yes + 当 Redis 启动时可能会发现 AOF 文件被截断了(不完整),这可能是由于系统崩溃了, 特别是 ext4 没有以 data=ordered 选项挂载的情况(在 Redis 崩溃而系统正常的情况下不会发生截断)。 + 当这种情况发生时,Redis 可以选择终止进程,或者加载 AOF 文件上尽可能多的数据(目前的默认行为)。 + 当 aof-load-truncated 设置为 yes, Redis 服务端在启动的时候发现加载的 AOF 文件是被截断的会发送一条日志来通知客户。 若 aof-load-truncated 设置为 no,服务端会以错误形式终止进程并拒绝启动。 这是需要用户在重启服务前使用 "redis-check-aof" 工具来修复 AOF 文件。 + + aof-use-rdb-preamble yes //aof,rdb混合持久化 + ``` + +1. lua脚本 + + ``` + lua-time-limit 5000 + 此选项用于控制 Lua 脚本的最长执行时间,单位为毫秒。 + 当 Lua 脚本的执行时间超出限制后,Redis 会在写入相关日志,并且向客户端返回出错。 + ``` + +1. redis cluster配置 + + ``` + # cluster-enabled yes + 如果配置yes则开启集群功能,此redis实例作为集群的一个节点,否则,它是一个普通的单一的redis实例。 + + # cluster-config-file nodes-6379.conf + 集群配置文件,但是此配置文件不能人工编辑,它是集群节点自动维护的文件,主要用于记录集群中有哪些节点、他们的状态以及一些持久化参数等,方便在重启时恢复这些状态。通常是在收到请求之后这个文件就会被更新。 + + # cluster-node-timeout 15000 + 这是集群中的节点能够失联的最大时间,超过这个时间,该节点就会被认为故障。如果主节点超过这个时间还是不可达,则用它的从节点将启动故障迁移,升级成主节点。注意,任何一个节点在这个时间之内如果还是没有连上大部分的主节点,则此节点将停止接收任何请求。一般设置为15秒即可。 + + # cluster-replica-validity-factor 10 + 如果数据太旧,集群中的不可用master的slave节点会避免成为备用master。如果slave和master失联时间超过:(node-timeout * slave-validity-factor) + repl-ping-slave-period则不会被提升为master。 + 如node-timeout为30秒,slave-validity-factor为10, 默认default repl-ping-slave-period为10秒,失联时间超过310秒slave就不会成为master。 + 较大的slave-validity-factor值可能允许包含过旧数据的slave成为master,同时较小的值可能会阻止集群选举出新master。 + 为了达到最大限度的高可用性,可以设置为0,即slave不管和master失联多久都可以提升为master + + #cluster-migration-barrier + 只有在之前master有其它指定数量的工作状态下的slave节点时,slave节点才能提升为master。默认为1(即该集群至少有3个节点,1 master+2 slaves,master宕机,仍有另外1个slave的情况下其中1个slave可以提升) + 测试环境可设置为0,生成环境中至少设置为1 + + #cluster-require-full-coverage yes + 默认情况下如果redis集群如果检测到至少有1个hash slot不可用,集群将停止查询数据。 + 如果所有slot恢复则集群自动恢复。 + 如果需要集群部分可用情况下仍可提供查询服务,设置为no。 + + #cluster-migration-barrier 1 + 只有在之前master有其它指定数量的工作状态下的slave节点时,slave节点才能提升为master。默认为1(即该集群至少有3个节点,1 master+2 slaves,master宕机,仍有另外1个slave的情况下其中1个slave可以提升) + 测试环境可设置为0,生成环境中至少设置为1 + + #cluster-require-full-coverage yes + 默认情况下如果redis集群如果检测到至少有1个hash slot不可用,集群将停止查询数据。 + 如果所有slot恢复则集群自动恢复。 + 如果需要集群部分可用情况下仍可提供查询服务,设置为no。 + + #cluster-replica-no-failover no + 选项设置为yes时,会阻止replicas尝试对其master在主故障期间进行故障转移 + 然而,master仍然可以执行手动故障转移,如果强制这样做的话。 + ``` + +1. DOCKER下的集群配置 + + ``` + # * cluster-announce-ip //实际为各节点网卡分配ip + # * cluster-announce-port //节点映射端口 + # * cluster-announce-bus-port //集群总线端口 + + 在Docker环境中,如果使用的不是host网络模式,在容器内部的IP和PORT都是隔离的,那么客户端和其他节点无法通过节点公布的IP和PORT建立连接,增加这三个配置,Redis节点会将配置中的这些IP和PORT告知客户端或其他节点。而这些IP和PORT是通过Docker转发到容器内的临时IP和PORT的。 + ``` + +[![redis-config-client.png](redis-config-client.png) + +2. 慢查询 + + slowlog-log-slower-than 10000 + + 慢查询日志,记录超过多少微秒的查询命令。查询的执行时间不包括客户端的IO执行和网络通信时间,只是查询命令执行时间。 + + 1000000等于1秒,设置为0则记录所有命令 + + ``` + slowlog-max-len 128 + + 记录大小,可通过SLOWLOG RESET命令重置 + ``` + +1. 监控 + + ``` + latency-monitor-threshold 0 + redis延时监控系统在运行时会采样一些操作,以便收集可能导致延时的数据根源。 + 通过 LATENCY命令 可以打印一些图样和获取一些报告,方便监控 + 这个系统仅仅记录那个执行时间大于或等于预定时间(毫秒)的操作, + 这个预定时间是通过latency-monitor-threshold配置来指定的, + 当设置为0时,这个监控系统处于停止状态 + ``` + +1. 过期事件通知 + + ``` + notify-keyspace-events Ex // 打开此配置,其中Ex表示键事件通知里面的key过期事件,每当有过期键被删除时,会发送通知 + ``` + +1. 数据结构相关配置 + + ``` + hash-max-ziplist-entries 512 + hash类型的数据结构在编码上可以使用ziplist和hashtable。ziplist的特点就是文件存储(以及内存存储)所需的空间较小,在内容较小时,性能和hashtable几乎一样.因此redis对hash类型默认采取ziplist。如果hash中条目的条目个数或者value长度达到阀值,将会被重构为hashtable。 + 这个参数指的是ziplist中允许存储的最大条目个数,,默认为512,建议为128 + + hash-max-ziplist-value 64 + ziplist中允许条目value值最大字节数,默认为64,建议为1024 + + 当取正值的时候,表示按照数据项个数来限定每个quicklist节点上的ziplist长度。比如,当这个参数配置 + 成5的时候,表示每个quicklist节点的ziplist最多包含5个数据项。 + 当取负值的时候,表示按照占用字节数来限定每个quicklist节点上的ziplist长度。这时,它只能取-1到-5 + 这五个值,每个值含义如下: + -5: 每个quicklist节点上的ziplist大小不能超过64 Kb。(注:1kb => 1024 bytes) + -4: 每个quicklist节点上的ziplist大小不能超过32 Kb。 + -3: 每个quicklist节点上的ziplist大小不能超过16 Kb。 + -2: 每个quicklist节点上的ziplist大小不能超过8 Kb。(-2是Redis给出的默认值) + -1: 每个quicklist节点上的ziplist大小不能超过4 Kb。 + list-max-ziplist-size -2 + + 这个参数表示一个quicklist两端不被压缩的节点个数。 + 注:这里的节点个数是指quicklist双向链表的节点个数,而不是指ziplist里面的数据项个数。 + 实际上,一个quicklist节点上的ziplist,如果被压缩,就是整体被压缩的。 + 参数list-compress-depth的取值含义如下: + 0: 是个特殊值,表示都不压缩。这是Redis的默认值。 + 1: 表示quicklist两端各有1个节点不压缩,中间的节点压缩。 + 2: 表示quicklist两端各有2个节点不压缩,中间的节点压缩。 + 3: 表示quicklist两端各有3个节点不压缩,中间的节点压缩。 + 由于0是个特殊值,很容易看出quicklist的头节点和尾节点总是不被压缩的,以便于在表的两端进行快速存取。 + list-compress-depth 0 + + set-max-intset-entries 512 + intset中允许保存的最大条目个数,如果达到阀值,intset将会被重构为hashtable + + 与hash和list相似,有序集合也可以用一种特别的编码方式来节省大量空间。 + 这种编码只适合长度和元素都小于下面限制的有序集合 + zset-max-ziplist-entries 128 + zset-max-ziplist-value 64 + + hll-sparse-max-bytes 3000 + HyperLogLogs算法相关的,3000为最优值 + + + Streams宏节点最大大小/项目。 流数据结构是基数编码内部多个项目的大节点树。 + 使用此配置可以配置单个节点的字节数,以及切换到新节点之前可能包含的最大项目数 + 追加新的流条目。 如果以下任何设置设置为0,忽略限制,因此例如可以设置一个 + 大入口限制将max-bytes设置为0,将max-entries设置为所需的值 + stream-node-max-bytes 4096 + stream-node-max-entries 100 + + redis所用的哈希表实现(见dict.c)采用延迟哈希刷新机制:你对一个哈希表操作越多,哈希刷新操作就越频繁; + 反之,如果服务器非常不活跃那么也就是用点内存保存哈希表而已。 + 默认是每秒钟进行10次哈希表刷新,用来刷新字典,然后尽快释放内存。 + 建议:如果你对延迟比较在意的话就用 "activerehashing no", + 如果你不太在意延迟而希望尽快释放内存的话就设置 "activerehashing yes"。 + activerehashing yes + + client-output-buffer-limit normal 0 0 0 + client-output-buffer-limit slave 256mb 64mb 60 + client-output-buffer-limit pubsub 32mb 8mb 60 + + 客户端buffer控制。在客户端与server进行的交互中,每个连接都会与一个buffer关联,此buffer用来队列化等待被client接受的响应信息。如果client不能及时的消费响应信息,那么buffer将会被不断积压而给server带来内存压力.如果buffer中积压的数据达到阀值,将会导致连接被关闭,buffer被移除。 + + buffer控制类型包括:normal -> 普通连接;slave ->与slave之间的连接;pubsub ->pub/sub类型连接,此类型的连接,往往会产生此种问题;因为pub端会密集的发布消息,但是sub端可能消费不足. + 指令格式:client-output-buffer-limit ",其中hard表示buffer最大值,一旦达到阀值将立即关闭连接; + soft表示"容忍值",它和seconds配合,如果buffer值超过soft且持续时间达到了seconds,也将立即关闭连接,如果超过了soft但是在seconds之后,buffer数据小于了soft,连接将会被保留. + 其中hard和soft都设置为0,则表示禁用buffer控制.通常hard值大于soft. + + client-query-buffer-limit 1gb + 客户端查询缓存大小限制 + + proto-max-bulk-len 512mb + 批量请求的大小限制 + + 44 hz 10 + + Redis server执行后台任务的频率,默认为10,此值越大表示redis对"间歇性task"的执行次数越频繁(次数/秒)。"间歇性task"包括"过期集合"检测、关闭"空闲超时"的连接等,此值必须大于0且小于500。此值过小就意味着更多的cpu周期消耗,后台task被轮询的次数更频繁。此值过大意味着"内存敏感"性较差。建议采用默认值 + + dynamic-hz yes + 设置yes,则根据客户端连接数可以自动调节hz + + aof-rewrite-incremental-fsync yes + 设置yes,则每32mb 执行fsync一次(增量式,避免一次性大写入导致的延时) + 设置no,则一次性fsync + + rdb-save-incremental-fsync yes + 设置yes,则每32mb 执行fsync一次(增量式,避免一次性大写入导致的延时) + 设置no,则一次性fsync + + + # lfu-log-factor 10 + # lfu-decay-time 1 + lfu算法相关,默认配置为最优配置,详情可以见之前的文章说明 + ``` + +2. 内存碎片整理相关配置 + + ``` + # activedefrag yes + 碎片整理总开关 + + # active-defrag-ignore-bytes 100mb + # 内存碎片达到多少的时候开启整理 + + # active-defrag-threshold-lower 10 + # 碎片率达到百分之多少开启整理 + + # active-defrag-threshold-upper 100 + # 碎片率小余多少百分比开启整理 + + # active-defrag-cycle-min 5 + # 在CPU百分比中进行碎片整理的最小工作量 + + # active-defrag-cycle-max 75 + # 整理磁盘碎片占cpu百分比的最大值 + + # active-defrag-max-scan-fields 1000 + #从主字典扫描能够处理的set/hash/zset/list字段的最大数量 + + 4.0开始的实验性功能,经过1轮大版本的测试迭代,默认关闭。 + ``` + +## 优化及安全 + +1. 尽量使用pipeline提升吞吐量,提升非常明显 +2. 设置内存上限值maxmemory,根据业务配置优先释放方式 +3. 控制键和值得长度,长键可以考虑hash后存储,值过大,考虑压缩再存储,snappy +4. 集合短结构,细节见配置,短结构的数量限制在500~2000个以内,单个元素的长度限制在128个字节以内,其性能一般都会处于合理的范围之内。《Redis in Action》中推荐的配置为:数量限制为1024个,长度限制为64字节,这样可以同时兼顾低内存占用与高性能。 +5. 业务相关与持久化策略 +6. 禁止大面积的数据同一时间失效 +7. rename禁止永不分命令flushall flushdb等 +8. monitor对性能消耗过大,禁止长期开启 +9. bind/protected-mode 配置一定 +10. 密码,ssl传输数据 + + + diff --git "a/Redis/redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260(\347\254\254\344\272\214\347\211\210).pdf" "b/Redis/redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260(\347\254\254\344\272\214\347\211\210).pdf" new file mode 100644 index 0000000..ed27cf4 Binary files /dev/null and "b/Redis/redis\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260(\347\254\254\344\272\214\347\211\210).pdf" differ diff --git "a/Redis/\345\246\202\344\275\225\345\210\251\347\224\250Redisson\345\210\206\345\270\203\345\274\217\345\214\226\344\274\240\347\273\237Web\351\241\271\347\233\256.pdf" "b/Redis/\345\246\202\344\275\225\345\210\251\347\224\250Redisson\345\210\206\345\270\203\345\274\217\345\214\226\344\274\240\347\273\237Web\351\241\271\347\233\256.pdf" new file mode 100644 index 0000000..1b00a6d Binary files /dev/null and "b/Redis/\345\246\202\344\275\225\345\210\251\347\224\250Redisson\345\210\206\345\270\203\345\274\217\345\214\226\344\274\240\347\273\237Web\351\241\271\347\233\256.pdf" differ diff --git a/Shell/Backup-MySQL-FTP.md b/Shell/Backup-MySQL-FTP.md new file mode 100644 index 0000000..350c71e --- /dev/null +++ b/Shell/Backup-MySQL-FTP.md @@ -0,0 +1,74 @@ +### your favorite editor, create the script file +``` +vim backupdb.sh +``` + +```shell +#!/bin/bash + +############### Infos - Edit them accordingly ######################## + +DATE=`date +%Y-%m-%d_%H%M` +LOCAL_BACKUP_DIR="/backups" +DB_NAME="database_name" +DB_USER="root" +DB_PASSWORD="root_password" + +FTP_SERVER="111.111.111.111" +FTP_USERNAME="ftp-user" +FTP_PASSWORD="ftp-pass" +FTP_UPLOAD_DIR="/upload" + +LOG_FILE=/backups/backup-DATE.log + +############### Local Backup ######################## + +mysqldump -u $DB_USER -p$DB_PASSWORD $DB_NAME | gzip > $LOCAL_BACKUP_DIR/$DATE-$DB_NAME.sql.gz + +############### UPLOAD to FTP Server ################ + +ftp -nv $FTP_SERVER << EndFTP +user "$FTP_USERNAME" "$FTP_PASSWORD" +binary +cd $FTP_UPLOAD_DIR +lcd $LOCAL_BACKUP_DIR +put "$DATE-$DB_NAME.sql.gz" +bye +EndFTP + +############### Check and save log, also send an email ################ + +if test $? = 0 +then + echo "Database Successfully Uploaded to the Ftp Server!" + echo -e "Database Successfully created and uploaded to the FTP Server!" | mail -s "Backup from $DATE" your_email@email.com + +else + echo "Error in database Upload to Ftp Server" > $LOG_FILE +fi +``` +完成脚本编辑并保存文件后,我们可以通过以下命令使文件可执行: +``` +chmod +x backupdb.sh +``` +您现在可以通过在终端中输入进行测试。 +``` +/backups/backupdb.sh +``` +完成执行后,键入ls -a以查看数据库是否已备份。还要确认它是否已发送到您的FTP服务器。 + +>如果到目前为止一切正常,我们可以使用Crontab使它每天运行。 + +## Crontab + +您可以使用以下命令编辑crontab: +``` +crontab -e +``` +这将打开一个文本编辑器,您可以在其中输入每份工作的时间表并在新行上输入。 + +因此,在编辑器中,键入或粘贴以下行: +``` +30 02 * * * /backups/backupdb.sh +``` +上面的示例将在每月的每天的02:30 am运行/backups/backupdb.sh。当然,您可以根据需要更改时间。 \ No newline at end of file diff --git a/Shell/write-shell-suggestions.md b/Shell/write-shell-suggestions.md new file mode 100644 index 0000000..a018814 --- /dev/null +++ b/Shell/write-shell-suggestions.md @@ -0,0 +1,14 @@ +### 编写快速安全Bash脚本的建议 ++ [编写快速安全 Bash 脚本的建议 ](https://www.oschina.net/translate/bash-scripting-quirks-safety-tips) ++ 变量赋值 + + Bash变量并不要求全部大写,但是通常是大写的 + + 变量赋值不要在`=`运算符的两边放置空格符 + + 错误写法:`VARIABLE = 2` 或者`VARIABLE =2` + + 正确写法:`VARIABLE=2` ++ 使用${}引用变量 + + 有时某些变量,内容为file.txt,并且我想这样使用它: + + 错误写法:`mv $MYVAR $MYVAR__bak # wrong!` + + 这段代码是无法工作的!它会去查找MYVAR__bak变量,但这并不是一个真实存在的变量。 + + 正确使用:`mv $MYVAR ${MYVAR}__bak # right!` ++ 全局变量,局部变量和环境变量 + diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..c419263 --- /dev/null +++ b/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-cayman \ No newline at end of file diff --git a/docs/Complete-NGINX-Cookbook-2019.pdf b/docs/Complete-NGINX-Cookbook-2019.pdf new file mode 100644 index 0000000..3a9a15f Binary files /dev/null and b/docs/Complete-NGINX-Cookbook-2019.pdf differ diff --git a/docs/Images/CDN_proxy_LiveNode_URLStream.png b/docs/Images/CDN_proxy_LiveNode_URLStream.png new file mode 100644 index 0000000..2435737 Binary files /dev/null and b/docs/Images/CDN_proxy_LiveNode_URLStream.png differ diff --git a/docs/Images/Nginx+Lua+Local_Redis+Mysql.png b/docs/Images/Nginx+Lua+Local_Redis+Mysql.png new file mode 100644 index 0000000..8609124 Binary files /dev/null and b/docs/Images/Nginx+Lua+Local_Redis+Mysql.png differ diff --git a/docs/Images/Nginx-Phase.png b/docs/Images/Nginx-Phase.png new file mode 100644 index 0000000..a01e312 Binary files /dev/null and b/docs/Images/Nginx-Phase.png differ diff --git a/docs/Images/Openresty_lua-resty-upstream-healthcheck.png b/docs/Images/Openresty_lua-resty-upstream-healthcheck.png new file mode 100644 index 0000000..bdee67f Binary files /dev/null and b/docs/Images/Openresty_lua-resty-upstream-healthcheck.png differ diff --git a/docs/Images/URI-URL-Image.jpg b/docs/Images/URI-URL-Image.jpg new file mode 100644 index 0000000..baee05d Binary files /dev/null and b/docs/Images/URI-URL-Image.jpg differ diff --git a/docs/Images/alipay.png b/docs/Images/alipay.png new file mode 100644 index 0000000..64b635b Binary files /dev/null and b/docs/Images/alipay.png differ diff --git a/docs/Images/github_good1.png b/docs/Images/github_good1.png new file mode 100644 index 0000000..d7bdd34 Binary files /dev/null and b/docs/Images/github_good1.png differ diff --git a/docs/Images/lua.jpg b/docs/Images/lua.jpg new file mode 100644 index 0000000..5ed4bc7 Binary files /dev/null and b/docs/Images/lua.jpg differ diff --git a/docs/Images/nginx-hls-locations.png b/docs/Images/nginx-hls-locations.png new file mode 100644 index 0000000..baf90e7 Binary files /dev/null and b/docs/Images/nginx-hls-locations.png differ diff --git a/docs/Images/nginx_start_script.png b/docs/Images/nginx_start_script.png new file mode 100644 index 0000000..3678c65 Binary files /dev/null and b/docs/Images/nginx_start_script.png differ diff --git a/docs/Images/websocket_lua01.png b/docs/Images/websocket_lua01.png new file mode 100644 index 0000000..93262ee Binary files /dev/null and b/docs/Images/websocket_lua01.png differ diff --git a/docs/Images/wechat.png b/docs/Images/wechat.png new file mode 100644 index 0000000..a0f99da Binary files /dev/null and b/docs/Images/wechat.png differ diff --git a/docs/Nginx/Nginx-Web/Nginx-2-4-all-config.md b/docs/Nginx/Nginx-Web/Nginx-2-4-all-config.md new file mode 100644 index 0000000..6ed123e --- /dev/null +++ b/docs/Nginx/Nginx-Web/Nginx-2-4-all-config.md @@ -0,0 +1,75 @@ + +#### Nginx服务器基础配置命令 +--- ++ 基于域名的虚拟主机配置 + + 语法格式: + ``` + server_name name name2 name3 ... # 可以有一个或者多个名称并列,之间用空格隔开 + ``` + + 普通案例: + ``` + server_name www.tinywan.com www.redis.com # 第一个名称作为虚拟主机的主要名称 + ``` + + 通配符案例: + ``` + server_name *.tinywan.com www.redis.* # 通配符只能用在域名字符串名称的手段或尾端 + ``` + + 正则匹配案例: + ``` + server_name ~^www\d+\.tinywan.com$ + ``` + > 波浪号 ` ~ ` 作为正则表达式字符串的开始标记 + > 正则表达式含义: + >>` ^www ` 以 www 开头 + >>` \d+ ` ,`\d` 代表 0 ~ 9 的某一个数字,`+` 表示以前的字符出现一次或者多次 + >>`\.` ,由于`.`在正则表达式有特殊含义,因此需要转义字符`\`进行转义 + >> ` m$` 表示一个m结束 + + + 访问服务器域名 + > 可以访问 + ``` + www1.tinywan.com + ``` + + > 不可以访问 + ``` + www.tinywan.com + ``` ++ 基于IP的虚拟主机配置 + + 本机IP地址为: `192.168.127.129` + + 添加 IP 别名 (`192.168.127.131` 和 `192.168.127.141`) + ``` + sudo ifconfig ens33:0 192.168.127.131 netmask 255.255.255.0 up + + sudo ifconfig ens33:1 192.168.127.141 netmask 255.255.255.0 up + ``` + + 将以上两条命令添加到Linux 系统的启动脚本rc.local 中,系统重启后,ens33 的别名就自动设置好了(注意:sudo 权限) + ``` + echo "ifconfig ens33:1 192.168.127.131 netmask 255.255.255.0 up" >> /etc/rc.local + + echo "ifconfig ens33:1 192.168.127.131 netmask 255.255.255.0 up" >> /etc/rc.local + ``` + + /etc/rc.local 解释 + >在Linux启动的最后阶段,系统会执行存于rc.local中的命令 + + + 配置虚拟主机 + ``` + server { + listen 80; + server_name 192.168.127.131; + location / { + root /usr/local/nginx/html2; + } + } + + server { + listen 80; + server_name 192.168.127.141; + location / { + root /usr/local/nginx/html3; + } + } + ``` + + + diff --git a/docs/Nginx/Nginx-Web/Nginx-2-4-basic-config.md b/docs/Nginx/Nginx-Web/Nginx-2-4-basic-config.md new file mode 100644 index 0000000..d6dc13a --- /dev/null +++ b/docs/Nginx/Nginx-Web/Nginx-2-4-basic-config.md @@ -0,0 +1,302 @@ + +#### 基础配置文件 +--- ++ 完整基础配置nginx.conf +``` +user www www; ## Default: nobody +worker_processes 5; ## Default: 1 +error_log logs/error.log; +pid logs/nginx.pid; +worker_rlimit_nofile 8192; + +events { + worker_connections 4096; ## Default: 1024 +} + +http { + include conf/mime.types; + include /etc/nginx/proxy.conf; + include /etc/nginx/fastcgi.conf; + index index.html index.htm index.php; + + default_type application/octet-stream; + log_format main '$remote_addr - $remote_user [$time_local] $status ' + '"$request" $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + access_log logs/access.log main; + sendfile on; + tcp_nopush on; + server_names_hash_bucket_size 128; # this seems to be required for some vhosts + + server { # php/fastcgi + listen 80; + server_name domain1.com www.domain1.com; + access_log logs/domain1.access.log main; + root html; + + location ~ \.php$ { + fastcgi_pass 127.0.0.1:1025; + } + } + + server { # simple reverse-proxy + listen 80; + server_name domain2.com www.domain2.com; + access_log logs/domain2.access.log main; + + # serve static files + location ~ ^/(images|javascript|js|css|flash|media|static)/ { + root /var/www/virtual/big.server.com/htdocs; + expires 30d; + } + + # pass requests for dynamic content to rails/turbogears/zope, et al + location / { + proxy_pass http://127.0.0.1:8080; + } + } + + upstream big_server_com { + server 127.0.0.3:8000 weight=5; + server 127.0.0.3:8001 weight=5; + server 192.168.0.1:8000; + server 192.168.0.1:8001; + } + + server { # simple load balancing + listen 80; + server_name big.server.com; + access_log logs/big.server.access.log main; + + location / { + proxy_pass http://big_server_com; + } + } +} +``` ++ proxy_conf 扩展参数 +``` +proxy_redirect off; +proxy_set_header Host $host; +proxy_set_header X-Real-IP $remote_addr; +proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; +client_max_body_size 10m; +client_body_buffer_size 128k; +proxy_connect_timeout 90; +proxy_send_timeout 90; +proxy_read_timeout 90; +proxy_buffers 32 4k; +``` ++ fastcgi_conf 扩展参数 +``` +fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; +fastcgi_param QUERY_STRING $query_string; +fastcgi_param REQUEST_METHOD $request_method; +fastcgi_param CONTENT_TYPE $content_type; +fastcgi_param CONTENT_LENGTH $content_length; +fastcgi_param SCRIPT_NAME $fastcgi_script_name; +fastcgi_param REQUEST_URI $request_uri; +fastcgi_param DOCUMENT_URI $document_uri; +fastcgi_param DOCUMENT_ROOT $document_root; +fastcgi_param SERVER_PROTOCOL $server_protocol; +fastcgi_param GATEWAY_INTERFACE CGI/1.1; +fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; +fastcgi_param REMOTE_ADDR $remote_addr; +fastcgi_param REMOTE_PORT $remote_port; +fastcgi_param SERVER_ADDR $server_addr; +fastcgi_param SERVER_PORT $server_port; +fastcgi_param SERVER_NAME $server_name; + +fastcgi_index index.php; + +fastcgi_param REDIRECT_STATUS 200; 32 4k; +``` ++ mime_types 扩展参数 +``` +types { + text/html html htm shtml; + text/css css; + text/xml xml rss; + image/gif gif; + image/jpeg jpeg jpg; + application/x-javascript js; + text/plain txt; + text/x-component htc; + text/mathml mml; + image/png png; + image/x-icon ico; + image/x-jng jng; + image/vnd.wap.wbmp wbmp; + application/java-archive jar war ear; + application/mac-binhex40 hqx; + application/pdf pdf; + application/x-cocoa cco; + application/x-java-archive-diff jardiff; + application/x-java-jnlp-file jnlp; + application/x-makeself run; + application/x-perl pl pm; + application/x-pilot prc pdb; + application/x-rar-compressed rar; + application/x-redhat-package-manager rpm; + application/x-sea sea; + application/x-shockwave-flash swf; + application/x-stuffit sit; + application/x-tcl tcl tk; + application/x-x509-ca-cert der pem crt; + application/x-xpinstall xpi; + application/zip zip; + application/octet-stream deb; + application/octet-stream bin exe dll; + application/octet-stream dmg; + application/octet-stream eot; + application/octet-stream iso img; + application/octet-stream msi msp msm; + audio/mpeg mp3; + audio/x-realaudio ra; + video/mpeg mpeg mpg; + video/quicktime mov; + video/x-flv flv; + video/x-msvideo avi; + video/x-ms-wmv wmv; + video/x-ms-asf asx asf; + video/x-mng mng; +} +``` ++ 生产环境的完整配置nginx.conf +``` +user www www; +worker_processes 2; +pid /var/run/nginx.pid; + +# [ debug | info | notice | warn | error | crit ] +error_log /var/log/nginx.error_log info; + +events { + worker_connections 2000; + # use [ kqueue | rtsig | epoll | /dev/poll | select | poll ] ; + use kqueue; +} + +http { + include conf/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] ' + '"$request" $status $bytes_sent ' + '"$http_referer" "$http_user_agent" ' + '"$gzip_ratio"'; + + log_format download '$remote_addr - $remote_user [$time_local] ' + '"$request" $status $bytes_sent ' + '"$http_referer" "$http_user_agent" ' + '"$http_range" "$sent_http_content_range"'; + + client_header_timeout 3m; + client_body_timeout 3m; + send_timeout 3m; + + client_header_buffer_size 1k; + large_client_header_buffers 4 4k; + + gzip on; + gzip_min_length 1100; + gzip_buffers 4 8k; + gzip_types text/plain; + + output_buffers 1 32k; + postpone_output 1460; + + sendfile on; + tcp_nopush on; + + tcp_nodelay on; + send_lowat 12000; + + keepalive_timeout 75 20; + + # lingering_time 30; + # lingering_timeout 10; + # reset_timedout_connection on; + + + server { + listen one.example.com; + server_name one.example.com www.one.example.com; + + access_log /var/log/nginx.access_log main; + + location / { + proxy_pass http://127.0.0.1/; + proxy_redirect off; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + client_max_body_size 10m; + client_body_buffer_size 128k; + + client_body_temp_path /var/nginx/client_body_temp; + + proxy_connect_timeout 90; + proxy_send_timeout 90; + proxy_read_timeout 90; + proxy_send_lowat 12000; + + proxy_buffer_size 4k; + proxy_buffers 4 32k; + proxy_busy_buffers_size 64k; + proxy_temp_file_write_size 64k; + + proxy_temp_path /var/nginx/proxy_temp; + + charset koi8-r; + } + + error_page 404 /404.html; + + location /404.html { + root /spool/www; + + charset on; + source_charset koi8-r; + } + + location /old_stuff/ { + rewrite ^/old_stuff/(.*)$ /new_stuff/$1 permanent; + } + + location /download/ { + valid_referers none blocked server_names *.example.com; + + if ($invalid_referer) { + #rewrite ^/ http://www.example.com/; + return 403; + } + + # rewrite_log on; + # rewrite /download/*/mp3/*.any_ext to /download/*/mp3/*.mp3 + rewrite ^/(download/.*)/mp3/(.*)\..*$ /$1/mp3/$2.mp3 break; + + root /spool/www; + # autoindex on; + access_log /var/log/nginx-download.access_log download; + } + + location ~* ^.+\.(jpg|jpeg|gif)$ { + root /spool/www; + access_log off; + expires 30d; + } + + location ~ \.php$ { + fastcgi_pass unix:/var/run/php7.0.9-fpm.sock; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; + } + + } +} +``` + diff --git a/docs/Nginx/Nginx-Web/Nginx-2-4-log-cut.md b/docs/Nginx/Nginx-Web/Nginx-2-4-log-cut.md new file mode 100644 index 0000000..2450164 --- /dev/null +++ b/docs/Nginx/Nginx-Web/Nginx-2-4-log-cut.md @@ -0,0 +1,172 @@ +#### 脚本思路 ++ 第一步就是重命名日志文件,不用担心重命名后nginx找不到日志文件而丢失日志。在你未重新打开原名字的日志文件前,nginx还是会向你重命名的文件写日志,linux是靠文件描述符而不是文件名定位文件。 ++ 第二步向nginx主进程发送USR1信号。nginx主进程接到信号后会从配置文件中读取日志文件名称,重新打开日志文件(以配置文件中的日志名称命名),并以工作进程的用户作为日志文件的所有者。重新打开日志文件后,nginx主进程会关闭重名的日志文件并通知工作进程使用新打开的日志文件。工作进程立刻打开新的日志文件并关闭重名名的日志文件。 ++ 然后你就可以处理旧的日志文件了。 ++ [Nginx日志切割shell脚本](http://www.jb51.net/article/47884.htm) +#### 日志 +--- ++ 日志格式允许包含的变量注释 + ``` + $remote_addr, $http_x_forwarded_for(反向) --记录客户端IP地址 + $remote_user --记录客户端用户名称 + $request --记录请求的URL和HTTP协议 + $status --记录请求状态 + $body_bytes_sent --发送给客户端的字节数,不包括响应头的大小; 该变量与Apache模块mod_log_config里的“%B”参数兼容。 + $bytes_sent --发送给客户端的总字节数。 + $connection --连接的序列号。 + $connection_requests --当前通过一个连接获得的请求数量。 + $msec --日志写入时间。单位为秒,精度是毫秒。 + $pipe --如果请求是通过HTTP流水线(pipelined)发送,pipe值为“p”,否则为“.”。 + $http_referer --记录从哪个页面链接访问过来的 + $http_user_agent --记录客户端浏览器相关信息(注意:个别浏览器是空的) + $request_length --请求的长度(包括请求行,请求头和请求正文)。 + $request_time --请求处理时间,单位为秒,精度毫秒; 从读入客户端的第一个字节开始,直到把最后一个字符发送给客户端后进行日志写入为止。 + $time_iso8601 --ISO8601标准格式下的本地时间。 + $time_local --通用日志格式下的本地时间。 + ``` ++ Nginx位于负载均衡器,squid,nginx反向代理之后,web服务器无法直接获取到客户端真实的IP地址 + + $remote_addr获取反向代理的IP地址。反向代理服务器在转发请求的http头信息中,可以增加X-Forwarded-For信息,用来记录 客户端IP地址和客户端请求的服务器地址 ++ 日志文件的切割 + + 查看主进程号( master process ):`cat /var/run/nginx.pid` + + 停止 + + 配置了pid文件存放路径则,QUIT向NGINX主进程发送(优雅关机)信号的方法:`kill -QUIT $(cat /usr/local/nginx/logs/nginx.pid )` + + 从容停止: `kill -QUIT 主进程号` + + 快速停止:`kill -TERM 主进程号` + + 强制停止:`kill -9 主进程号` + + 重启 + + 验证配置文件是否正确: `/usr/local/nginx/sbin/nginx -t` + + 重启方式一:`kill -HUP 主进程号` + >[1] 配置重新加载 + >[2] 使用新配置启动新的工作进程 + >[3] 正常关闭旧的工作进程 + + + 重启方式二:`/usr/local/nginx/sbin/nginx -s reload` + > `-s ` 参数包含四个命令分别是 `stop/quit/reopen/reload` (发送信号到主进程:停止,退出,重新打开,重新加载) + + + 发送 kill -USR1 信号给Nginx 主进程号,让Nginx 生成一个新的日志文件 `/usr/local/nginx/logs/access.log` + + 单日志备份Shell 脚本 `cut_nginx_log.sh` + ``` + #!/bin/bash + # ====================================================================================== + # chmod u+x /opt/nginx/cut_nginx_log.sh + # crontab -e + # 0 0 * * * /home/tinywan/bin/cut_nginx_log.sh > /home/tinywan/bin/cut_nginx_log.log 2>&1 + # ======================================================================================= + + LOGS_PATH="/usr/local/nginx/logs" + YEAR=$(date -d "yesterday" "+%Y") + MONTH=$(date -d "yesterday" "+%m") + # 获取昨天的日期 + DATE=$(date -d "yesterday" "+%Y%m%d_%H%M%S") + echo "YEAR : ${YEAR} MONTH : ${MONTH} DATE :${DATE}" + # Nginx的master 主进程号 + NGINX_PID="/var/run/nginx.pid" + # -r 检测文件是否可读,如果是,则返回 true + if [ -r ${NGINX_PID} ]; then + mkdir -p "${LOGS_PATH}/${YEAR}/${MONTH}" + mv "${LOGS_PATH}/access.log" "${LOGS_PATH}/${YEAR}/${MONTH}/access_${DATE}.log" + kill -USR1 $(cat "/var/run/nginx.pid") + sleep 1 + gzip "${LOGS_PATH}/${YEAR}/${MONTH}/access_${DATE}.log" + echo 'Nginx Cut Log Success' + else + echo "Nginx might be down" + fi + # ============================================================================== + # Clean up log files older than 100 days + # ============================================================================== + # Change HOUSEKEEPING=1 to enable clean up + HOUSEKEEPING=0 + KEEP_DAYS=100 + if [ $HOUSEKEEPING == 1 ]; then # 删除日志开关,开关为1的时候才会去根据设置的天数删除压缩日志文件 + if [ -d "${LOGS_PATH}" ]; then + find "${LOGS_PATH}" -type f -name "*.log.gz" -mtime +${KEEP_DAYS} -exec rm -f {} \; + fi + fi + ``` + + Nginx 报错误日志,由于php-fpm没有启动,就会报错以下错误信息error.log中 + ``` + 11 connect() to unix:/var/run/php7.0.9-fpm.sock failed (2: No such file or directory) while connecting to upstream + ``` + + 多日志备份Shell 脚本 `cut_ multiple_nginx_log.sh` + ``` + #!/bin/bash + # ====================================================================================== + # chmod u+x /opt/nginx/cut_multiple_nginx_log.sh + # crontab -e + # 0 0 * * * /home/tinywan/bin/cut_multiple_nginx_log.sh > /home/tinywan/bin/cut_nginx_log.log 2>&1 + # ======================================================================================= + + LOGS_PATH="/usr/local/nginx/logs" # 注意这里在路径末尾多个"/" + YEAR=$(date -d "yesterday" "+%Y") + MONTH=$(date -d "yesterday" "+%m") + # 获取昨天的日期 + DATE=$(date -d "yesterday" "+%Y%m%d_%H%M%S") + echo "YEAR : ${YEAR} MONTH : ${MONTH} DATE :${DATE}" + # Nginx的master 主进程号 + NGINX_PID="/var/run/nginx.pid" + # -r 检测文件是否可读,如果是,则返回 true + CUT_LOG(){ + if [ -r ${NGINX_PID} ]; then + mkdir -p "${LOGS_PATH}/${YEAR}/${MONTH}" + cd ${LOGS_PATH} + for i in $(ls *.log) # i = access.log/error.log/...等等 + do + FILE_NAME=$(echo ${i} | sed 's/\.log//') # FILE_NAME=access/error/...等等 + echo ${FILE_NAME} + mv "${LOGS_PATH}/${i}" "${LOGS_PATH}/${YEAR}/${MONTH}/${FILE_NAME}_${DATE}.log" + sleep 1 + gzip "${LOGS_PATH}/${YEAR}/${MONTH}/${FILE_NAME}_${DATE}.log" + done + kill -USR1 $(cat "/var/run/nginx.pid") + echo 'Nginx Cut Log Success' + else + echo "Nginx might be down" + exit 1 + fi + } + CUT_LOG + # ============================================================================== + # Clean up log files older than 100 days + # ============================================================================== + # Change HOUSEKEEPING=1 to enable clean up + HOUSEKEEPING=1 + KEEP_DAYS=100 + if [ $HOUSEKEEPING == 1 ]; then + if [ -d "${LOGS_PATH}" ]; then + find "${LOGS_PATH}" -type f -name "*.log.gz" -mtime +${KEEP_DAYS} -exec rm -f {} \; + fi + fi + ``` ++ Linux命令之 ` find ` 命令中的 `-mtime` 参数 + + mtime参数的理解应该如下: + + -mtime n 按照文件的更改时间来找文件,n为整数。 + + n表示文件更改时间距离为n天, -n表示文件更改时间距离在n天以内,+n表示文件更改时间距离在n天以前。 + + 例如: + > -mtime 0 表示文件修改时间距离当前为0天的文件,即距离当前时间不到1天(24小时)以内的文件。 + > -mtime 1 表示文件修改时间距离当前为1天的文件,即距离当前时间1天(24小时-48小时)的文件。 + > -mtime+1 表示文件修改时间为大于1天的文件,即距离当前时间2天(48小时)之外的文件 + > -mtime -1 表示文件修改时间为小于1天的文件,即距离当前时间1天(24小时)之内的文件 + + + ` find "/usr/local/nginx/logs" -type f -name "*.log.gz" -mtime 0 ` + > 查找距离当前时间不到1天(24小时)以内的文件的日志文件 + >> 查找结果: + ``` + root@tinywan:/usr/local/nginx/logs/2017/04# find "/usr/local/nginx/logs" -type f -name "*.log.gz" -mtime 0 + /usr/local/nginx/logs/2017/04/error_20170401_224602.log.gz + /usr/local/nginx/logs/2017/04/access_20170401_224602.log.gz + /usr/local/nginx/logs/2017/04/host.access_20170401_224602.log.gz + ``` + + + 为什么-mtime+1 表示文件修改时间为大于1天的文件,即距离当前时间48小时之外的文件,而不是24小时之外的呢? + + 因为n值只能是整数,即比1大的最近的整数是2,所有-mtime+1不是比当前时间大于1天(24小时),而是比当前时间大于2天(48小时)。 + + ` find . -name "*ab*" -exec rm -f {}\; ` + > 整句命令表示:在当前目录下查找以ab结尾的文件,并删除 + > ` -name “*ab” ` 表示查找以ab结尾的文件或文件名 + > `-exec` 表示执行什么命令。后面跟要执行的命令。此处是 `rm -f`,表示不确认删除 + > `{} \;` 表示把查找到的结果发送到此来 + + + + + diff --git a/docs/Nginx/Nginx-Web/Nginx-2-Log.md b/docs/Nginx/Nginx-Web/Nginx-2-Log.md new file mode 100644 index 0000000..b8a723c --- /dev/null +++ b/docs/Nginx/Nginx-Web/Nginx-2-Log.md @@ -0,0 +1,49 @@ + +#### Nginx日志服务 +--- + +* Nginx日志主要分为两种:访问日志和错误日志。日志开关在Nginx配置文件(/etc/nginx/nginx.conf)中设置,两种日志都可以选择性关闭,默认都是打开的 + +**一、访问日志** + +* 访问日志主要记录客户端访问Nginx的每一个请求,格式可以自定义。通过访问日志,你可以得到用户地域来源、跳转来源、使用终端、某个URL访问量等相关信息。Nginx中访问日志相关指令主要有两条 + +> 1.log_format + +| 字段 | 作用 | 案例 | +| :------------ |:---------------:| -----:| +| $server_name | 虚拟主机名称 | 虚拟主机名称 | +| $remote_addr | 记录客户端IP地址 | 218.108.35.150 | +| $remote_user | 远程客户端用户名称 | 如登录百度的用户名scq2099yt,如果没有登录就是空白 | +| $time_local | 访问的时间与时区 | 16/Mar/2017:17:50:52 +0800 时间信息最后的"+0800"表示服务器所处时区位于UTC之后的8小时 | +| $request | 请求的URI和HTTP协议| 这是整个PV日志记录中最有用的信息,记录服务器收到一个什么样的请求 | +| $status | 记录请求返回的http状态码 | 比如成功是200 | +| $uptream_status| upstream状态 | 比如成功是200 | +| $body_bytes_sent | 发送给客户端的文件主体内容的大小 | 比如899,可以将日志每条记录中的这个值累加起来以粗略估计服务器吞吐量 | +| $http_referer | 记录从哪个页面链接访问过来的 | ... | +| $http_user_agent | 记录客户端浏览器信息 | ... | +| $http_x_forwarded_for | 客户端的真实ip| 通常web服务器放在反向代理的后面,这样就不能获取到客户的IP地址了,通过$remote_add拿到的IP地址是反向代理服务器的iP地址。反向代理服务器在转发请求的http头信息中,可以增加x_forwarded_for信息,用以记录原有客户端的IP地址和原来客户端的请求的服务器地址。 | +| $ssl_protocol | SSL协议版本 | 比如TLSv1| +| $ssl_cipher | 交换数据中的算法 | 比如RC4-SHA| +| $upstream_addr | upstream的地址 | 即真正提供服务的主机地址| +| $request_time | 整个请求的总时间 | ...| +| $upstream_response_time | 请求过程中,upstream的响应时间 | 0.1s| + +> 2.access_log +>> 格式:`access_log path(存放路径) [format(自定义日志格式名称) [buffer=size | off]]` +>> 案例:`access_log logs/access.log main;` +>> 上下文:`http、server、location` +>> 注意要点:`Nginx进程设置的用户和组必须对日志路径有创建文件的权限,否则,会报错` + + +**二、错误日志** + +* 错误日志主要记录客户端访问Nginx出错时的日志,格式不支持自定义。通过错误日志,你可以得到系统某个服务或server的性能瓶颈等。因此,将日志好好利用,你可以得到很多有价值的信息。错误日志由指令error_log来指定` + +>> 格式:`error_log path(存放路径) level(日志等级)` +>> 日志等级:`[ debug | info | notice | warn | error | crit ]`,从左至右,日志详细程度逐级递减,即debug最详细,crit最少 +>> 案例:`error_log logs/error.log info;` +>> 上下文:`http、server、location` +>> 注意要点:`error_log off并不能关闭错误日志,而是会将错误日志记录到一个文件名为off的文件中` +>> 正确的关闭错误日志记录功能:`error_log /dev/null;`,上面表示将存储日志的路径设置为“垃圾桶” + diff --git a/docs/Nginx/Nginx-Web/Nginx-6-ReWrite-1.md b/docs/Nginx/Nginx-Web/Nginx-6-ReWrite-1.md new file mode 100644 index 0000000..03872a8 --- /dev/null +++ b/docs/Nginx/Nginx-Web/Nginx-6-ReWrite-1.md @@ -0,0 +1,78 @@ + +#### rewrite 重写 +--- + ++ 重写中用到的指令 + + if (条件) {} 设定条件,再进行重写 + + If 语法格式 + ``` + If 空格 (条件) { + 重写模式 + } + ``` + + 配置案例一:禁止某一个IP地址访问 + ``` + location / { + if ( $remote_addr = 192.168.127.129 ){ # 注意:这里的if和()之间是有个空格的 + return 403; + } + root html; + } + ``` + + 配置案例二:正则表达式的用法 + ``` + # 这个没有添加break 则会一直循环重定向,服务器会相应 500 + rewrite_log on; + if ($http_user_agent ~ Mozilla){ + rewrite ^.*$ /ie.html; + } + # nginx 日志记录():rewrite or internal redirection cycle while processing "/404.html", + # 这里要开启重写日志:rewrite_log on + + # 正确配置信息 ,服务器会输出ie.html 中的内容 + rewrite_log on; + if ($http_user_agent ~ Mozilla){ + rewrite ^.*$ /ie.html; + break; + } + ``` + + + set #设置变量 + + return #返回状态码 + + break #跳出rewrite + + rewrite #重写 + ++ Nginx 全局应用的变量文件路径:root@tinywan:/usr/local/nginx/conf# cat fastcgi.conf + ``` + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; -- 脚本文件请求的路径 + fastcgi_param QUERY_STRING $query_string; -- 请求的参数;如?app=123 + fastcgi_param REQUEST_METHOD $request_method; -- 请求的方法(GET,POST) + fastcgi_param CONTENT_TYPE $content_type; -- 请求头中的Content-Type字段 + fastcgi_param CONTENT_LENGTH $content_length; -- 请求头中的Content-length字段 + + fastcgi_param SCRIPT_NAME $fastcgi_script_name; -- 脚本名称 + fastcgi_param REQUEST_URI $request_uri; -- 请求的地址不带参数 + fastcgi_param DOCUMENT_URI $document_uri; -- 与$uri相同 + fastcgi_param DOCUMENT_ROOT $document_root; -- 网站的根目录。在server配置中root指令中指定的值 + fastcgi_param SERVER_PROTOCOL $server_protocol; -- 请求使用的协议,通常是HTTP/1.0或HTTP/1.1 + fastcgi_param REQUEST_SCHEME $scheme; -- + fastcgi_param HTTPS $https if_not_empty; -- + + fastcgi_param GATEWAY_INTERFACE CGI/1.1; + fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; + + fastcgi_param REMOTE_ADDR $remote_addr; -- 客户端IP + fastcgi_param REMOTE_PORT $remote_port; -- 客户端端口 + fastcgi_param SERVER_ADDR $server_addr; -- 服务器IP地址 + fastcgi_param SERVER_PORT $server_port; -- 服务器端口 + fastcgi_param SERVER_NAME $server_name; -- 服务器名,域名在server配置中指定的server_name + + PHP : fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + ``` + +**二、错误日志** + +* 错误日志主要记录客户端访问Nginx出错时的日志,格式不支持自定义。通过错误日志,你可以得到系统某个服务或server的性能瓶颈等。因此,将日志好好利用,你可以得到很多有价值的信息。错误日志由指令error_log来指定` + + + diff --git a/docs/Nginx/Nginx-Web/Nginx-7-Proxy-1.md b/docs/Nginx/Nginx-Web/Nginx-7-Proxy-1.md new file mode 100644 index 0000000..6099aee --- /dev/null +++ b/docs/Nginx/Nginx-Web/Nginx-7-Proxy-1.md @@ -0,0 +1,20 @@ + +#### 简单的负载平衡 +``` +http { +upstream myproject { + server 127.0.0.1:8000 weight=3; + server 127.0.0.1:8001; + server 127.0.0.1:8002; + server 127.0.0.1:8003; +} + +server { + listen 80; + server_name www.domain.com; + location / { + proxy_pass http://myproject; + } +} +} +``` diff --git a/docs/Nginx/Nginx-Web/Nginx-7-Proxy.md b/docs/Nginx/Nginx-Web/Nginx-7-Proxy.md new file mode 100644 index 0000000..69a0964 --- /dev/null +++ b/docs/Nginx/Nginx-Web/Nginx-7-Proxy.md @@ -0,0 +1,262 @@ + +#### Nginx服务器的HTTP代理服务 +--- ++ 网络初始化之listen常见配置 + ```Lua + listen 127.0.0.1:8000; + listen 127.0.0.1; + listen 8000; + listen *:8000; + listen localhost:8000; + listen [::]:8000; + listen [fe80::1]; + listen unix:/var/run/nginx.sock; + ``` +* **配置实例一:对所有请求实现一般轮询规则的负载均衡** + ``` + http { + + upstream live_node { # 配置后端服务器组 + #max_fails默认值为1,fail_timeout默认值为10s,max_fails=0表示不做检查 + server 127.0.0.1:8089 weight=1 max_fails=1 fail_timeout=10s; + server 127.0.0.1:8088; + keepalive 32; + hash $request_uri consistent; + } + + server { + listen 80; + server_name localhost; + location / { + proxy_pass http://live_node; # 注意:proxy_pass后面的路径不带uri时,其会将location的uri传递给后端主机 + proxy_set_header Host $host; # 保留客户端的真实信息 + proxy_set_header Host $host:$server_port; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Real-PORT $remote_port; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + proxy_redirect off; + proxy_buffer_size 128k; + proxy_buffers 32 32k; + proxy_busy_buffers_size 128k; + } + } + + server { # 配置虚拟服务器8088 + listen 8088; + server_name localhost; + location / { + root /usr/local/nginx/html2; + index index.html index.htm; + } + } + + server { # 配置虚拟服务器8089 + listen 8089; + server_name localhost; + location / { + root /usr/local/nginx/html3; + index index.html index.htm; + } + } + } + ``` + + Nginx-proxy 详解文章链接 + + 查看错误日志 + `upstream sent too big header while reading response header from upstream` + + [Nginx-proxy_buffer_size and fastcgi_buffer](http://blog.csdn.net/u010391029/article/details/50850210) + + [http://wiki.nginx.org/NginxHttpProxyModule](http://wiki.nginx.org/NginxHttpProxyModule) + + [http://blog.sina.com.cn/s/blog_5dc960cd0100i4mt.html](http://blog.sina.com.cn/s/blog_5dc960cd0100i4mt.html) + > 参数:`keepalive connections;` + >>补充:`由于短连接消耗前端代理服务器的资源现象严重,因此会将一部分连接定义为长连接以节省资源` + >>FUN:`#为每个worker进程保留的空闲的长连接数量` + >>FUN:`#定义nginx与后端服务器的保持连接的数量` + + > 参数:`hash $request_uri consistent;` + >>FUN:`#[consistent]; 使用一致性哈希算法, 建议开启此项` + >>FUN:`#基于指定的key的hash表来实现对请求的调度,此处的key可以直接文本、变量或二者的组合;` + >>FUN:`#将请求分类,同一类请求将发往同一个upstream server;` + +* **配置实例二:对所有请求实现加权轮询规则负载均衡** + ``` + http { + + upstream live_node { # 配置后端服务器组 + server 127.0.0.1:8089 weight=5; # 这个处理客户端请求会多些 + server 127.0.0.1:8088 weight=1; # 默认 weight = 1 + } + + server { + listen 80; + server_name localhost; + location / { + #proxy_pass http://new_uri/; # 注意:proxy_pass后面的路径是一个uri时,其会将location的uri替换为proxy_pass的uri + proxy_pass http://live_node; + proxy_set_header Host $host; # 保留客户端的真实信息 + } + } + + server { # 配置虚拟服务器8088 + listen 8088; + server_name localhost; + location / { + root /usr/local/nginx/html2; + index index.html index.htm; + } + } + + server { # 配置虚拟服务器8089 + listen 8089; + server_name localhost; + location / { + root /usr/local/nginx/html3; + index index.html index.htm; + } + } + } + ``` + +* **配置实例三:对特定资源实现负载均衡** + ``` + http { + + upstream videobackend { # 配置后端服务器组视频代理 + server 127.0.0.1:8088; + server 127.0.0.1:8089; + } + + upstream filebackend { # 配置后端服务器组文件代理 + server 127.0.0.1:8888; + server 127.0.0.1:8889; + } + + server { + listen 80; + server_name localhost; + location /video/ { + proxy_pass http://videobackend; # 视频代理 + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; # proxy_set_header field value; 设定发往后端主机的请求报文的请求首部的值 + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + location /file/ { + proxy_pass http://filebackend; # 文件代理 + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + } + + server { # 配置虚拟服务器8088 + listen 8088; + server_name localhost; + location /video { + alias /usr/local/nginx/html2; + } + } + + server { # 配置虚拟服务器8089 + listen 8089; + server_name localhost; + location /video { + alias /usr/local/nginx/html3; + } + } + + server { # 文件虚拟服务器1 + listen 8888; + server_name localhost; + location /file { + alias /usr/local/nginx/html4; + } + } + + server { # 文件虚拟服务器2 + listen 8889; + server_name localhost; + location /file { + alias /usr/local/nginx/html5; + } + } + } + ``` + > 访问方式:`http://127.0.0.1/video/demo.txt` + >>输出:`this is video HTML2 demo2 8088` + >>输出:`this is video HTML3 demo3 8089` + + > 访问方式:`http://127.0.0.1/file/demo.txt` + >>输出:`this is file HTML4 demo4 8888` + >>输出:`this is file HTML4 demo5 8889` + + >测数文件:`demo.txt` + >>`echo "this is video HTML2 demo2 8088" > ./html2/demo.txt` + +* **配置实例四:不同的域名实现负载均衡** + ``` + http { + + upstream frontend { # 配置后端服务器组视频代理 + server 127.0.0.1:8088; + server 127.0.0.1:8089; + } + + upstream backend { # 配置后端服务器组文件代理 + server 127.0.0.1:8888; + server 127.0.0.1:8889; + } + + server { + listen 80; + server_name www.frontend.com; + location /video/ { + proxy_pass http://frontend; # 前台域名代理 + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + } + + server { + listen 8088; + server_name www.backend.com; + location /video/ { + proxy_pass http://backend; # 后台域名代理 + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + } + + } + ``` +* **配置实例五:实现带有URL重写的负载均衡** + ``` + http { + + upstream backend { # 配置后端服务器组 + server 127.0.0.1:8888; + server 127.0.0.1:8889; + } + + server { + listen 80; + server_name www.backend.com; + index index.html index.htm; + location /file/ { + rewrite ^(/file/.*)/media/(.*)\.*$ $1/mp3/$2.mp3 last; + } + + location / { + proxy_pass http://frontend; # 前台域名代理 + proxy_set_header Host $host; + } + } + + } + ``` + + >客户端请求URL为:`http://www.backend.com/file/download/media/1.mp3` + >[1]:虚拟主机` location /file/ `块将该URL进行重写为:`http://www.backend.com/file/download/media/mp3/1.mp3` + >[2]:新的URL再有 ` location / `块转发转发到后端的backend服务器组中实现负载均衡 + >[3]: 这样就可以实现URL重写的负载均衡 diff --git a/docs/Nginx/Nginx-Web/Nginx-8-proxy_cache.md b/docs/Nginx/Nginx-Web/Nginx-8-proxy_cache.md new file mode 100644 index 0000000..c106db4 --- /dev/null +++ b/docs/Nginx/Nginx-Web/Nginx-8-proxy_cache.md @@ -0,0 +1,187 @@ +## 如何配置proxy_cache模块 ++ [官方:ngx_http_proxy_module](http://nginx.org/en/docs/http/ngx_http_proxy_module.html) ++ `Http`配置文件 + ```bash + user www; + worker_processes 1; + + error_log logs/error.log error; + + pid /run/nginx.pid; + + worker_rlimit_nofile 204800; + + events { + worker_connections 65535; + multi_accept on; + use epoll; + } + + http { + lua_package_path '/usr/local/openresty/lualib/?.lua;/usr/local/openresty/nginx/conf/waf/?.lua;'; + lua_package_cpath '/usr/local/openresty/lualib/?.so;;'; + + init_by_lua_file "/usr/local/openresty/nginx/conf/waf/init.lua"; + access_by_lua_file "/usr/local/openresty/nginx/conf/waf/waf.lua"; + + include mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"' + '"$upstream_cache_status"'; # nginx cache命中率统计 + + charset UTF-8; + client_header_buffer_size 32k; + large_client_header_buffers 4 32k; + + client_header_timeout 100; + client_body_timeout 100; + client_max_body_size 800m; + client_body_buffer_size 512k; + reset_timedout_connection on; + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + + keepalive_timeout 75 20; + + proxy_connect_timeout 5; + proxy_send_timeout 5; + proxy_read_timeout 60; + # 是否启用或者关闭 proxy_buffer,默认为 on + proxy_buffering on; + # 设置缓存大小,默认4KB、8KB 保持与 proxy_buffers 指令中size变量相同或者更小 + proxy_buffer_size 16k; + # proxy_buffer个数和Buffer大小(一般设置为内存页大小) + proxy_buffers 4 64k; + # 限制处于 BUSY 状态的 proxy_buffer 的总大小 + proxy_busy_buffers_size 128k; + # 所有临时文件总体积大小,磁盘上的临时文件不能超过该配置 + proxy_max_temp_file_size 500MB; + # 配置同时写入临时文件的数据量的总大小 + proxy_temp_file_write_size 128k; + + gzip on; + gzip_min_length 1k; + gzip_buffers 4 64k; + gzip_http_version 1.1; + gzip_comp_level 6; + gzip_types text/plain application/x-javascript text/css application/javascript text/javascript image/jpeg image/gif image/png application/xml application/json; + gzip_vary on; + gzip_disable "MSIE [1-6].(?!.*SV1)"; + + # 文件路径,临时存放代理服务器的大体积响应数据 + proxy_temp_path /home/www/data/nginx/tmp-test; + # 设置WEB缓存区名称为 cache_one ,内存缓存空间大小为100M,一天清理一次,硬盘缓存空间大小为10G + proxy_cache_path /home/www/data/nginx/cache-test levels=1:2 keys_zone=cache_one:100m inactive=1d max_size=10g; + + index index.php index.html index.htm; + include "/usr/local/openresty/nginx/conf/vhost/*.conf"; + } + ``` ++ `Server`配置文件 + + ```bash + server { + listen 8087; + server_name localhost; + + location / { + # 如果后端的服务器返回500 502 503 504 执行超时等错误,将请求转发到另外一台服务器 + proxy_next_upstream http_500 http_502 http_503 http_504 error timeout invalid_header; + # 定义用于缓存的共享内存区域 + proxy_cache cache_one; + # 针对不同的HTTP状态码设置不同的缓存时间 + proxy_cache_valid 200 304 1h; + proxy_cache_valid 404 1m; + # WEB缓存的Key值域名、URI、参数组成 + proxy_cache_key $host$uri$is_args$args; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + # 显示缓存的状态 + add_header Nginx-Cache "$upstream_cache_status"; + # 可以禁用一个或多个响应头字段的处理 [Nginx不缓存,可以添加以下语句] + proxy_ignore_headers X-Accel-Expires Expires Cache-Control Set-Cookie; + proxy_pass http://www.tinywan.com; + expires 1d; + } + } + ``` ++ 配置测试 + + 打印响应头 + ```bash + www@TinywanAliYun:$ curl -I http://127.0.0.1:8087/ + HTTP/1.1 200 OK + Server: openresty/1.11.2.5 + Date: Sat, 18 Nov 2017 15:46:55 GMT + Content-Type: text/html; charset=utf-8 + Connection: keep-alive + Keep-Alive: timeout=20 + Vary: Accept-Encoding + Expires: Sun, 19 Nov 2017 15:46:55 GMT + Cache-Control: max-age=86400 + Pragma: no-cache + Nginx-Cache: HIT + ``` + + 缓存目录 + ```bash + www@TinywanAliYun:~/data/nginx/cache-test$ tree -L 4 + . + ├── 0 + │   └── 49 + │   └── 51ab3cb31fd7929a0346796693d53490 + └── 9 + └── f4 + └── 3a020dc16513d3abee9ba74688d53f49 + + 4 directories, 2 files + www@TinywanAliYun:~/data/nginx/cache-test$ cat 0/49/51ab3cb31fd7929a0346796693d53490 + ³QZݵǘ£CZC°(±"58c8b5dd-423e" + KEY: 127.0.0.1/favicon.ico // 代理服务IP地址 + HTTP/1.1 200 OK + Server: nginx/1.6.0 // 被代理服务器信息 + Date: Sat, 18 Nov 2017 14:28:51 GMT + Content-Type: image/x-icon + Content-Length: 16958 + Last-Modified: Wed, 15 Mar 2017 03:32:45 GMT + Connection: close + ETag: "58c8b5dd-423e" + Accept-Ranges: bytes + ``` ++ `$upstream_cache_status` 包含以下几种状态 + + ```bash + ·MISS 未命中,请求被传送到后端 + ·HIT 缓存命中 + ·EXPIRED 缓存已经过期请求被传送到后端 + ·UPDATING 正在更新缓存,将使用旧的应答 + ·STALE 后端将得到过期的应答 + ``` ++ `nginx cache`命中率统计 + + ```bash + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"' + '"$upstream_cache_status"'; + ``` + > 命中率统计方法:用HIT的数量除以日志总量得出缓存命中率 + + > `awk '{if($NF==""HIT"") hit++} END {printf "%.2f%",hit/NR}' access.log` + ++ 通过crontab脚本将每天的命中率统计到一个日志中,以备查看 + + ```bash + #!/bin/bash + LOG_FILE='/usr/local/nginx/logs/access.log.1' + LAST_DAY=$(date +%F -d "-1 day") + awk '{if($NF==""HIT"") hit++} END {printf "'$LAST_DAY': %d %d %.2f%n", hit,NR,hit/NR}' $LOG_FILE + ``` ++ 帮助文档 + + [Nginx proxy_cache 缓存配置](http://blog.csdn.net/dengjiexian123/article/details/53386586) + + [Nginx Proxy Cache原理和最佳实践](http://www.jianshu.com/p/625c2b15dad5) + + [nginx缓存设置proxy_cache(PHP)](https://www.cnblogs.com/zlingh/p/5879988.html) \ No newline at end of file diff --git a/docs/Nginx/Nginx-Web/Nginx-8-tcp-Proxy.md b/docs/Nginx/Nginx-Web/Nginx-8-tcp-Proxy.md new file mode 100644 index 0000000..7bffc9a --- /dev/null +++ b/docs/Nginx/Nginx-Web/Nginx-8-tcp-Proxy.md @@ -0,0 +1,20 @@ + +#### Nginx服务器TCP代理服务 +--- +* **Nginx 官方自带配置** + ``` + stream { + upstream rtmp { + server 127.0.0.1:8089; # 这里配置成要访问的地址 + server 127.0.0.2:1935; + server 127.0.0.3:1935; #需要代理的端口,在这里我代理一一个RTMP模块的接口1935 + } + server { + listen 1935; # 需要监听的端口 + proxy_timeout 20s; + proxy_pass rtmp; + } + } + ``` ++ [参考博客地址](http://www.cnblogs.com/tinywan/p/6560889.html) + diff --git a/docs/Nginx/Nginx-Web/openresty-nginx-lua-Proxy.md b/docs/Nginx/Nginx-Web/openresty-nginx-lua-Proxy.md new file mode 100644 index 0000000..67fef7d --- /dev/null +++ b/docs/Nginx/Nginx-Web/openresty-nginx-lua-Proxy.md @@ -0,0 +1,345 @@ +### Openresty-Lua动态修改upstream后端服务 ++ nginx.conf 配置文件 + ``` + worker_processes 1; + pid logs/nginx.pid; + events { + worker_connections 1024; + } + + http { + include mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + sendfile on; + + keepalive_timeout 65; + + lua_shared_dict upstreams 1m; # 声明一个ngx多进程全局共享内存区域,_G 作为基于shm的Lua字典的存储空间ngx.shared. + upstream default_upstream { # 配置后端服务器组 + server 127.0.0.1:8081; + server 127.0.0.1:8082; + } + + upstream lua_upstream { # 配置后端服务器组 + server 127.0.0.1:8084; + server 127.0.0.1:8083; + } + + server { + listen 80; + server_name localhost; + + access_log logs/80.access.log main; + error_log logs/80.error.log error; + + location = /_switch_upstream { + content_by_lua_block{ + local ups = ngx.req.get_uri_args()["upstream"] + if ups == nil or ups == "" then + ngx.say("upstream is nil 1") + return nil + end + local host = ngx.var.http_host + local upstreams = ngx.shared.upstreams + local ups_src = upstreams:get(host) + ngx.say("Current upstream is :",ups_src) + ngx.log(ngx.WARN, host, " change upstream from ", ups_src, " to ", ups) + local succ, err, forcible = upstreams:set(host, ups) + ngx.say(host, " change upstream from ", ups_src, " to ", ups) + } + } + + location / { + set_by_lua_block $my_upstream { + local ups = ngx.shared.upstreams:get(ngx.var.http_host) + if ups ~= nil then + ngx.log(ngx.ERR, "get [", ups,"] from ngx.shared") + return ups + end + return "default_upstream" + } + + proxy_next_upstream off; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $host; + proxy_http_version 1.1; + proxy_set_header Connection ""; + proxy_pass http://$my_upstream ; + } + } + + server { + listen 8081; + server_name localhost; + + location / { + root html81; + index index.html index.htm; + } + } + + server { + listen 8082; + server_name localhost; + + location / { + root html82; + index index.html index.htm; + } + } + + server { + listen 8083; + server_name localhost; + + location / { + root html83; + index index.html index.htm; + } + } + + server { + listen 8084; + server_name localhost; + + location / { + root html84; + index index.html index.htm; + } + } + } + ``` ++ 4个端口分别对应4个html 根目录 + + html81/index.html 内容 `server name 8081` + + html82/index.html 内容 `server name 8082` + + html83/index.html 内容 `server name 8083` + + html84/index.html 内容 `server name 8084` ++ 如何切换后端upstream + + `default_upstream` 切换到 `lua_upstream` + ``` + root@tinywan:# curl http://127.0.0.1/_switch_upstream?upstream=lua_upstream + 127.0.0.1 change upstream from default_upstream to lua_upstream + ``` + + `lua_upstream` 切换(还原`default_upstream`)到 `default_upstream` + ``` + root@tinywan:# curl http://127.0.0.1/_switch_upstream?upstream=default_upstream + 127.0.0.1 change upstream from lua_upstream to default_upstream + ``` ++ 一个收集upstream_response_time的平均数据的例子 + ``` + lua_shared_dict log_dict 5M # 声明一个ngx多进程全局共享内存区域 + + server{ + location / { + proxy_pass http;//mybackend # 代理模块 + log_by_lua ' + local log_dict = ngx.shared.log_dict + local upstream_time = tonumber(ngx.var.upstream_response_time) + local sum = log_dict:get("upstream_time-sum") or 0 + sum = sum + upstream_time + log_dict:set("upsteam_time-sum", sum) + local newval, err = log_dict:incr("upstream_time-nb", 1) + if not newval and err == "not found" then + log_dict:add("upstream_time-nb", 0) + log_dict:incr("upstream_time-nb", 1) + end + ' + } + location = /status { + content_by_lua ' + local log_dict = ngx.shared.log_dict + local sum = log_dict:get("upstream_time-sum") + local nb = log_dict:get("upstream_time-nb") + + if nb and sum then + ngx.say("average upstream response time: ", sum/nb, " (", nb, " reqs)") + else + ngx.say("no data yet") + end + ' + } + } + ``` ++ 动态修改upstream后端服务和upstream_response_time的平均数据 合并后的代码 + ``` + #user nobody; + worker_processes 1; + pid logs/nginx.pid; + events { + worker_connections 1024; + } + + http { + include mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + sendfile on; + + keepalive_timeout 65; + + lua_shared_dict upstreams 1m; # ngx多进程全局共享内存,保存upstream值 + upstream default_upstream { # 配置后端服务器组 + server 127.0.0.1:8081; + server 127.0.0.1:8082; + } + + upstream lua_upstream { # 配置后端服务器组 + server 127.0.0.1:8084; + server 127.0.0.1:8083; + } + + + server { + listen 80; + server_name localhost; + + access_log logs/80.access.log main; + error_log logs/80.error.log error; + + location = /_switch_upstream { + default_type 'text/html'; + content_by_lua_block{ + local ups = ngx.req.get_uri_args()["upstream"] + if ups == nil or ups == "" then + ngx.say("upstream is nil 1") + return nil + end + local host = ngx.var.http_host + local upstreams = ngx.shared.upstreams + local ups_src = upstreams:get(host) + ngx.say("Current upstream is :",ups_src) + ngx.log(ngx.WARN, host, " change upstream from ", ups_src, " to ", ups) + local succ, err, forcible = upstreams:set(host, ups) + ngx.say(host, " change upstream from ", ups_src, " to ", ups) + } + } + + #location ~ (^/api/|^/p/|^/m/|^/oauthapi/) { + location / { + set_by_lua_block $my_upstream { + local ups = ngx.shared.upstreams:get(ngx.var.http_host) + if ups ~= nil then + ngx.log(ngx.ERR, "get [", ups,"] from ngx.shared") + return ups + end + return "default_upstream" + } + + proxy_next_upstream off; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $host; + proxy_http_version 1.1; + proxy_set_header Connection ""; + proxy_pass http://$my_upstream ; + + log_by_lua_block { + local log_dict = ngx.shared.upstreams + local upstream_time = tonumber(ngx.var.upstream_response_time) + + local sum = log_dict:get("upstream_time-sum") or 0 + sum = sum + upstream_time + log_dict:set("upstream_time-sum", sum) + + local newval, err = log_dict:incr("upstream_time-nb", 1) + if not newval and err == "not found" then + log_dict:add("upstream_time-nb", 0) + log_dict:incr("upstream_time-nb", 1) + end + } + } + + location = /status { + default_type 'text/html'; -- 通过浏览器可访问 + content_by_lua_block { + local log_dict = ngx.shared.upstreams + local sum = log_dict:get("upstream_time-sum") + local nb = log_dict:get("upstream_time-nb") + + if nb and sum then + ngx.say("average upstream response time: ", sum / nb, + " (", nb, " reqs)") + else + ngx.say("no data yet") + end + } + } + + } + + server { + listen 8099; + server_name localhost; + + #charset koi8-r; + + access_log logs/80.access.log main; + error_log logs/80.error.log error; + location / { + #proxy_pass http://live_node; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root html; + } + } + + server { + listen 8081; + server_name localhost; + + location / { + root html81; + index index.html index.htm; + } + } + + server { + listen 8082; + server_name localhost; + + location / { + root html82; + index index.html index.htm; + } + } + + server { + listen 8083; + server_name localhost; + + location / { + root html83; + index index.html index.htm; + } + } + + server { + listen 8084; + server_name localhost; + + location / { + root html84; + index index.html index.htm; + } + } + } + ``` ++ 测试结果: + ``` + root@tinywan:/opt/openresty/nginx/conf# curl http://127.0.0.1/status + average upstream response time: 0.0003953488372093 (129 reqs) + ``` ++ 浏览器测试,记得加上这个`default_type 'text/html';`,在浏览器地址栏直接输入即可访问 + `http://127.0.0.1/_switch_upstream?upstream=default_upstream` + ++ HELP:[http://chattool.sinaapp.com/?p=2372](http://chattool.sinaapp.com/?p=2372) \ No newline at end of file diff --git a/docs/Nginx/location-detail.md b/docs/Nginx/location-detail.md new file mode 100644 index 0000000..38357ba --- /dev/null +++ b/docs/Nginx/location-detail.md @@ -0,0 +1,165 @@ +## location 详解 + +### 匹配顺序 + +* location 的匹配顺序其实是`先匹配普通,再匹配正则` + +* 正则匹配会覆盖普通匹配(实际的规则,比这复杂) + +### 执行顺序 + +* `普通 location`的匹配规则是“最大前缀”,`普通 location`与 location 编辑顺序无关。 + +* `正则 location `的匹配规则是`顺序匹配,且只要匹配到第一个就停止后面的匹配` + +* 两种情况下,不需要继续匹配正则 location + + * 当普通 location 前面指定了`^~ `,特别告诉 Nginx 本条普通 location 一旦匹配上,则不需要继续正则匹配 + + * 当普通location 恰好严格匹配上,不是最大前缀匹配,则不再继续匹配正则。 + +* 正则 location 匹配让步普通 location 的严格精确匹配结果,但覆盖普通 location 的最大前缀匹配结果 + +### 官方文档 + +* 语法 + ```bash + location + syntax: location [=|~|~*|^~|@] /uri/ { … } + default: no + context: server + ``` +* 正则location `(location using regular expressions)` + + * `~` 和 `~*` 前缀表示正则location + + * `~` 区分大小写 + + * `~*` 不区分大小写 + +* 普通location `(location using literal strings)` + + * 无任何前缀的都属于普通 location + + * 其他前缀(包括:`=`、`^~` 和 `@`)也属于普通匹配 + +* 对于特定的HTTP请求`(a particular query)`,`nginx`应该匹配哪个`location`块的指令 + + > 原文:`To determine which location directive matches a particular query, the literal strings are checked first.` 先普通location再正则location + +* `普通location` 与 `普通location` 是如何匹配的? + + * 匹配规则1:先匹配普通location,再匹配正则location + + * 匹配规则2:最大前缀匹配 + + * 匹配规则3:匹配`URI`的前缀部分`(match the beginning portion of the query)` + + * 匹配规则4:最具体的匹配将被使用`(the most specific match will be used)`,因为 location 不是`严格匹配`,而是`前缀匹配`,就会产生一个HTTP 请求,可以`前缀匹配`到多个普通location + + * 案例 + + * 列如:`location /prefix/mid/ {} `和`location /prefix/ {}` + + * 于HTTP 请求`/prefix/mid/t.html`,前缀匹配的话两个`location`都满足,该匹配哪个? + + * 匹配原则:最具体匹配原则`the most specific match` + + * 最后的匹配是:`location /prefix/mid/ {}` + +* `正则location` 与 `正则location` 是如何匹配的? + + * 匹配规则是:按照 **正则location** 在配置文件中的物理顺序(编辑顺序)匹配 + + * 注意1:`location` 并不是一定跟顺序无关,只是**普通 location**与顺序无关,**正则 location**还是与顺序有关的 + +* `正则location`和`普通location`最大匹配如何匹配? + + * 正则:**只要匹配到一条正则location ,就不再考虑后面匹配** + + * 普通:**选择出“普通location”的最大前缀匹配结果后,还需要继续搜索正则location** + +* `(普通)`最大前缀匹配结果与继续搜索的`正则location`匹配结果的决策关系 + + * 原文:`If no regular expression matches are found, the result from the literal string search is used.` 如果找不到正则表达式匹配,则使用文字字符串搜索的结果 + + * 匹配一:如果继续搜索的`正则location`也有匹配上的,那么`正则location`覆盖`普通location`的最大前缀匹配 + + * ~~因为有这个覆盖关系,所以造成有些同学以为正则location 先于普通location 执行的错误理解~~ + + * 但是如果“正则location ”没有能匹配上,那么就用“普通location ”的最大前缀匹配结果 + +* 一般匹配原则 + + * 匹配完了`普通location`指令,还需要继续匹配`正则location` + + * 也可以告诉Nginx:匹配到了`普通location`后,不再需要继续匹配`正则location`了, + >1.要做到这一点只要在`普通location`前面加上`^~`符号 + >2.`^` 表示`非`,`~` 表示`正则`,字符意思是:`不要继续匹配正则` + + * `^~`和`=` 区别 + + * 共同点:都能阻止继续搜索`正则location` + + * 不同点: + + >1.`^~`依然遵守`最大前缀`匹配规则 + >2.`=`不是`最大前缀`,而是必须是严格匹配(exact match ) + +#### `location / {} `和`location = / {}`的区别 + +* `location / {}` + + * 遵守的是:`普通location 的最大前缀匹配` + + * 由于任何`URI`都必然以`/ `根开头,所以对于一个`URI`,如果有更具体(specific)的匹配,那自然是选这个更具体的匹配了,如果没有,`/`一定能为这个`URI`垫背(至少能匹配到`/`),可以说:`location / {}`有点像默认配置,其他更具体(specific)的配置能覆盖(overwrite)这个默认配置(这也是为什么总能看到`location / {}`这个配置的一个很重要的原因) + +* `location = / {}` + + * 遵守的是:`严格精确匹配exact match` + + * 只能匹配`http://host:port/`请求,同时会禁止继续搜索`正则location` + + * 因此如果我们只想对`GET / `请求配置作用指令,那么我们可以选`location = / {} `这样能减少正则location 的搜索,因此效率比`location / {}` 高 + + > 注:前提是我们的目的仅仅只想对“GET / ”起作用 + +#### 精确匹配 + +* `精确匹配exact match `,即使`普通location`没有带`=`或`^~`前缀,也一样会终止后面的匹配 + +* 原文:`On exact match with literal location without “=” or “^~” prefixes search is also immediately terminated` + +* 当`最大前缀`匹配恰好就是一个`严格精确(exact match )`匹配,照样会停止后面的搜索 + +* 案例: + + * 假设当前配置是: + + * `location /exact/match/test.html { 配置指令块1}` + + * `location /prefix/ { 配置指令块2}` + + * `location ~ \.html$ { 配置指令块3}` + + * 请求URI:`GET /prefix/index.html` + + > 则会被匹配到:`配置指令块3`,因为`普通location /prefix/`依据最大匹配原则能匹配当前请求,但是会被后面的`正则location`覆盖 + + * 请求URI:`GET /exact/match/test.html` + + > 则会被匹配到:`配置指令块1`,因为这个是`普通location` 的完全匹配(exact match),会禁止继续搜索`正则location` + +#### 正则location 匹配让步 普通location 的严格精确匹配结果,但覆盖 普通location 的最大前缀匹配结果 +### location 案例 +* 先普通 location ,再正则 location + + * nginx 其实是:`先匹配普通 location ,再匹配正则 location ` + + * 普通 location 的匹配结果又分两种 + + * 一种是`严格精确匹配(exact match)` + + * 另一种是`最大前缀匹配(Literal strings match the beginning portion of the query – the most specific match will be used)` + +#### [http://www.cnblogs.com/lidabo/p/4169396.html](http://www.cnblogs.com/lidabo/p/4169396.html) diff --git a/docs/Nginx/more-domain-config.md b/docs/Nginx/more-domain-config.md new file mode 100644 index 0000000..670bdca --- /dev/null +++ b/docs/Nginx/more-domain-config.md @@ -0,0 +1,147 @@ +## Nginx 同一个IP上配置多个HTTPS主机 ++ [Nginx 同一个IP上配置多个HTTPS主机](http://www.ttlsa.com/web/multiple-https-host-nginx-with-a-ip-configuration/) ++ 域名列表 + + | 序号 | 名称 | 域名 | HTTPS主机 | + | :--: |:--: |:---------------:| :-----| + | 1 | 官方域名 | www.tinywan.com | https://www.tinywan.com/ | + | 2 | 直播域名 | live.tinywan.com | https://live.tinywan.com/ | + | 3 | 点播域名 | vod.tinywan.com | https://vod.tinywan.com/ | + | 4 | 文档域名 | doc.tinywan.com | https://doc.tinywan.com/ | + ++ Openresty 编译 + + ```bash + www@TinywanAliYun:~/DEMO/openresty-1.11.2.5$ + ./configure --prefix=/usr/local/openresty --with-luajit \ + --with-http_ssl_module --with-openssl=/usr/local/openssl \ + --with-openssl-opt="enable-tlsext" --without-http_redis2_module \ + --with-http_iconv_module --with-http_stub_status_module \ + --with-http_xslt_module --add-dynamic-module=/home/www/DEMO/nginx-ts-module \ + --add-dynamic-module=/home/www/DEMO/nginx-rtmp-module + ... + make + sudo make install + ``` + > 注意添加配置:`--with-openssl-opt="enable-tlsext" `,默认情况下是`TLS SNI support disabled` ++ `Nginx.conf`配置文件: + + 配置文件列表 + + ```bash + www@TinywanAliYun:/usr/local/openresty/nginx/conf/vhost$ ls + doc.tinywan.com.conf live_rtmp_hls.conf live.tinywan.com.conf + main.conf vod.tinywan.com.conf www.tinywan.com.conf + ``` + + `nginx.conf` + + ```bash + http { + ... + index index.php index.html index.htm; + include "/usr/local/openresty/nginx/conf/vhost/*.conf"; + } + ``` + + `main.conf` + + ```bash + # 配置HTTP请求重定向 + server { + listen 80; + server_name www.tinywan.com; #live.tinywan.com vod.tinywan.com; + rewrite ^ https://$http_host$request_uri? permanent; + } + ``` + + `www.tinywan.com.conf` + + ```bash + server { + #listen 80; + listen 443 ssl; + server_name www.tinywan.com; + set $root_path /home/www/web/go-study-line/public; + root $root_path; + + ssl on; + ssl_certificate /etc/letsencrypt/live/www.tinywan.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/www.tinywan.com//privkey.pem; + server_tokens off; + + location / { + #access_by_lua_file /usr/local/openresty/nginx/conf/lua_script/resty-limit-req.lua; + if (!-e $request_filename) { + rewrite ^(.*)$ /index.php?s=/$1 last; + break; + } + } + + location = /favicon.ico { + log_not_found off; + } + + location ~ \.php$ { + #access_by_lua_file /usr/local/openresty/nginx/conf/lua_script/resty-limit-req.lua; + fastcgi_pass unix:/var/run/php7.1.8-fpm.sock; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; + fastcgi_buffer_size 128k; + fastcgi_buffers 4 256k; + fastcgi_busy_buffers_size 256k; + fastcgi_connect_timeout 300; + fastcgi_send_timeout 300; + fastcgi_read_timeout 300; + } + } + ``` + + `live.tinywan.com.conf` + + ```bash + # live.tinywan.com + server{ + listen 443 ssl; + server_name live.tinywan.com; + + root /home/www/web/live.tinywan.com; + + ssl on; + ssl_certificate /etc/letsencrypt/live/www.tinywan.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/www.tinywan.com//privkey.pem; + server_tokens off; + + } + ``` + + `vod.tinywan.com.conf` + + ```bash + # vod.tinywan.com + server{ + listen 443 ssl; + server_name vod.tinywan.com; + + root /home/www/web/vod.tinywan.com; + + ssl on; + ssl_certificate /etc/letsencrypt/live/www.tinywan.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/www.tinywan.com//privkey.pem; + server_tokens off; + + } + ``` + + `doc.tinywan.com.conf` + + ```bash + # doc.tinywan.com + server{ + listen 443 ssl; + server_name doc.tinywan.com; + + root /home/www/web/doc.tinywan.com; + + ssl on; + ssl_certificate /etc/letsencrypt/live/www.tinywan.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/www.tinywan.com//privkey.pem; + server_tokens off; + + } + ``` + \ No newline at end of file diff --git a/docs/Nginx/nginx b/docs/Nginx/nginx new file mode 100644 index 0000000..42ea15a --- /dev/null +++ b/docs/Nginx/nginx @@ -0,0 +1,408 @@ +#! /bin/sh +### BEGIN INIT INFO +# Provides: nginx +# Required-Start: $remote_fs $syslog +# Required-Stop: $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: nginx init.d dash script for Ubuntu or other *nix. +# Description: nginx init.d dash script for Ubuntu or other *nix. +### END INIT INFO +#------------------------------------------------------------------------------ +# nginx - this Debian Almquist shell (dash) script, starts and stops the nginx +# daemon for Ubuntu and other *nix releases. +# +# description: Nginx is an HTTP(S) server, HTTP(S) reverse \ +# proxy and IMAP/POP3 proxy server. This \ +# script will manage the initiation of the \ +# server and it's process state. +# +# processname: nginx +# config: /usr/local/nginx/conf/nginx.conf +# pidfile: /usr/local/nginx/logs/nginx.pid +# Provides: nginx +# +# Author: Jason Giedymin +# . +# +# Version: 3.9.0 12-MAY-2015 jason.giedymin AT gmail.com +# Notes: nginx init.d dash script for Ubuntu. +# Tested with: Ubuntu 14.10, nginx-1.7.9 +# +# This script's project home is: +# http://github.com/JasonGiedymin/nginx-init-ubuntu +# +#------------------------------------------------------------------------------ +# MIT X11 License +#------------------------------------------------------------------------------ +# +# Copyright (c) 2008-2013 Jason Giedymin, http://jasongiedymin.com +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#------------------------------------------------------------------------------ + +#------------------------------------------------------------------------------ +# Functions +#------------------------------------------------------------------------------ +LSB_FUNC=/lib/lsb/init-functions + +# Test that init functions exists +test -r $LSB_FUNC || { + echo "$0: Cannot find $LSB_FUNC! Script exiting." 1>&2 + exit 5 +} + +. $LSB_FUNC + +#------------------------------------------------------------------------------ +# Consts +#------------------------------------------------------------------------------ +# Include nginx defaults if available +if [ -f /etc/default/nginx ]; then + . /etc/default/nginx +fi + +# Minimize path +PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin + +PS=${PS:-"nginx"} # process name +DESCRIPTION=${DESCRIPTION:-"Nginx Server..."} # process description +NGINXPATH=${NGINXPATH:-/opt/openresty/nginx} # root path where installed +DAEMON=${DAEMON:-$NGINXPATH/sbin/nginx} # path to daemon binary +NGINX_CONF_FILE=${NGINX_CONF_FILE:-$NGINXPATH/conf/nginx.conf} # config file path + +PIDNAME=${PIDNAME:-"nginx"} # lets you do $PS-slave +PIDFILE=${PIDFILE:-$PIDNAME.pid} # pid file +PIDSPATH=${PIDSPATH:-$NGINXPATH/logs} # default pid location, you should change it +RUNAS=${RUNAS:-root} # user to run as + +SCRIPT_OK=0 # ala error codes +SCRIPT_ERROR=1 # ala error codes +TRUE=1 # boolean +FALSE=0 # boolean + +#------------------------------------------------------------------------------ +# Simple Tests +#------------------------------------------------------------------------------ + +# Test if nginx is a file and executable +test -x $DAEMON || { + echo "$0: You don't have permissions to execute nginx." 1>&2 + exit 4 +} + +# You can also set your conditions like so: +# set exit condition +# set -e + +#------------------------------------------------------------------------------ +# Functions +#------------------------------------------------------------------------------ + +setFilePerms(){ + if [ -f $PIDSPATH/$PIDFILE ]; then + chmod 400 $PIDSPATH/$PIDFILE + fi +} + +configtest() { + $DAEMON -t -c $NGINX_CONF_FILE +} + +getPSCount() { + return `pgrep -f $PS | wc -l` +} + +isRunning() { + if [ $1 ]; then + pidof_daemon $1 + PID=$? + + if [ $PID -gt 0 ]; then + return 1 + else + return 0 + fi + else + pidof_daemon + PID=$? + + if [ $PID -gt 0 ]; then + return 1 + else + return 0 + fi + fi +} + +#courtesy of php-fpm +wait_for_pid () { + try=0 + + while test $try -lt 35 ; do + case "$1" in + 'created') + if [ -f "$2" ]; then + try='' + break + fi + ;; + + 'removed') + if [ ! -f "$2" ]; then + try='' + break + fi + ;; + esac + + try=`expr $try + 1` + sleep 1 + done +} + +status(){ + isRunning + isAlive=$? + + if [ "${isAlive}" -eq $TRUE ]; then + log_warning_msg "$DESCRIPTION found running with processes: `pidof $PS`" + rc=0 + else + log_warning_msg "$DESCRIPTION is NOT running." + rc=3 + fi + + return +} + +removePIDFile(){ + if [ $1 ]; then + if [ -f $1 ]; then + rm -f $1 + fi + else + #Do default removal + if [ -f $PIDSPATH/$PIDFILE ]; then + rm -f $PIDSPATH/$PIDFILE + fi + fi +} + +start() { + log_daemon_msg "Starting $DESCRIPTION" + + isRunning + isAlive=$? + + if [ "${isAlive}" -eq $TRUE ]; then + log_end_msg $SCRIPT_ERROR + rc=0 + else + start-stop-daemon --start --quiet --chuid \ + $RUNAS --pidfile $PIDSPATH/$PIDFILE --exec $DAEMON \ + -- -c $NGINX_CONF_FILE + status=$? + setFilePerms + + if [ "${status}" -eq 0 ]; then + log_end_msg $SCRIPT_OK + rc=0 + else + log_end_msg $SCRIPT_ERROR + rc=7 + fi + fi + + return +} + +stop() { + log_daemon_msg "Stopping $DESCRIPTION" + + isRunning + isAlive=$? + + if [ "${isAlive}" -eq $TRUE ]; then + start-stop-daemon --stop --quiet --pidfile $PIDSPATH/$PIDFILE + + wait_for_pid 'removed' $PIDSPATH/$PIDFILE + + if [ -n "$try" ]; then + log_end_msg $SCRIPT_ERROR + rc=0 # lsb states 1, but under status it is 2 (which is more prescriptive). Deferring to standard. + else + removePIDFile + log_end_msg $SCRIPT_OK + rc=0 + fi + else + log_end_msg $SCRIPT_ERROR + rc=7 + fi + + return +} + +reload() { + configtest || return $? + + log_daemon_msg "Reloading (via HUP) $DESCRIPTION" + + isRunning + + if [ $? -eq $TRUE ]; then + kill -HUP `cat $PIDSPATH/$PIDFILE` + log_end_msg $SCRIPT_OK + rc=0 + else + log_end_msg $SCRIPT_ERROR + rc=7 + fi + + return +} + +quietupgrade() { + log_daemon_msg "Peforming Quiet Upgrade $DESCRIPTION" + + isRunning + isAlive=$? + + if [ "${isAlive}" -eq $TRUE ]; then + kill -USR2 `cat $PIDSPATH/$PIDFILE` + kill -WINCH `cat $PIDSPATH/$PIDFILE.oldbin` + + isRunning + isAlive=$? + + if [ "${isAlive}" -eq $TRUE ]; then + kill -QUIT `cat $PIDSPATH/$PIDFILE.oldbin` + wait_for_pid 'removed' $PIDSPATH/$PIDFILE.oldbin + removePIDFile $PIDSPATH/$PIDFILE.oldbin + + log_end_msg $SCRIPT_OK + rc=0 + else + log_end_msg $SCRIPT_ERROR + + log_daemon_msg "ERROR! Reverting back to original $DESCRIPTION" + + kill -HUP `cat $PIDSPATH/$PIDFILE` + kill -TERM `cat $PIDSPATH/$PIDFILE.oldbin` + kill -QUIT `cat $PIDSPATH/$PIDFILE.oldbin` + + wait_for_pid 'removed' $PIDSPATH/$PIDFILE.oldbin + removePIDFile $PIDSPATH/$PIDFILE.oldbin + + log_end_msg $SCRIPT_OK + rc=0 + fi + else + log_end_msg $SCRIPT_ERROR + rc=7 + fi + + return +} + +terminate() { + log_daemon_msg "Force terminating (via KILL) $DESCRIPTION" + + PIDS=`pidof $PS` || true + + [ -e $PIDSPATH/$PIDFILE ] && PIDS2=`cat $PIDSPATH/$PIDFILE` + + for i in $PIDS; do + if [ "$i" = "$PIDS2" ]; then + kill $i + wait_for_pid 'removed' $PIDSPATH/$PIDFILE + removePIDFile + fi + done + + log_end_msg $SCRIPT_OK + rc=0 +} + +destroy() { + log_daemon_msg "Force terminating and may include self (via KILLALL) $DESCRIPTION" + killall $PS -q >> /dev/null 2>&1 + log_end_msg $SCRIPT_OK + rc=0 +} + +pidof_daemon() { + PIDS=`pidof $PS` || true + + [ -e $PIDSPATH/$PIDFILE ] && PIDS2=`cat $PIDSPATH/$PIDFILE` + + for i in $PIDS; do + if [ "$i" = "$PIDS2" ]; then + return 1 + fi + done + + return 0 +} + +action="$1" +case "$1" in + start) + start + ;; + stop) + stop + ;; + restart|force-reload) + stop + # if [ $rc -ne 0 ]; then + # script_exit + # fi + sleep 1 + start + ;; + reload) + $1 + ;; + status) + status + ;; + configtest) + $1 + ;; + quietupgrade) + $1 + ;; + terminate) + $1 + ;; + destroy) + $1 + ;; + *) + FULLPATH=/etc/init.d/$PS + echo "Usage: $FULLPATH {start|stop|restart|force-reload|reload|status|configtest|quietupgrade|terminate|destroy}" + echo " The 'destroy' command should only be used as a last resort." + exit 3 + ;; +esac + +exit $rc diff --git a/docs/Nginx/nginx-1-config.md b/docs/Nginx/nginx-1-config.md new file mode 100644 index 0000000..9ed040b --- /dev/null +++ b/docs/Nginx/nginx-1-config.md @@ -0,0 +1,96 @@ + +## Nginx 陷阱和常见错误(以下为正确或者推荐配置) +--- +* 把 root 放在 location 区块外 + ``` + server { + server_name www.example.com; + root /var/www/Nginx -default/; + location / { + # [...] + } + location /foo { + # [...] + } + location /bar { + # [...] + } + } + ``` +* 简单的使用“ index ”指令一次就够了。只需要把它放到 http {} 区块里面,下面的就会继承这个配置 + + ``` + http { + index index.php index.htm index.html; + server { + server_name www.example.com; + location / { + # [...] + } + } + server { + server_name example.com; + location / { + # [...] + } + location /foo { + # [...] + } + } + } + ``` +* 不要使用 if 判断 Server Name + > 不推荐 + + ``` + server { + server_name example.com *.example.com; + if ($host ~* ^www\.(.+)) { + set $raw_domain $1; + rewrite ^/(.*)$ $raw_domain/$1 permanent; + } + # [...] + } + } + ``` + > 推荐配置 + + ``` + server { + server_name www.example.com; + return 301 $scheme://example.com$request_uri; + } + server { + server_name example.com; + # [...] + } + ``` +* 使用主机名来解析地址 + + > 不推荐配置 + + ``` + upstream { + server http://someserver; + } + + server { + listen myhostname:80; + # [...] + } + ``` + + > 推荐配置 + + ``` + upstream { + server http://10.48.41.12; + } + + server { + listen 127.0.0.16:80; + # [...] + } + ``` +* [更多信息](https://moonbingbing.gitbooks.io/openresty-best-practices/content/ngx/pitfalls_and_common_mistakes.html) + diff --git a/docs/Nginx/nginx-2-config.md b/docs/Nginx/nginx-2-config.md new file mode 100644 index 0000000..93a3621 --- /dev/null +++ b/docs/Nginx/nginx-2-config.md @@ -0,0 +1,114 @@ + +## Nginx 编译安装以及参数详解 +#### Nginx安装 +``` +# wget http://nginx.org/download/nginx-1.10.2.tar.gz +# tar xvf nginx-1.10.2.tar.gz -C /usr/local/src +# yum groupinstall "Development too +# yum -y install gcc wget gcc-c++ automake autoconf \ +-- libtool libxml2-devel libxslt-devel perl-devel \ +--perl-ExtUtils-Embed pcre-devel openssl-devel +# cd /usr/local/src/nginx-1.10.2 +# ./configure \ +--prefix=/usr/local/nginx \ 指向安装目录 +--sbin-path=/usr/sbin/nginx \ 指向(执行)程序文件(nginx) +--conf-path=/etc/nginx/nginx.conf \ 指向配置文件(nginx.conf) +--error-log-path=/var/log/nginx/error.log \ 指向错误日志目录 +--http-log-path=/var/log/nginx/access.log \ +--pid-path=/var/run/nginx.pid \ 指向pid文件(nginx.pid) +--lock-path=/var/run/nginx.lock \ 指向lock文件(nginx.lock)(安装文件锁定,防止安装文件被别人利用,或自己误操作。) +--http-client-body-temp-path=/var/tmp/nginx/client \ +--http-proxy-temp-path=/var/tmp/nginx/proxy \ +--http-fastcgi-temp-path=/var/tmp/nginx/fcgi \ +--http-uwsgi-temp-path=/var/tmp/nginx/uwsgi \ +--http-scgi-temp-path=/var/tmp/nginx/scgi \ +--user=nginx \ 指定程序运行时的非特权用户 +--group=nginx \ 指定程序运行时的非特权用户组 +--with-pcre \ 启用pcre库 +--with-http_v2_module \ +--with-http_ssl_module \ 启用ngx_http_ssl_module支持(使支持https请求,需已安装openssl) +--with-http_realip_module \ 启用ngx_http_realip_module支持(这个模块允许从请求标头更改客户端的IP地址值,默认为关) +--with-http_addition_module \ +--with-http_sub_module \ 启用ngx_http_sub_module支持(允许用一些其他文本替换nginx响应中的一些文本) +--with-http_dav_module \ 启用ngx_http_dav_module支持(增加PUT,DELETE,MKCOL:创建集合,COPY和MOVE方法)默认情况下为关闭,需编译开启 +--with-http_flv_module \ 启用ngx_http_flv_module支持(提供寻求内存使用基于时间的偏移量文件) +--with-http_mp4_module \ +--with-http_gunzip_module \ 禁用ngx_http_gzip_module支持(该模块同-with-http_gzip_static_module功能一样) +--with-http_gzip_static_module \ 启用ngx_http_gzip_static_module支持(在线实时压缩输出数据流) +--with-http_random_index_module \ 启用ngx_http_random_index_module支持(从目录中随机挑选一个目录索引) +--with-http_secure_link_module \ 启用ngx_http_secure_link_module支持(计算和检查要求所需的安全链接网址) +--with-http_stub_status_module \ 启用ngx_http_stub_status_module支持(获取nginx自上次启动以来的工作状态) +--with-http_auth_request_module \ 禁用ngx_http_auth_basic_module(该模块是可以使用用户名和密码基于http基本认证方法来保护你的站点或其部分内容) +-–without-http_access_module 禁用ngx_http_access_module支持(该模块提供了一个简单的基于主机的访问控制。允许/拒绝基于ip地址) +–-without-http_autoindex_module 禁用disable ngx_http_autoindex_module支持(该模块用于自动生成目录列表,只在ngx_http_index_module模块未找到索引文件时发出请求。) +–-without-http_geo_module 禁用ngx_http_geo_module支持(创建一些变量,其值依赖于客户端的IP地址 +-–without-http_map_module 禁用ngx_http_map_module支持(使用任意的键/值对设置配置变量) +–-without-http_split_clients_module 禁用ngx_http_split_clients_module支持(该模块用来基于某些条件划分用户。条件如:ip地址、报头、cookies等等) +–-without-http_referer_module 禁用disable ngx_http_referer_module支持(该模块用来过滤请求,拒绝报头中Referer值不正确的请求) +–-without-http_rewrite_module 禁用ngx_http_rewrite_module支持(该模块允许使用正则表达式改变URI,并且根据变量来转向以及选择配置 +–-without-http_proxy_module 禁用ngx_http_proxy_module支持(有关代理服务器) +-–without-http_fastcgi_module 禁用ngx_http_fastcgi_module支持(该模块允许Nginx 与FastCGI 进程交互,并通过传递参数来控制FastCGI 进程工作。 )FastCGI一个常驻型的公共网关接口。 +–-without-http_upstream_ip_hash_module 禁用ngx_http_upstream_ip_hash_module支持(该模块用于简单的负载均衡) +-–with-http_perl_module 启用ngx_http_perl_module支持(该模块使nginx可以直接使用perl或通过ssi调用perl) +–-with-perl_modules_path= 设定perl模块路径 +-–with-perl= 设定perl库文件路径 +-–http-log-path= 设定access log路径 +-–http-client-body-temp-path= 设定http客户端请求临时文件路径 +-–http-proxy-temp-path= 设定http代理临时文件路径 +-–http-fastcgi-temp-path= 设定http fastcgi临时文件路径 +-–http-uwsgi-temp-path= 设定http uwsgi临时文件路径 +–-http-scgi-temp-path= 设定http scgi临时文件路径 +--without-http 禁用http server功能 +-–without-http-cache 禁用http cache功能 +-–with-mail 启用POP3/IMAP4/SMTP代理模块支持 +-–with-mail_ssl_module 启用ngx_mail_ssl_module支持 +-–without-mail_pop3_module 禁用pop3协议 +--with-file-aio \ +--with-ipv6 \ 启用ipv6支持 +--with-http_v2_module \ +--with-threads \ +--with-stream \ +-–with-libatomic= 指向libatomic_ops安装目录 +-–with-openssl= 指向openssl安装目录 +-–with-openssl-opt 在编译时为openssl设置附加参数 +--with-debug 启用debug日志 +--with-stream_ssl_module +--add-module=/home/tinywan/nchan-1.1.3/ +# make && make install +# mkdir -pv /var/tmp/nginx/client +``` + +#### 启动关闭重置Nginx +* 启动:直接执行以下命令,nginx就启动了,不需要改任何配置文件 +``` +/usr/local/nginx -1.5.1/sbin/nginx +``` +* 关闭 +``` +/usr/local/nginx -1.5.1/sbin/nginx -s stop +``` +* 重启 +``` +/usr/local/nginx -1.5.1/sbin/nginx -s reload +``` +C:\Program Files\Git\bin + +#### nginx root&alias文件路径配置 +nginx指定文件路径有两种方式root和alias,这两者的用法区别,使用方法总结了下,方便大家在应用过程中,快速响应。root与alias主要区别在于nginx如何解释location后面的uri,这会使两者分别以不同的方式将请求映射到服务器文件上。 + +``` +location /abc/ { + alias /home/html/def/; +} +``` +alias会把location后面配置的路径丢弃掉,把当前匹配到的目录指向到指定的目录。如果一个请求的URI是/abc/a.ttlsa.com/favicon.jgp时,web服务器将会返回服务器上的/home/html/def/a.ttlsa.com/favicon.jgp的文件。 + +###### alias注意要点 + +> 1.使用alias时,目录名后面一定要加”/”`。 + +> 2.alias可以指定任何名称。 + +> 3.alias在使用正则匹配时,必须捕捉要匹配的内容并在指定的内容处使用。 + +> 4.alias只能位于location块中 \ No newline at end of file diff --git a/docs/Nginx/nginx-base-config.md b/docs/Nginx/nginx-base-config.md new file mode 100644 index 0000000..7c4016d --- /dev/null +++ b/docs/Nginx/nginx-base-config.md @@ -0,0 +1,374 @@ +## Nginx 配置文件 nginx.conf 详解 + +```bash +#定义Nginx运行的用户和用户组 +user www www; + +#nginx进程数,建议设置为等于CPU总核心数。 +worker_processes 8; + +#全局错误日志定义类型,[ debug | info | notice | warn | error | crit ] +error_log /var/log/nginx/error.log info; + +#进程文件 +pid /var/run/nginx.pid; + +#一个nginx进程打开的最多文件描述符数目,理论值应该是最多打开文件数(系统的值ulimit -n)与nginx进程数相除,但是nginx分配请求并不均匀,所以建议与ulimit -n的值保持一致。 +worker_rlimit_nofile 65535; + +#工作模式与连接数上限 +events +{ +#参考事件模型,use [ kqueue | rtsig | epoll | /dev/poll | select | poll ]; epoll模型是Linux 2.6以上版本内核中的高性能网络I/O模型,如果跑在FreeBSD上面,就用kqueue模型。 +use epoll; +#单个进程最大连接数(最大连接数=连接数*进程数) +worker_connections 65535; +} + +#设定http服务器 +http +{ + include mime.types; #文件扩展名与文件类型映射表 + default_type application/octet-stream; #默认文件类型 + #charset utf-8; #默认编码 + server_names_hash_bucket_size 128; #服务器名字的hash表大小 + client_header_buffer_size 32k; #上传文件大小限制 + large_client_header_buffers 4 64k; #设定请求缓 + client_max_body_size 8m; #设定请求缓 + sendfile on; #开启高效文件传输模式,sendfile指令指定nginx是否调用sendfile函数来输出文件,对于普通应用设为 on,如果用来进行下载等应用磁盘IO重负载应用,可设置为off,以平衡磁盘与网络I/O处理速度,降低系统的负载。注意:如果图片显示不正常把这个改成off。 + autoindex on; #开启目录列表访问,合适下载服务器,默认关闭。 + tcp_nopush on; #防止网络阻塞 + tcp_nodelay on; #防止网络阻塞 + keepalive_timeout 120; #长连接超时时间,单位是秒 + + #FastCGI相关参数是为了改善网站的性能:减少资源占用,提高访问速度。下面参数看字面意思都能理解。 + fastcgi_connect_timeout 300; + fastcgi_send_timeout 300; + fastcgi_read_timeout 300; + fastcgi_buffer_size 64k; + fastcgi_buffers 4 64k; + fastcgi_busy_buffers_size 128k; + fastcgi_temp_file_write_size 128k; + + #gzip模块设置 + gzip on; #开启gzip压缩输出 + gzip_min_length 1k; #最小压缩文件大小 + gzip_buffers 4 16k; #压缩缓冲区 + gzip_http_version 1.0; #压缩版本(默认1.1,前端如果是squid2.5请使用1.0)开始压缩的http协议版本(可以不设置,目前几乎全是1.1协议) + gzip_comp_level 2; #推荐6压缩级别(级别越高,压的越小,越浪费CPU计算资源) + gzip_types text/plain application/x-javascript text/css application/xml; + #压缩类型,默认就已经包含text/html,所以下面就不用再写了,写上去也不会有问题,但是会有一个warn。 + gzip_vary on; # 是否传输gzip压缩标志 + #limit_zone crawler $binary_remote_addr 10m; #开启限制IP连接数的时候需要使用 + + upstream blog.ha97.com { + #upstream的负载均衡,weight是权重,可以根据机器配置定义权重。weigth参数表示权值,权值越高被分配到的几率越大。 + server 192.168.80.121:80 weight=3; + server 192.168.80.122:80 weight=2; + server 192.168.80.123:80 weight=3; +} + +#虚拟主机的配置 +server +{ + #监听端口 + listen 80; + #域名可以有多个,用空格隔开 + server_name www.ha97.com ha97.com; + index index.html index.htm index.php; + root /data/www/ha97; + + location ~ .*\.(php|php5)?$ + { + fastcgi_pass 127.0.0.1:9000; + fastcgi_index index.php; + include fastcgi.conf; + } + + #图片缓存时间设置 + location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ + { + expires 10d; + } + + #JS和CSS缓存时间设置 + location ~ .*\.(js|css)?$ + { + expires 1h; + } + + #日志格式设定 + log_format access '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" $http_x_forwarded_for'; + #定义本虚拟主机的访问日志 + access_log /var/log/nginx/ha97access.log access; + + #对 "/" 启用反向代理 + location / { + proxy_pass http://127.0.0.1:88; + proxy_redirect off; + proxy_set_header X-Real-IP $remote_addr; + #后端的Web服务器可以通过X-Forwarded-For获取用户真实IP + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + #以下是一些反向代理的配置,可选。 + proxy_set_header Host $host; + client_max_body_size 10m; #允许客户端请求的最大单文件字节数 + client_body_buffer_size 128k; #缓冲区代理缓冲用户端请求的最大字节数, + proxy_connect_timeout 90; #nginx跟后端服务器连接超时时间(代理连接超时) + proxy_send_timeout 90; #后端服务器数据回传时间(代理发送超时) + proxy_read_timeout 90; #连接成功后,后端服务器响应时间(代理接收超时) + proxy_buffer_size 4k; #设置代理服务器(nginx)保存用户头信息的缓冲区大小 + proxy_buffers 4 32k; #proxy_buffers缓冲区,网页平均在32k以下的设置 + proxy_busy_buffers_size 64k; #高负荷下缓冲大小(proxy_buffers*2) + proxy_temp_file_write_size 64k; + #设定缓存文件夹大小,大于这个值,将从upstream服务器传 + } + + #设定查看Nginx状态的地址 + location /NginxStatus { + stub_status on; + access_log on; + auth_basic "NginxStatus"; + auth_basic_user_file conf/htpasswd; + #htpasswd文件的内容可以用apache提供的htpasswd工具来产生。 + } + + #本地动静分离反向代理配置 + #所有jsp的页面均交由tomcat或resin处理 + location ~ .(jsp|jspx|do)?$ { + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_pass http://127.0.0.1:8080; + } + + #所有静态文件由nginx直接读取不经过tomcat或resin + location ~ .*.(htm|html|gif|jpg|jpeg|png|bmp|swf|ioc|rar|zip|txt|flv|mid|doc|ppt|pdf|xls|mp3|wma)$ + { + expires 15d; + } + location ~ .*.(js|css)?$ + { + expires 1h; + } + } +} + +``` +#### 常用配置案例 + +```bash +user nginx nginx; +worker_processes 1; + +error_log /var/log/nginx/error.log; +#error_log logs/error.log notice; +#error_log logs/error.log info; + +pid /var/run/nginx/nginx.pid; + +events { + worker_connections 1024; +} + + +http { + include mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + #tcp_nopush on; + + #keepalive_timeout 0; + keepalive_timeout 65; + + #隐藏Nginx版本信息,禁止网站目录浏览 + server_tokens off; + autoindex off; + #当FastCGI后端服务器处理请求给出http响应码为4xx和5xx时,就转发给nginx + fastcgi_intercept_errors on; + + #关于fastcgi的配置 + fastcgi_connect_timeout 300; + fastcgi_send_timeout 300; + fastcgi_read_timeout 300; + fastcgi_buffer_size 64k; + fastcgi_buffers 4 64k; + fastcgi_busy_buffers_size 128k; + fastcgi_temp_file_write_size 128k; + + #支持gzip压缩 + gzip on; + gzip_min_length 1k; + gzip_buffers 16 64k; + gzip_http_version 1.1; + gzip_comp_level 6; + gzip_types text/plain application/x-javascript text/css application/javascript text/javascript image/jpeg image/gif image/png application/xml application/json; + gzip_vary on; + gzip_disable "MSIE [1-6].(?!.*SV1)"; + + # + # 重定向所有带www请求到非www的请求 + # + server { + listen *:80; + listen *:443 ssl spdy; + server_name www.typecodes.com; + # ssl证书配置见文章 https://typecodes.com/web/lnmppositivessl.html + ssl_certificate /etc/nginx/ssl/typecodes.crt; + # ssl密钥文件见文章 https://typecodes.com/web/lnmppositivessl.html + ssl_certificate_key /etc/nginx/ssl/typecodes.key; + # 不产生日志 + access_log off; + + # 访问favicon.ico和robots.txt不跳转(把这两个文件存放在上级目录html中) + location ~* ^/(favicon.ico|robots.txt)$ { + root html; + expires max; + log_not_found off; + break; + } + + location / { + return 301 https://typecodes.com$request_uri; + } + } + + # + # 将所有http请求重定向到https + # + server { + listen *:80; + server_name typecodes.com; + # 不产生日志 + access_log off; + + # 访问favicon.ico和robots.txt不跳转(把这两个文件存放在上级目录html中) + location ~* ^/(favicon.ico|robots.txt)$ { + root html; + expires max; + log_not_found off; + break; + } + + location / { + return 301 https://typecodes.com$request_uri; + } + } + + # + # HTTPS server + # + server { + listen *:443 ssl spdy; + server_name typecodes.com; + + # ssl证书配置见文章 https://typecodes.com/web/lnmppositivessl.html + ssl_certificate /etc/nginx/ssl/typecodes.crt; + # ssl密钥文件见文章 https://typecodes.com/web/lnmppositivessl.html + ssl_certificate_key /etc/nginx/ssl/typecodes.key; + ssl_session_cache shared:SSL:20m; + ssl_session_timeout 10m; + ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA:!CAMELLIA; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #enables TLSv1, but not SSLv2, SSLv3 which is weak and should no longer be used. + ssl_prefer_server_ciphers on; + # 开启spdy功能 + add_header Alternate-Protocol 443:npn-spdy/3.1; + # 严格的https访问 + add_header Strict-Transport-Security "max-age=31536000; includeSubdomains;"; + + #设置网站根目录 + root /usr/share/nginx/html/typecodes; + index index.php index.html; + + charset utf-8; + + #access_log /var/log/nginx/log/host.access.log main; + + #设置css/javascript/图片等静态资源的缓存时间 + location ~ .*\.(css|js|ico|png|gif|jpg|json|mp3|mp4|flv|swf)(.*) { + expires 60d; + } + + # include /etc/nginx/default.d/*.conf; + # 设置typecho博客的config文章不被访问,保证安全 + location = /config.inc.php{ + deny all; + } + + # keep the uploads directory safe by excluding php, php5, html file accessing. Applying to wordpress and typecho. + # location ~ .*/uploads/.*\.(php|php5|html)$ { + # deny all; + # } + + # 设置wordpress和typecho博客中,插件目录无法直接访问php或者html文件 + location ~ .*/plugins/.*\.(php|php5|html)$ { + deny all; + } + + #Rewrite的伪静态(针对wordpress/typecho),url地址去掉index.php + location / { + if (-f $request_filename/index.html){ + rewrite (.*) $1/index.html break; + } + if (-f $request_filename/index.php){ + rewrite (.*) $1/index.php; + } + if (!-f $request_filename){ + rewrite (.*) /index.php; + } + } + + #访问favicon.ico时不产生日志 + location = /favicon.ico { + access_log off; + } + + #设置40系列错误的应答文件为40x.html + error_page 400 401 402 403 404 /40x.html; + location = /40x.html { + root html; + index index.html index.htm; + } + + #设置50系列错误的应答文件为50x.html + # + error_page 500 501 502 503 504 /50x.html; + location = /50x.html { + root html; + index index.html index.htm; + } + + # proxy the PHP scripts to Apache listening on 127.0.0.1:80 + # + #location ~ \.php$ { + # proxy_pass http://127.0.0.1; + #} + + # 设置Nginx和php通信机制为tcp的socket模式,而不是直接监听9000端口 + location ~ .*\.php(\/.*)*$ { + fastcgi_split_path_info ^(.+\.php)(/.+)$; + #fastcgi_pass 127.0.0.1:9000; + # the better form of fastcgi_pass than before + fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; + } + + # deny access to .htaccess files, if Apache's document root + # concurs with nginx's one + # + #location ~ /\.ht { + # deny all; + #} + } +} +``` \ No newline at end of file diff --git a/docs/Nginx/nginx-basic.md b/docs/Nginx/nginx-basic.md new file mode 100644 index 0000000..8c2a3e4 --- /dev/null +++ b/docs/Nginx/nginx-basic.md @@ -0,0 +1,90 @@ + +# Nginx 基础知识 +--- + ++ [NGINX 所有 Modules](https://www.nginx.com/resources/wiki/modules/) + ++ [agentzh的Nginx教程地址](https://openresty.org/download/agentzh-nginx-tutorials-zhcn.html) + +## agentzh的Nginx教程笔记(版本2016.07.21) + +#### Nginx 变量漫谈(一) + +* Nginx 变量的值只有一种类型,那就是字符串 + +* Nginx “变量插值” + + ```bash + location /test { + set $first "hello "; + echo "${first}world"; + } + ``` + +* `set` 指令(以及前面提到的 geo 指令)不仅有赋值的功能,它还有创建 Nginx 变量的副作用,即当作为赋值对象的变量尚不存在时 + +* Nginx 变量一旦创建,其变量名的可见范围就是整个 Nginx 配置,甚至可以跨越不同虚拟主机的 server 配置块 + +* Nginx 变量的生命期是不可能跨越请求边界的 + +#### Nginx 变量漫谈(二) + ++ 跳转 + + + 内部跳转:就是在处理请求的过程中,于服务器内部,从一个 location 跳转到另一个 location 的过程。 + + + 外部跳转: HTTP 状态码 301 和 302 所进行的“外部跳转” + ++ 标准 ngx_rewrite 模块的 rewrite 配置指令其实也可以发起“内部跳转” + ++ Nginx 核心和各个 Nginx 模块提供的“预定义变量” + ++ Nginx 会在匹配参数名之前,自动把原始请求中的参数名调整为全部小写的形式 + ++ 如果你尝试改写另外一些只读的内建变量,比如 $arg_XXX 变量,在某些 Nginx 的版本中甚至可能导致进程崩溃。 + +#### Nginx 变量漫谈(三) + ++ map 指令:用于定义两个 Nginx 变量之间的映射关系,或者说是函数关系 + ++ map 指令只能在 http 块中使用 + ++ map 配置指令的工作原理是为用户变量注册 “取处理程序”,并且实际的映射计算是在“取处理程序”中完成的,而“取处理程序”只有在该用户变量被实际读取时才会执行(当然,因为缓存的存在,只在请求生命期中的第一次读取中才被执行),所以对于那些根本没有用到相关变量的请求来说,就根本不会执行任何的无用计算。 + +#### [Nginx的11个Phases](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/nginx-phases.md) + +#### [Nginx 陷阱和常见错误](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/nginx-1-config.md) + +#### [Nginx 高并发系统内核优化](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/nginx-parameter-config.md) + +#### [nginx 并发数问题思考:worker_connections,worker_processes与 max clients](http://liuqunying.blog.51cto.com/3984207/1420556?utm_source=tuicool) + ++ 从用户的角度,http 1.1协议下,由于浏览器默认使用两个并发连接,因此计算方法: + + 1. nginx作为http服务器的时候: + + ```bash + max_clients = worker_processes * worker_connections/2 + ``` + + 1. nginx作为反向代理服务器的时候: + + ```bash + max_clients = worker_processes * worker_connections/4 + ``` ++ 从一般建立连接的角度,客户并发连接为1: + + 1. nginx作为http服务器的时候: + + ```bash + max_clients = worker_processes * worker_connections + ``` + 1. nginx作为反向代理服务器的时候: + + ```bash + max_clients = worker_processes * worker_connections/2 + ``` ++ nginx做反向代理时,和客户端之间保持一个连接,和后端服务器保持一个连接 + ++ clients与用户数 + > 同一时间的clients(客户端数)和用户数还是有区别的,当一个用户请求发送一个连接时这两个是相等的,但是当一个用户默认发送多个连接请求的时候,clients数就是用户数*默认发送的连接并发数了。 diff --git a/docs/Nginx/nginx-high-basic.md b/docs/Nginx/nginx-high-basic.md new file mode 100644 index 0000000..927f22c --- /dev/null +++ b/docs/Nginx/nginx-high-basic.md @@ -0,0 +1,351 @@ +# Nginx高性能WEB服务器详解 +--- +## 第一章 初探 + ++ [Nginx 编译安装以及参数详解](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/nginx-2-config.md) + ++ NGINX变量详解 + + * [nginx变量使用方法详解笔记(1)](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx-Develop/notes-1.md) + + * [nginx变量使用方法详解笔记(2)](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx-Develop/notes-2.md) + + * [nginx变量使用方法详解笔记(3)](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/nginx-2-config.md) + ++ Nginx指令执行顺序 + + * [Nginx指令执行命令(01)](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx-Develop/command-order-01.md) + +## 第二章 安装部署 + ++ 启动错误: +> `Nginx [emerg]: bind() to 0.0.0.0:80 failed (98: Address already in use)` + 执行:`sudo fuser -k 80/tcp` + ++ [基于域名、IP的虚拟主机配置](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/Nginx-Web/Nginx-2-4-all-config.md) + ++ [完整、标准配置实际示列](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/Nginx-Web/Nginx-2-4-basic-config.md) + ++ [日志文件配置与切割](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/Nginx-Web/Nginx-2-4-log-cut.md) + ++ alias 和 root 在location 下的应用 + + + 通过alias 实现别名功能 + + ```bash + location /live { + alias /home/tinywan/HLS/; + } + ``` + + curl 请求结果 + + ```bash + tinywan@tinywan:~/HLS$ cat index.html + alias /home/tinywan/HLS/index.html + tinywan@tinywan:~/HLS$ curl http://127.0.0.1/live/index.html + alias /home/tinywan/HLS/index.html + ``` + + 结论: + + 1. cul 请求 `/live/index.html`,那么Nginx将会在服务器上查找`/home/tinywan/HLS/index.html` 文件 + + 1. 请求的`url` 中的`location`后面的部分会被追加到`alias `指定的目录后面,而`location`后面的`/live`路径将会别自动抛弃 + + - 类似案例[2]: + + - config配置信息 + + ```bash + location ~ ^/live/(.*)$ { + alias /home/tinywan/HLS/$1; + } + ``` + - curl 请求结果 + + ```bash + tinywan@tinywan:~/HLS$ pwd + /home/tinywan/HLS + tinywan@tinywan:~/HLS$ cat txt.txt + txt file + tinywan@tinywan:~/HLS$ curl http://127.0.0.1/live/txt.txt + txt file + ``` + + - 如果url请求`/live/txt.txt`那么Nginx将会在服务器上查找`/home/tinywan/HLS/txt.txt` 文件 + + - **与root 功能的差别**: + + - config配置信息,注意:一下的`alias` 换成 `root ` + + ```bash + location ~ ^/live/(.*)$ { + root /home/tinywan/HLS/$1; + } + ``` + + - curl 请求结果 + + ```bash + tinywan@tinywan:~/HLS$ curl http://127.0.0.1/live/txt.txt + + 404 Not Found + +

      404 Not Found

      +
      openresty/1.11.2.1
      + + + ``` + + - 日志文件信息(打开Nginx的rewrite日志:rewrite_log on;): + + ``` + /home/tinywan/HLS/txt.txt/live/txt.txt + ``` + + - **二者的区别** + + 1. `alias` 指定的目录是当前目录 + + 1. `root` 指定的是根目录 + 1. 一般建议的`location /`中通过`root`命令配置目录,其他目录匹配的位置使用`alias`命令 + + - 案例[3]: + + - config配置信息 + ``` + location ~ ^/live/(\w+)/(.*) { + alias /home/tinywan/HLS/live/$1/$2; + } + ``` + + - curl 请求结果 + ``` + tinywan@tinywan:~/HLS/live/stream123$ pwd + /home/tinywan/HLS/live/stream123 + tinywan@tinywan:~/HLS/live/stream123$ cat index.m3u8 + 12312312312 + tinywan@tinywan:~/HLS/live/stream123$ curl "http://127.0.0.1/live/stream123/index.m3u8?token=1234&api=009132" + 12312312312 + ``` +####
      第三章 架构初探 + +- [ ] 测试一 + +#### 第四章 高级配置 + ++ 基本语法:location [=|~|~*|^~] /uri/ { … } + 1. `= `:严格匹配。如果这个查询匹配,那么将停止搜索并立即处理此请求。 + 2. `~ `:为区分大小写匹配(可用正则表达式) + 3. `!~ `:为区分大小写不匹配 + 4. `!~*`:为不区分大小写不匹配 + 5. ` ^~ `:如果把这个前缀用于一个常规字符串,那么告诉nginx 如果路径匹配那么不测试正则表达式 + ++ [Perl 正则表达式参考](http://www.runoob.com/perl/perl-regular-expressions.html) + ++ 正则中需要转义的特殊字符小结 + - [1] ` $` 匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 ‘\n' 或 ‘\r'。要匹配 $ 字符本身,请使用 \$。 + - [2] ` ( )` 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 和。 + - [3] ` * ` 匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 \*。 + - [4] ` +` 匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 \+。 + - [5] ` . ` 匹配除换行符 \n之外的任何单字符。要匹配 .,请使用 \。 + - [6] ` [ ]` 标记一个中括号表达式的开始。要匹配 [,请使用 \[。 + - [7] ` ? ` 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用 \?。 + - [8] ` \ ` 将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如, ‘n' 匹配字符 ‘n'。'\n' 匹配换行符。序列 ‘\\' 匹配 “\”,而 ‘\(' 则匹配 “(”。 + - [9] ` ^ ` 匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合。要匹配 ^ 字符本身,请使用 \^。 + - [10] ` { }` 标记限定符表达式的开始。要匹配 {,请使用 \{。 + - [11] ` | ` 指明两项之间的一个选择。要匹配 |,请使用 \|。 + ++ 正则表达式 (Regular expression) 匹配location + - [1] `location ~* \.(gif|jpg|jpeg)$ { }`:匹配所有以 gif,jpg或jpeg 结尾的请求 + - [2] `location ~ /documents/Abc { }`:匹配任何以 /documents/ 开头的地址,匹配符合以后,还要继续往下搜索 + - [3] **目录匹配:** + 1. 可以匹配静态文件目录`(static/lib)` + 2. HLS直播目录`(/home/HLS/stream123/index.m3u8)` + 3. HLS/MP4/FLV点播视频目录`(/home/HLS/stream123.m3u8)` + 4. 匹配URL地址:`http://127.0.0.1/live/stream123/index.m3u8` + 5. nginx.conf 配置信息 + ``` + # 匹配任何以/live/ 开头的任何查询并且停止搜索。任何正则表达式将不会被测试 + location ^~ /live/ { + root /home/tinywan/HLS/; + } + # 以上匹配成功后的组合:/home/tinywan/HLS/live/.... + ``` ++ 后缀匹配 + 1. 匹配任何后缀文件名`gif|jpg|jpeg|png|css|js|ico|m3u8|ts` 结尾的请求 + 2. TS 文件匹配`http://127.0.0.1/live/stream123/11.ts` + 3. M3U8 文件匹配`http://127.0.0.1/live/stream123/index.m3u8` + 4. 匹配URL地址:`http://127.0.0.1/hls/123.m3u8` + 5. nginx.conf 配置信息 + ``` + location ~* \.(gif|jpg|jpeg|png|css|js|ico|m3u8|ts)$ { + root /home/tinywan/HLS/; + } + ``` ++ HSL直播目录匹配实际案例(请测试上线) + 1. 可以后缀文件名:`http://127.0.0.1/live/stream123/index.m3u8` + ``` + location ^~ /live/ { + root /home/tinywan/HLS/; + } + ``` + ++ [nginx配置location总结及rewrite规则写法](http://seanlook.com/2015/05/17/nginx-location-rewrite/) + +#### 第五章 Gzip压缩 ++ 测试一 + +#### 第六章 Rewrite 功能 + ++ Rewrite 常用全局变量 + + 请求案例: `curl -G -d "name=Tinywan&age=24" http://127.0.0.1/rewrite_var/1192/index.m3u8` + + 接受结果: + + | 变量 | 值 |描述 | + | --------- | ----------- |----------- | + | $args | name=Tinywan&age=24 |存放URL 请求的指令 | + | $content_length | 0 | 请求头中的Content-length字段| + | $content_type | 0 |请求头中的Content-Type字段 | + | $document_root | /opt/openresty/nginx/html | 当前请求在root指令中指定的值 | + | $document_uri | /rewrite_var/1192/index.m3u8 | 与$uri相同 | + | $host | 127.0.0.1 |请求主机头字段,否则为服务器名称 | + | $http_user_agent | curl/7.47.0 | 客户端agent信息| + | $http_cookie | 0 | COOKIE变量的值| + | $limit_rate | 0 | 限制连接速率| + | $request_body_file | null | 客户端请求主体信息的临时文件名| + | $request_method | GET | 客户端请求的动作,通常为GET或POST | + | $remote_addr | 127.0.0.1 |客户端的IP地址 | + | $remote_port | 33516 |客户端端口| + | $remote_user | 0 | 已经经过Auth Basic Module验证的用户名| + | $request_filename | /opt/openresty/nginx/html/rewrite_var/1192/index.m3u8 |当前请求的文件路径 | + | $request_uri | /rewrite_var/1192/index.m3u8?name=Tinywan&age=24 |包含请求参数的原始URI,不包含主机名 | + | $query_string | name=Tinywan&age=24 | 与$args相同| + | $scheme | http |HTTP方法(如http,https | + | $server_protocol | HTTP/1.1 |请求使用的协议,通常是HTTP/1.0或HTTP/1.1 | + | $server_addr | 127.0.0.1 |服务器地址 | + | $server_name | localhost | 服务器名称| + | $server_port | 80 |请求到达服务器的端口号 | + | $uri | /rewrite_var/1192/index.m3u8 | 不带请求参数的当前URI| + | $binary_remote_addr | 乱码 | 二进制格式的客户端地址| + + + uri 介绍 **(Nginx中的URI是相对的URI)** + + URL:`https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/config.md` + + 绝对URI:`https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/config.md` + + 相对URI:`/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/config.md` + ![Markdown](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Images/URI-URL-Image.jpg) + ++ Rewrite 正则匹配` uri `参数接收 + 1. 请求案例:`curl http://192.168.18.143/live/tinywan123/index.m3u8` + 2. Nginx.conf配置文件 + ```Lua + location ~* ^/live/(\w+)/(\D+)\.(m3u8|ts)$ { + set $num $2; + set $arg1 $1; + echo "args === ${arg1}"; + echo "1==$1 2==$2 3==$3"; + echo "Total_numbser :: $num"; + echo "URI $uri"; + } + + ``` + 3. 输出结果 + ``` + args === tinywan123 + $1==tinywan123 $2==index $3==m3u8 + Total_numbser :: index + URI /live/tinywan123/index.m3u8 + Total_numbser :: + ``` + 4. $1为正则匹配多个英文字母或数字的字符串 `(\w+)` + $2 为正则匹配多个非数字 `(\D+)` + $3 为正则匹配的第一个值 `(m3u8|ts)` + `.` 需要用转义字符转义`\.` +## 第七章 代理服务 + ++ [正向代理和反向代理的概念](#title) + ++ [正向代理服务](#title) + ++ [反向代理的服务](#title) + ++ [Nginx日志服务](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/Nginx-Web/Nginx-2-Log.md) + ++ 负载均衡 + ++ HTTP负载均衡 + + - [x] [简单的负载平衡](http://nginx.org/en/docs/http/ngx_http_core_module.html?&_ga=1.179030369.49817296.1480411319#http) + + - [x] [简单的负载平衡](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/Nginx-Web/Nginx-7-Proxy-1.md) + + - [x] [负载均衡五个配置实例](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/Nginx-Web/Nginx-7-Proxy.md) + + - [x] [Openresty-Lua动态修改upstream后端服务](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/Nginx-Web/openresty-nginx-lua-Proxy.md) + ++ TCP负载均衡 + + - [x] [Module ngx_stream_core_module](http://nginx.org/en/docs/stream/ngx_stream_core_module.html#stream) + + - [x] [负载均衡](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/Nginx-Web/Nginx-8-tcp-Proxy.md) + ++ proxy_pass 代理的URL总结 + + + 在nginx中配置proxy_pass时,当在后面的url加上了/,相当于是绝对根路径,则nginx不会把location中匹配的路径部分代理走;如果没有/,则会把匹配的路径部分也给代理走。 + + + 将url中以/wap/开头的请求转发到后台对应的某台server上,注意最后的?$args,表明把原始url最后的get参数也给代理到后台 + ```bash + location ~* /wap/(\d+)/(.+) + { + proxy_pass http://mx$1.test.com:6601/$2?$args; + } + ``` + + + 第一种配置,访问:`http://127.0.0.1/proxy/index.html` 会被代理到:`http://127.0.0.1:8000/index.html` + ```bash + location /proxy/ { + proxy_pass http://127.0.0.1:8000/; + } + ``` + + + 第二种配置,访问:`http://127.0.0.1/proxy/index.html` 会被代理到:`http://127.0.0.1:8000/proxy/index.html` + ```bash + location /proxy/ { + proxy_pass http://127.0.0.1:8000; + } + ``` + + + 第三种配置,访问:`http://127.0.0.1/proxy/index.html` 会被代理到:`http://127.0.0.1:8000/video/index.html` + ```bash + location /proxy/ { + proxy_pass http://127.0.0.1:8000/video/; + } + ``` + + + 第四种配置,访问:`http://127.0.0.1/proxy/index.html` 会被代理到:`http://127.0.0.1:8000/videoindex.html` + ```bash + location /proxy/ { + proxy_pass http://127.0.0.1:8000/video; + } + ``` + ++ location 直接访问: + + + 以下配置,当访问:`http://127.0.0.1:8000/proxy/index.html` 会被匹配到:`/usr/local/nginx/html/proxy/index.html` + ```bash + location /proxy/ { + root /usr/local/nginx/html; + index index.html index.htm; + } + ``` + +## 第八章 缓存机制 + ++ [Proxy Cache 缓存机制](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/Nginx-Web/Nginx-8-proxy_cache.md) + +## 第九章 Nginx初探1 + ++ 测试一 + +## 第十章 Nginx初探1 + ++ 测试一 diff --git a/docs/Nginx/nginx-install.md b/docs/Nginx/nginx-install.md new file mode 100644 index 0000000..0b72467 --- /dev/null +++ b/docs/Nginx/nginx-install.md @@ -0,0 +1,238 @@ +# 在Ubuntu 16.04中如何从源代码编译Nginx +NGINX可用作HTTP/HTTPS服务器,反向代理服务器,邮件代理服务器,负载均衡器,TLS终结器或缓存服务器。它是相当模块化的设计。它具有由社区创建的本机模块和第三方模块。以C编程语言编写,它是一个非常快速和轻便的软件。 + +## 从源头构建NGINX的要求,强制性要求: + ++ OpenSSL库版本介于1.0.2 - 1.1.0之间 + ++ Zlib库版本介于1.1.3 - 1.2.11之间 + ++ PCRE库版本在4.4 - 8.40之间 + ++ GCC编译器 + +## 可选要求: + ++ PERL + ++ LIBATOMIC_OPS + ++ LibFD + ++ MaxMind GeoIP + ++ libxml2 + ++ libxslt + +## 开始之前 + ++ 创建普通用户`sudo`访问。 + ++ 切换到新用户:`su - ` + ++ 更新系统:`sudo apt update && sudo apt upgrade -y` + +## 从源代码构建NGINX + ++ NGINX是用C编写的程序,所以我们需要安装C编译器(GCC)。 + + ```bash + sudo apt install build-essential -y + ``` + ++ 下载最新版本的NGINX源代码并解压缩: + + ```bash + wget https://nginx.org/download/nginx-1.13.1.tar.gz && tar zxvf nginx-1.13.1.tar.gz + ``` ++ 下载NGINX依赖项的源代码并解压缩 + + > NGINX依赖于3个库:PCRE,zlib和OpenSSL: + + ```bash + # PCRE version 4.4 - 8.40 + wget https://ftp.pcre.org/pub/pcre/pcre-8.40.tar.gz && tar xzvf pcre-8.40.tar.gz + + # zlib version 1.1.3 - 1.2.11 + wget http://www.zlib.net/zlib-1.2.11.tar.gz && tar xzvf zlib-1.2.11.tar.gz + + # OpenSSL version 1.0.2 - 1.1.0 + wget https://www.openssl.org/source/openssl-1.1.0f.tar.gz && tar xzvf openssl-1.1.0f.tar.gz + ``` + ++ 删除所有.tar.gz文件。我们不再需要了 + + ```bash + wget https://nginx.org/download/nginx-1.13.1.tar.gz && tar zxvf nginx-1.13.1.tar.gz + ``` + ++ 转到NGINX源目录:`` + + cd ~/nginx-1.13.1 + ++ 有关帮助,您可以通过运行以下列出可用的配置开关 + + ./configure --help + ++ 配置,编译和安装NGINX: + + ./configure --prefix=/usr/share/nginx \ + --sbin-path=/usr/sbin/nginx \ + --modules-path=/usr/lib/nginx/modules \ + --conf-path=/etc/nginx/nginx.conf \ + --error-log-path=/var/log/nginx/error.log \ + --http-log-path=/var/log/nginx/access.log \ + --pid-path=/run/nginx.pid \ + --lock-path=/var/lock/nginx.lock \ + --user=www-data \ + --group=www-data \ + --build=Ubuntu \ + --http-client-body-temp-path=/var/lib/nginx/body \ + --http-fastcgi-temp-path=/var/lib/nginx/fastcgi \ + --http-proxy-temp-path=/var/lib/nginx/proxy \ + --http-scgi-temp-path=/var/lib/nginx/scgi \ + --http-uwsgi-temp-path=/var/lib/nginx/uwsgi \ + --with-openssl=../openssl-1.1.0f \ + --with-openssl-opt=enable-ec_nistp_64_gcc_128 \ + --with-openssl-opt=no-nextprotoneg \ + --with-openssl-opt=no-weak-ssl-ciphers \ + --with-openssl-opt=no-ssl3 \ + --with-pcre=../pcre-8.40 \ + --with-pcre-jit \ + --with-zlib=../zlib-1.2.11 \ + --with-compat \ + --with-file-aio \ + --with-threads \ + --with-http_addition_module \ + --with-http_auth_request_module \ + --with-http_dav_module \ + --with-http_flv_module \ + --with-http_gunzip_module \ + --with-http_gzip_static_module \ + --with-http_mp4_module \ + --with-http_random_index_module \ + --with-http_realip_module \ + --with-http_slice_module \ + --with-http_ssl_module \ + --with-http_sub_module \ + --with-http_stub_status_module \ + --with-http_v2_module \ + --with-http_secure_link_module \ + --with-mail \ + --with-mail_ssl_module \ + --with-stream \ + --with-stream_realip_module \ + --with-stream_ssl_module \ + --with-stream_ssl_preread_module \ + --with-debug \ + --with-cc-opt='-g -O2 -fPIE -fstack-protector-strong -Wformat -Werror=format-security + -Wdate-time -D_FORTIFY_SOURCE=2' \ + --with-ld-opt='-Wl,-Bsymbolic-functions -fPIE -pie -Wl,-z,relro -Wl,-z,now' + make + sudo make install + ++ 从主目录中删除所有下载的文件,在这种情况下/home/username: + + cd ~ + rm -r nginx-1.13.1/ openssl-1.1.0f/ pcre-8.40/ zlib-1.2.11/ + ++ 检查NGINX版本和编译时间选项: + + sudo nginx -v && sudo nginx -V + + # nginx version: nginx/1.13.0 (Ubuntu) + # built by gcc 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4) + # built with OpenSSL 1.1.0f 25 May 2017 + # TLS SNI support enabled + # configure arguments: --prefix=/etc/nginx . . . + # . . . + # . . . + ++ 检查语法和潜在错误: + + sudo nginx -t + # Will throw this error nginx: [emerg] mkdir() "/var/lib/nginx/body" failed (2: No such file or directory) + # Just create directory + mkdir -p /var/lib/nginx && sudo nginx -t + ++ 为NGINX创建systemd单元文件: + + sudo vim /etc/systemd/system/nginx.service + ++ 复制/粘贴以下内容: + > 注意:根据NGINX的编译方式,PID文件和NGINX二进制文件的位置可能不同。 + + [Unit] + Description=A high performance web server and a reverse proxy server + After=network.target + + [Service] + Type=forking + PIDFile=/run/nginx.pid + ExecStartPre=/usr/sbin/nginx -t -q -g 'daemon on; master_process on;' + ExecStart=/usr/sbin/nginx -g 'daemon on; master_process on;' + ExecReload=/usr/sbin/nginx -g 'daemon on; master_process on;' -s reload + ExecStop=-/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/nginx.pid + TimeoutStopSec=5 + KillMode=mixed + + [Install] + WantedBy=multi-user.target + ++ 启动并启用NGINX服务: + + sudo systemctl start nginx.service && sudo systemctl enable nginx.service + ++ 检查NGINX是否在重启后启动: + + sudo systemctl is-enabled nginx.service + # enabled + ++ 检查NGINX是否正在运行: + + sudo systemctl status nginx.service + ps aux | grep nginx + curl -I 127.0.0.1 + ++ 重新启动Ubuntu VPS以验证NGINX是否自动启动: + + sudo shutdown -r now + ++ 创建UFW NGINX应用程序配置文件: + + sudo vim /etc/ufw/applications.d/nginx + ++ 复制/粘贴以下内容: + + [Nginx HTTP] + title=Web Server (Nginx, HTTP) + description=Small, but very powerful and efficient web server + ports=80/tcp + + [Nginx HTTPS] + title=Web Server (Nginx, HTTPS) + description=Small, but very powerful and efficient web server + ports=443/tcp + + [Nginx Full] + title=Web Server (Nginx, HTTP + HTTPS) + description=Small, but very powerful and efficient web server + ports=80,443/tcp + ++ 现在,验证UFW应用配置文件是否被创建和识别: + sudo ufw app list + + # Available applications: + # Nginx Full + # Nginx HTTP + # Nginx HTTPS + # OpenSSH + +### Build + +cd to NGINX source directory & run this: + + ./configure --add-module=/path/to/nginx-rtmp-module + make + make install diff --git a/docs/Nginx/nginx-parameter-config.md b/docs/Nginx/nginx-parameter-config.md new file mode 100644 index 0000000..af639aa --- /dev/null +++ b/docs/Nginx/nginx-parameter-config.md @@ -0,0 +1,460 @@ +# 高并发系统内核优化 +--- +## [Socket优化](#Socket) + +* Nginx + +* 系统内核 + +## [文件优化](#file) + +* Nginx + +* 系统内核 + +## [配置文件优化](#config-file) + +* Nginx配置文件 + +* 内核配置文件 + +* PHP7配置文件 + +* PHP-FPM配置文件 + +# Socket优化 +## Nginx + +#### 子进程允许打开的连接数: + +```bash +worker_connections +``` +## 系统内核 +#### [内核参数的优化](http://blog.csdn.net/moxiaomomo/article/details/19442737) + ++ 实践优化配置 + + 编辑: `vim /etc/sysctl.conf` + + 配置结果 + + ```bash + net.ipv4.tcp_max_tw_buckets = 6000 + net.ipv4.ip_local_port_range = 1024 65000 + net.ipv4.tcp_tw_recycle = 1 + net.ipv4.tcp_tw_reuse = 1 + net.ipv4.tcp_syncookies = 1 + net.core.somaxconn = 262144 + net.core.netdev_max_backlog = 262144 + net.ipv4.tcp_max_orphans = 262144 + net.ipv4.tcp_max_syn_backlog = 262144 + net.ipv4.tcp_syn_retries = 1 + net.ipv4.tcp_fin_timeout = 1 + net.ipv4.tcp_keepalive_time = 30 + ``` + + 执行命令使之生效:`/sbin/sysctl -p` +# 文件优化 + +## Nginx + +* 指当一个nginx进程打开的最多文件描述符数目:`worker_rlimit_nofile 100000;` + +## 系统内核 + ++ 系统限制其最大进程数:`ulimit -n` + ++ 编辑文件:`/etc/security/limits.conf` + + ```conf + # End of file + root soft nofile 65535 + root hard nofile 65535 + * soft nofile 65535 + * hard nofile 65535 + ``` +## 配置文件优化 + ++ Nginx配置文件 + + ```lua + user www www; + worker_processes 8; + worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000; + error_log /www/log/nginx_error.log crit; + pid /usr/local/nginx/nginx.pid; + worker_rlimit_nofile 204800; + + events + { + use epoll; + worker_connections 204800; + } + + http + { + include mime.types; + default_type application/octet-stream; + + charset utf-8; + + server_names_hash_bucket_size 128; + client_header_buffer_size 2k; + large_client_header_buffers 4 4k; + client_max_body_size 8m; + + sendfile on; + tcp_nopush on; + + keepalive_timeout 60; + + fastcgi_cache_path /usr/local/nginx/fastcgi_cache levels=1:2 + keys_zone=TEST:10m + inactive=5m; + fastcgi_connect_timeout 300; + fastcgi_send_timeout 300; + fastcgi_read_timeout 300; + fastcgi_buffer_size 64k; + fastcgi_buffers 8 64k; + fastcgi_busy_buffers_size 128k; + fastcgi_temp_file_write_size 128k; + fastcgi_cache TEST; + fastcgi_cache_valid 200 302 1h; + fastcgi_cache_valid 301 1d; + fastcgi_cache_valid any 1m; + fastcgi_cache_min_uses 1; + fastcgi_cache_use_stale error timeout invalid_header http_500; + + open_file_cache max=204800 inactive=20s; + open_file_cache_min_uses 1; + open_file_cache_valid 30s; + tcp_nodelay on; + + #gzip on; + gzip on; + gzip_min_length 1k; + gzip_buffes 16 64k; + gzip_http_version 1.1; + gzip_comp_level 6; + gzip_types text/plain application/x-javascript text/css application/javascript text/javascript image/jpeg image/gif image/png application/xml application/json; + gzip_vary on; + gzip_disable "MSIE [1-6].(?!.*SV1)"; + + index index.php index.html index.htm; + + server + { + listen 8080; + server_name backup.aiju.com; + root /www/html/; #这里的位置很重要,不要写在其它指令里面,我曾经就调试了好久才发现这个问题的 + + location /status + { + stub_status on; + } + + location ~ .*\.(html|htm|gif|jpg|jpeg|bmp|png|ico|txt|js|css)$ { + #root /home/www/sansan-web/public; + expires 3d; + } + + location ~ ^/(status|ping)$ + { + include fastcgi_params; + fastcgi_pass unix:/var/run/php7.0.22-fpm.sock; + fastcgi_param SCRIPT_FILENAME $fastcgi_script_name; + } + + location = /favicon.ico { + access_log off; + } + + error_page 400 401 402 403 404 /40x.html; + #location = /40x.html { + # root html; + #} + + error_page 500 501 502 503 504 /50x.html; + location = /50x.html { + root html; + } + + # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 + location ~ \.php$ { + fastcgi_pass unix:/var/run/php7.0.22-fpm.sock; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; + fastcgi_buffer_size 128k; + fastcgi_buffers 4 256k; + fastcgi_busy_buffers_size 256k; + fastcgi_connect_timeout 300; + fastcgi_send_timeout 300; + fastcgi_read_timeout 300; + } + + location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|js|css)$ + { + expires 30d; + } + + log_format access '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" $http_x_forwarded_for'; + access_log /www/log/access.log access; + } + } + ``` ++ 完整的内核优化配置 + + ```lua + net.ipv4.ip_forward = 0 + net.ipv4.conf.default.rp_filter = 1 + net.ipv4.conf.default.accept_source_route = 0 + kernel.sysrq = 0 + kernel.core_uses_pid = 1 + net.ipv4.tcp_syncookies = 1 + kernel.msgmnb = 65536 + kernel.msgmax = 65536 + kernel.shmmax = 68719476736 + kernel.shmall = 4294967296 + net.ipv4.tcp_max_tw_buckets = 6000 + net.ipv4.tcp_sack = 1 + net.ipv4.tcp_window_scaling = 1 + net.ipv4.tcp_rmem = 4096 87380 4194304 + net.ipv4.tcp_wmem = 4096 16384 4194304 + net.core.wmem_default = 8388608 + net.core.rmem_default = 8388608 + net.core.rmem_max = 16777216 + net.core.wmem_max = 16777216 + net.core.netdev_max_backlog = 262144 + net.core.somaxconn = 262144 + net.ipv4.tcp_max_orphans = 3276800 + net.ipv4.tcp_max_syn_backlog = 262144 + net.ipv4.tcp_timestamps = 0 + net.ipv4.tcp_synack_retries = 1 + net.ipv4.tcp_syn_retries = 1 + net.ipv4.tcp_tw_recycle = 1 + net.ipv4.tcp_tw_reuse = 1 + net.ipv4.tcp_mem = 94500000 915000000 927000000 + net.ipv4.tcp_fin_timeout = 1 + net.ipv4.tcp_keepalive_time = 30 + net.ipv4.ip_local_port_range = 1024 65000 + ``` +## PHP.ini配置文件优化(PHP7) + ++ 启用Zend Opcache,php.ini配置文件中加入 + + ```bash + opcache.enable=1 + zend_extension=opcache.so + opcache.memory_consumption=128 + opcache.interned_strings_buffer=8 + opcache.max_accelerated_files=4000 + opcache.revalidate_freq=60 + opcache.fast_shutdown=1 + opcache.enable_cli=1 + opcache.huge_code_pages=1 + opcache.file_cache=/tmp + ``` ++ 缓存文件记录 + + ```bash + www@TinywanAliYun:/tmp$ tree -L 6 + . + ├── 8fc9c56d14b6542c6ff7147207730f6b + │   └── home + │   └── www + │   └── web + │   └── go-study-line + │   ├── application + │   ├── config + │   ├── public + │   ├── runtime + │   ├── thinkphp + │   └── vendor + ``` ++ 使用新的编译器,使用新一点的编译器, 推荐GCC 4.8以上, 因为只有GCC 4.8以上PHP才会开启Global Register for opline and execute_data支持, 这个会带来5%左右的性能提升 + ++ 开启HugePages,然后开启Opcache的huge_code_pages + + + 系统中开启HugePages + + ```bash + sudo sysctl vm.nr_hugepages=512 + ``` + + 分配512个预留的大页内存 + + ```bash + $ cat /proc/meminfo | grep Huge + AnonHugePages: 106496 kB + HugePages_Total: 512 + HugePages_Free: 504 + HugePages_Rsvd: 27 + HugePages_Surp: 0 + Hugepagesize: 2048 kB + ``` + + 然后在php.ini中加入,`opcache.huge_code_pages=1` + + + 开启Opcache File Cache,`opcache.file_cache=/tmp` + + + 启用Zend Opcache + + +## PHP-FPM优化 + ++ 结构 + + ```bash + +---> php.ini PHP配置文件 + | + PHP-->|---> php-fpm 服务控制脚本 + +---> php-fpm.conf 进程服务主配置文件 + | + +---> www.conf 进程服务扩展配置文件 + ``` ++ `php.ini` + + ```php + # 设置错误日志的路径 + error_log = /var/log/php-fpm/error.log + + # 引入www.conf文件中的配置 + include=/usr/local/php7/etc/php-fpm.d/*.conf + + # 设置主进程打开的最大文件数 + rlimit_files = 102400 + ``` + ++ `php-fpm.conf` 进程服务主配置文件 + + ```php + pid = run/php-fpm.pid + # 设置错误日志的路径 + error_log = /var/log/php-fpm/error.log + + # 引入www.conf文件中的配置 + include=/usr/local/php7/etc/php-fpm.d/*.conf + + # 设置主进程打开的最大文件数 + rlimit_files = 65535 + ``` + ++ `www.conf` 进程服务扩展配置文件 + + ```php + # 设置启动进程的帐户和组 + user = www + group = www + + # 设置php监听方式,注意这里要设置PHP套接字文件的权限,默认是root,Nginx无法访问 + # listen = 127.0.0.1:9000 + listen = /var/run/php-fpm/php-fpm.sock + + #backlog数,-1表示无限制,由操作系统决定,此行注释掉就行。backlog含义参考:http://www.3gyou.cc/?p=41 + + listen.allowed_clients = 127.0.0.1 + # 允许访问FastCGI进程的IP,设置any为不限制IP,如果要设置其他主机的nginx也能访问这台FPM进程, + # listen处要设置成本地可被访问的IP。默认值是any。每个地址是用逗号分隔. + # 如果没有设置或者为空,则允许任何服务器请求连接 + + #backlog数,-1表示无限制,由操作系统决定,此行注释掉就行。backlog含义参考:http://www.3gyou.cc/?p=41 + listen.backlog = 4096 + + # unix socket设置选项,如果使用tcp方式访问,这里注释即可。 + listen.owner = www + listen.group = www + listen.mode = 0660 + + # 开启慢日志 + slowlog = /var/log/php-fpm/php-slow.log + request_slowlog_timeout = 10s + request_terminate_timeout = 30 + + #对于专用服务器,pm可以设置为static。 + pm = dynamic + + # 设置工作进程数(根据实际情况设置) + pm.max_children = 50 + + # pm.start_servers不能小于pm.min_spare_servers,推荐为最大的pm.max_children的%10 + pm.start_servers = 8 + pm.min_spare_servers = 5 + pm.max_spare_servers = 10 + pm.max_requests = 10240 + + # cat /usr/local/php-5.5.10/etc/php-fpm.conf | grep status_path + pm.status_path = /status + + # 设置扩展配置主进程打开的最大文件数 + rlimit_files = 65535 + + # 设置php的session目录(所属用户和用户组都是www) + php_value[session.save_handler] = files + php_value[session.save_path] = /var/tmp/php/session + ``` + ++ 调整PHP-FPM(Nginx)的子进程 + + 日志中出现以下警告消息,这意味着没有足够的PHP-FPM进程 + + ```php + [19-Aug-2017 01:02:20] WARNING: [pool www] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers) + [19-Aug-2017 01:02:21] WARNING: [pool www] server reached pm.max_children setting (256), consider raising it + ``` + + + 根据系统内存量来计算和更改这些值,` /etc/php-fpm.d/www.conf` + + ```php + pm.max_children = 50 + pm.start_servers = 5 + pm.min_spare_servers = 5 + pm.max_spare_servers = 35 + ``` + + + 以下命令将帮助我们确定每个(PHP-FPM)子进程使用的内存: + + > RSS列显示PHP-FPM进程的未交换的物理内存使用量,单位为千字节 + > 平均每个PHP-FPM进程在我的机器上占用大约75MB的RAM + + ```php + ps -ylC php-fpm --sort:rss + ``` + + + pm.max_children的适当值可以计算为: + + ```php + pm.max_children = Total RAM dedicated to the web server / Max child process size + ``` + + + 在我的情况下是56MB,服务器有16GB的RAM,所以: + + >我留下了一些记忆,让系统呼吸。在计算内存使用情况时,您需要考虑计算机上运行的任何其他服务。 + + ```php + pm.max_children = 15806MB / 56MB = 282 + # Tinywan 计算方式(实战) + # pm.max_children = (15806MB - 1024MB) / 57MB = 259 + ``` + + + 已经改变了如下设置 + >请注意,非常高的价值并不意味着任何好处 + + ```php + pm.max_children = 256 + pm.start_servers = 32 + pm.min_spare_servers = 32 + pm.max_spare_servers = 128 + pm.max_requests = 65535 + ``` + + + 您可以使用此方便的命令检查单个PHP-FPM进程的平均内存使用情况 + + ```php + ps --no-headers -o "rss,cmd" -C php-fpm | awk '{ sum+=$1 } END { printf ("%d%s\n", sum/NR/1024,"M") }' + ``` + +## HELP + ++ [php-fpm - 启动参数及重要配置详解](http://www.4wei.cn/archives/1002061) ++ [php-fpm backlog参数潜在问题](http://blog.csdn.net/willas/article/details/11634825) ++ [Adjusting child processes for PHP-FPM (Nginx)](https://myshell.co.uk/blog/2012/07/adjusting-child-processes-for-php-fpm-nginx/) + ++ [Nginx的worker_processes优化](http://blog.chinaunix.net/uid-26000296-id-3987521.html) + diff --git a/docs/Nginx/nginx-phases.md b/docs/Nginx/nginx-phases.md new file mode 100644 index 0000000..9ccc952 --- /dev/null +++ b/docs/Nginx/nginx-phases.md @@ -0,0 +1,155 @@ +## nginx的11个phases ++ 一个请求经过nginx处理的过程中,会经过一系列的阶段(phases),下面这个表格列出了nginx的所有phases,每个阶段可选的退出方式,包含的模块和对应的指令 + + | Phases | modules / directives | description | + | :------------ |:---------------:| -----:| + | NGX_HTTP_POST_READ_PHASE | HttpRealIpModule | 读取请求内容阶段 | + | NGX_HTTP_SERVER_REWRITE_PHASE
      Location ( server rewrite ) | HttpRewriteModule
      rewrite | 请求地址重写阶段| + | NGX_HTTP_FIND_CONFIG_PHASE
      Location ( location selection ) | HttpCoreModule
      location |配置查找阶段| + | NGX_HTTP_REWRITE_PHASE
      Location ( location rewrite ) | HttpLuaModule
      set_by_lua、rewrite_by_lua |请求地址重写阶段| + | NGX_HTTP_POST_REWRITE_PHASE | 不注册其他模块 |请求地址重写提交阶段| + | NGX_HTTP_PREACCESS_PHASE
      ( location selection ) | degradation
      NginxHttpLimitZoneModule / limit_zone
      HttpLimitReqModule / limit req |访问权限检查准备阶段| + | NGX_HTTP_ACCESS_PHASE | HttpAccessModule
      allow, deny
      NginxHttpAuthBasicModule
      HttpLuaModule
      access_by_lua |访问权限检查阶段| + | NGX_HTTP_POST_ACCESS_PHASE | 该指令可以用于控制access阶段的指令彼此之间的协作方式 |访问权限检查提交阶段| + | NGX_HTTP_TRY_FILES_PHASE | HttpCoreModule
      try_files |配置项try_files处理阶段| + | NGX_HTTP_CONTENT_PHASE | HttpProxyModule / proxy
      HttpLuaModule / content_by_lua
      HttpCoreModule / proxy_pass
      HttpFcgiModule / FastCGI |内容产生阶段| + | NGX_HTTP_TRY_FILES_PHASE | HttpLogModuel / access_log |日志模块处理阶段| + ++ 各个phase说明 + + (1) post read phase + > `post-read ` 属于 `rewrite`阶段 + + > `post-read` 支持Nginx模块的钩子 + + > 内置模块 `ngx_realip` 把它的处理程序`post-read`分阶段挂起,强制重写请求的原始地址作为特定请求头的值 + + ``` + server { + listen 8080; + + set_real_ip_from 127.0.0.1; + real_ip_header X-My-IP; + + location /test { + set $addr $remote_addr; + echo "from: $addr"; + } + } + ``` + + > 该配置告诉Nginx强制将每个请求的原始地址重写127.0.0.1为请求头的值X-My-IP。同时它使用内置变量 `$remote_addr`来输出请求的原始地址 + + ``` + $ curl -H 'X-My-IP: 1.2.3.4' localhost:8080/test + from: 1.2.3.4 + ``` + > curl 参数 -H :自定义头信息传递给服务器 + + > 该选项X-My-IP: 1.2.3.4在请求中包含一个额外的HTTP头 + + > 测试结果 + ``` + $ curl localhost:8080/test + from: 127.0.0.1 + + $ curl -H 'X-My-IP: abc' localhost:8080/test + from: 127.0.0.1 + `` + + + server_rewrite phase + + > 这个阶段主要进行初始化全局变量,或者server级别的重写。如果把重写指令放到 server 中,那么就进入了server rewrite 阶段。(重写指令见rewrite phase) + + > ( 1 ) `server-rewrite ` 阶段运行时间早于 `rewrite` 阶段 + + ``` + location /tinywan { + set $bbb "$aaa, world"; + echo $bbb; + } + set $aaa "HELLO"; + ``` + + > `set $a hello` 声明被放在`server`指令中,所以它运行在`server-rewrite`阶段 + + > 因此 `set $b "$a, world'" `,在location指令中执行 `set ` 指令后,它将获得正确的`$a`值 + + > 执行结果 + + ``` + # curl http://127.0.0.2:8008/tinywan + HELLO, world + ``` + > ( 2 ) `post-read` 阶段阶段运行时间早于 `server-rewrite` 阶段执行 + + ``` + server { + listen 8080; + + set $addr $remote_addr; + + set_real_ip_from 127.0.0.1; + real_ip_header X-Real-IP; + + location /test { + echo "from: $addr"; + } + } + ``` + + > ( 3 ) `ngx_realip` 阶段阶段运行时间早于 `server 的 set 指令` 阶段执行 + + ``` + $ curl -H 'X-Real-IP: 1.2.3.4' localhost:8080/test + from: 1.2.3.4 + ``` + + > 服务器指令中的命令集始终比模块ngx_realip晚, + + > ( 4 ) `server-rewrite` 阶段阶段运行时间早于 `find-config` 阶段执行 + + + find config phase + > ( 5 ) `find-config` 阶段阶段运行时间早于 `rewrite` 阶段执行 + + > 这个阶段使用重写之后的uri来查找对应的location,值得注意的是该阶段可能会被执行多次,因为也可能有location级别的重写指令。这个阶段并不支持 Nginx 模块注册处理程序,而是由 Nginx 核心来完成当前请求与 location 配置块之间的配对工作 + + + rewrite phase: + > 如果把重写指令放到 location中,那么就进入了rewrite phase,这个阶段是location级别的uri重写阶段,重写指令也可能会被执行多次 + + > 有`HttpRewriteModule` 的set指令、rewrite指令 + + > HttpLuaModule的 set_by_lua指令, + + > ngx_set_misc模块的set_unescape_uri指令 + + > 另外HttpRewriteModule的几乎所有指令都属于rewrite阶段。 ++ 结论:作用域为同一个phase的不同modules的指令,如果modules之间做了特殊的兼容,则它们按照指令在配置文件中出现的顺序依次执行下来 ++ HttpLuaModule 模块指令 + + init_by_lua + > 在nginx重新加载配置文件时,运行里面lua脚本,常用于全局变量的申请。例如lua_shared_dict共享内存的申请,只有当nginx重起后,共享内存数据才清空,这常用于统计。 + + + set_by_lua + > 设置一个变量,常用与计算一个逻辑,然后返回结果,该阶段不能运行Output API、Control API、Subrequest API、Cosocket API + + + rewrite_by_lua + > 在access阶段前运行,主要用于rewrite + + + access_by_lua + > 主要用于访问控制,能收集到大部分变量,类似status需要在log阶段才有。这条指令运行于nginx access阶段的末尾,因此总是在 allow 和 deny 这样的指令之后运行,虽然它们同属 access 阶段。 + + + content_by_lua + > 阶段是所有请求处理阶段中最为重要的一个,运行在这个阶段的配置指令一般都肩负着生成内容(content)并输出HTTP响应。 + + + header_filter_by_lua + > 一般只用于设置Cookie和Headers等,该阶段不能运行Output API、Control API、Subrequest API、Cosocket API + + + body_filter_by_lua + > 一般会在一次请求中被调用多次, 因为这是实现基于 HTTP 1.1 chunked 编码的所谓“流式输出”的,该阶段不能运行Output API、Control API、Subrequest API、Cosocket API + + + log_by_lua + > 该阶段总是运行在请求结束的时候,用于请求的后续操作,如在共享内存中进行统计数据,如果要高精确的数据统计,应该使用body_filter_by_lua + ++ --with-http_realip_module 模块 + + set_real_ip_from   192.168.1.0/24;     指定接收来自哪个前端发送的 IP head 可以是单个IP或者IP段 + + set_real_ip_from 192.168.2.1; + + real_ip_header X-Real-IP; IP head 的对应参数,默认即可。 \ No newline at end of file diff --git a/docs/Nginx/nginx-start-script.md b/docs/Nginx/nginx-start-script.md new file mode 100644 index 0000000..d667548 --- /dev/null +++ b/docs/Nginx/nginx-start-script.md @@ -0,0 +1,757 @@ +## 服务启动、停止和重启脚本 ++ [Ubuntu 14.04.2 LTS 启动脚本](#Ubuntu14) + + PHP-FPM 服务 + + Nginx 服务 ++ [Ubuntu 16.04.2 LTS 启动脚本](#Ubuntu16) + + PHP-FPM 服务 + + Nginx 服务 +##
      Ubuntu 14.04.2 LTS 启动脚本 +### PHP-FPM 服务 ++ 下载文件[php-fpm.sh](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/PHP/PHP-FPM/php-fpm.sh) ++ 注意配置文件:`sudo vim /usr/local/php-7.2/etc/php-fpm.conf` + > 务必开启配置文件的pid路径:`pid = run/php-fpm.pid` +   否则会报错:`no pid file found - php-fpm is not running ?`  ++ CP到默认开启的服务脚本: + + ``` + sudo cp php-fpm.sh /etc/init.d/php-fpm + ``` ++ 给予权限: + + ``` + sudo chmod +x /etc/init.d/php-fpm + ``` ++ 使用`sysv-rc-conf`安装,[如何安装sysv-rc-conf管理服务](http://blog.csdn.net/gatieme/article/details/45251389) + + ![Markdown](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Images/nginx_start_script.png) ++ `php-fpm.sh`代码 + + ``` + #! /bin/sh + ### BEGIN INIT INFO + # Provides: php-fpm + # Required-Start: $remote_fs $network + # Required-Stop: $remote_fs $network + # Default-Start: 2 3 4 5 + # Default-Stop: 0 1 6 + # Short-Description: starts php-fpm + # Description: starts the PHP FastCGI Process Manager daemon + ### END INIT INFO + + prefix=/opt/php-7.0.9 # 只需要修改这里就可以里,这里是编译路径 + exec_prefix=${prefix} + + php_fpm_BIN=${exec_prefix}/sbin/php-fpm + php_fpm_CONF=${prefix}/etc/php-fpm.conf + php_fpm_PID=${prefix}/var/run/php-fpm.pid + + php_opts="--fpm-config $php_fpm_CONF --pid $php_fpm_PID" + + wait_for_pid () { + try=0 + + while test $try -lt 35 ; do + + case "$1" in + 'created') + if [ -f "$2" ] ; then + try='' + break + fi + ;; + + 'removed') + if [ ! -f "$2" ] ; then + try='' + break + fi + ;; + esac + + echo -n . + try=`expr $try + 1` + sleep 1 + + done + + } + case "$1" in + start) + echo -n "Starting PHP-FPM Server ... " + + $php_fpm_BIN --daemonize $php_opts + + if [ "$?" != 0 ] ; then + echo " failed" + exit 1 + fi + + wait_for_pid created $php_fpm_PID + + if [ -n "$try" ] ; then + echo " failed" + exit 1 + else + echo "[OK]" + fi + ;; + + stop) + echo -n "Stopping PHP-FPM Server ... " + + if [ ! -r $php_fpm_PID ] ; then + echo "warning, no pid file found - php-fpm is not running ?" + exit 1 + fi + + kill -QUIT `cat $php_fpm_PID` + + wait_for_pid removed $php_fpm_PID + + if [ -n "$try" ] ; then + echo " failed. Use force-quit" + exit 1 + else + echo " [OK]" + fi + ;; + + force-quit) + echo -n "Terminating PHP-FPM " + + if [ ! -r $php_fpm_PID ] ; then + echo "warning, no pid file found - php-fpm is not running ?" + exit 1 + fi + + kill -TERM `cat $php_fpm_PID` + + wait_for_pid removed $php_fpm_PID + + if [ -n "$try" ] ; then + echo " failed" + exit 1 + else + echo " [OK]" + fi + ;; + + restart) + $0 stop + $0 start + ;; + + reload) + + echo -n "Reload service php-fpm " + + if [ ! -r $php_fpm_PID ] ; then + echo "warning, no pid file found - php-fpm is not running ?" + exit 1 + fi + + kill -USR2 `cat $php_fpm_PID` + + echo "[OK]" + ;; + + *) + echo "Usage: $0 {start|stop|force-quit|restart|reload}" + exit 1 + ;; + + esac + ``` ++ 运行效果 + + ```bash + www@tinywan:~$ sudo service php-fpm restart + Stopping PHP-FPM Server ... [OK] + Starting PHP-FPM Server ... [OK] + ``` +### Nginx 服务 + ++ 第一种安装方式 + + 查看当前nginx是否已经在开机启动项里面: + + ```bash + ls /etc/rc* + ``` + + 如何安装 + + ```bash + #使用wget -O 下载并以不同的文件名保存 + sudo wget https://raw.github.com/JasonGiedymin/nginx-init-ubuntu/master/nginx -O /etc/init.d/nginx + # 给与权限 + sudo chmod +x /etc/init.d/nginx + # 设置为启动项 + sudo update-rc.d nginx defaults + ``` + +   需要修改的地方: + +   1、`NGINXPATH=${NGINXPATH:-/opt/openresty/nginx}` 修改为自己的路径 + +   2、`PIDSPATH=${PIDSPATH:-$NGINXPATH/logs}` pid文件路径 + > 如果在配置文件修改为:`pid /run/nginx.pid;` +         PIDSPATH=${PIDSPATH:-$NGINXPATH/logs}修改为:PIDSPATH="/run" ++   第二种安装方式 + + 和PHP-FPM一样,`nginx.sh`代码 + + ```bash + #! /bin/sh + ### BEGIN INIT INFO + # Provides: nginx + # Required-Start: $remote_fs $syslog + # Required-Stop: $remote_fs $syslog + # Default-Start: 2 3 4 5 + # Default-Stop: 0 1 6 + # Short-Description: nginx init.d dash script for Ubuntu or other *nix. + # Description: nginx init.d dash script for Ubuntu or other *nix. + ### END INIT INFO + #------------------------------------------------------------------------------ + # nginx - this Debian Almquist shell (dash) script, starts and stops the nginx + # daemon for Ubuntu and other *nix releases. + # + # description: Nginx is an HTTP(S) server, HTTP(S) reverse \ + # proxy and IMAP/POP3 proxy server. This \ + # script will manage the initiation of the \ + # server and it's process state. + # + # processname: nginx + # config: /usr/local/nginx/conf/nginx.conf + # pidfile: /usr/local/nginx/logs/nginx.pid + # Provides: nginx + + #------------------------------------------------------------------------------ + # Functions + #------------------------------------------------------------------------------ + LSB_FUNC=/lib/lsb/init-functions + + # Test that init functions exists + test -r $LSB_FUNC || { + echo "$0: Cannot find $LSB_FUNC! Script exiting." 1>&2 + exit 5 + } + + . $LSB_FUNC + + #------------------------------------------------------------------------------ + # Consts + #------------------------------------------------------------------------------ + # Include nginx defaults if available + if [ -f /etc/default/nginx ]; then + . /etc/default/nginx + fi + + # Minimize path + PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin + + PS=${PS:-"nginx"} # process name + DESCRIPTION=${DESCRIPTION:-"Nginx Server..."} # process description + NGINXPATH=${NGINXPATH:-/opt/openresty/nginx} # root path where installed + DAEMON=${DAEMON:-$NGINXPATH/sbin/nginx} # path to daemon binary + NGINX_CONF_FILE=${NGINX_CONF_FILE:-$NGINXPATH/conf/nginx.conf} # config file path + PIDNAME=${PIDNAME:-"nginx"} # lets you do $PS-slave + PIDFILE=${PIDFILE:-$PIDNAME.pid} # pid file + PIDSPATH=${PIDSPATH:-$NGINXPATH/logs} # default pid location, you should change it + RUNAS=${RUNAS:-root} # user to run as + SCRIPT_OK=0 # ala error codes + SCRIPT_ERROR=1 # ala error codes + TRUE=1 # boolean + FALSE=0 # boolean + + #------------------------------------------------------------------------------ + # Simple Tests + #------------------------------------------------------------------------------ + + # Test if nginx is a file and executable + test -x $DAEMON || { + echo "$0: You don't have permissions to execute nginx." 1>&2 + exit 4 + } + + # You can also set your conditions like so: + # set exit condition + # set -e + + #------------------------------------------------------------------------------ + # Functions + #------------------------------------------------------------------------------ + + setFilePerms(){ + if [ -f $PIDSPATH/$PIDFILE ]; then + chmod 400 $PIDSPATH/$PIDFILE + fi + } + + configtest() { + $DAEMON -t -c $NGINX_CONF_FILE + } + + getPSCount() { + return `pgrep -f $PS | wc -l` + } + + isRunning() { + if [ $1 ]; then + pidof_daemon $1 + PID=$? + + if [ $PID -gt 0 ]; then + return 1 + else + return 0 + fi + else + pidof_daemon + PID=$? + + if [ $PID -gt 0 ]; then + return 1 + else + return 0 + fi + fi + } + + #courtesy of php-fpm + wait_for_pid () { + try=0 + + while test $try -lt 35 ; do + case "$1" in + 'created') + if [ -f "$2" ]; then + try='' + break + fi + ;; + + 'removed') + if [ ! -f "$2" ]; then + try='' + break + fi + ;; + esac + + try=`expr $try + 1` + sleep 1 + done + } + + status(){ + isRunning + isAlive=$? + + if [ "${isAlive}" -eq $TRUE ]; then + log_warning_msg "$DESCRIPTION found running with processes: `pidof $PS`" + rc=0 + else + log_warning_msg "$DESCRIPTION is NOT running." + rc=3 + fi + + return + } + + removePIDFile(){ + if [ $1 ]; then + if [ -f $1 ]; then + rm -f $1 + fi + else + #Do default removal + if [ -f $PIDSPATH/$PIDFILE ]; then + rm -f $PIDSPATH/$PIDFILE + fi + fi + } + + start() { + log_daemon_msg "Starting $DESCRIPTION" + + isRunning + isAlive=$? + + if [ "${isAlive}" -eq $TRUE ]; then + log_end_msg $SCRIPT_ERROR + rc=0 + else + start-stop-daemon --start --quiet --chuid \ + $RUNAS --pidfile $PIDSPATH/$PIDFILE --exec $DAEMON \ + -- -c $NGINX_CONF_FILE + status=$? + setFilePerms + + if [ "${status}" -eq 0 ]; then + log_end_msg $SCRIPT_OK + rc=0 + else + log_end_msg $SCRIPT_ERROR + rc=7 + fi + fi + + return + } + + stop() { + log_daemon_msg "Stopping $DESCRIPTION" + + isRunning + isAlive=$? + + if [ "${isAlive}" -eq $TRUE ]; then + start-stop-daemon --stop --quiet --pidfile $PIDSPATH/$PIDFILE + + wait_for_pid 'removed' $PIDSPATH/$PIDFILE + + if [ -n "$try" ]; then + log_end_msg $SCRIPT_ERROR + rc=0 # lsb states 1, but under status it is 2 (which is more prescriptive). Deferring to standard. + else + removePIDFile + log_end_msg $SCRIPT_OK + rc=0 + fi + else + log_end_msg $SCRIPT_ERROR + rc=7 + fi + + return + } + + reload() { + configtest || return $? + + log_daemon_msg "Reloading (via HUP) $DESCRIPTION" + + isRunning + + if [ $? -eq $TRUE ]; then + kill -HUP `cat $PIDSPATH/$PIDFILE` + log_end_msg $SCRIPT_OK + rc=0 + else + log_end_msg $SCRIPT_ERROR + rc=7 + fi + + return + } + + quietupgrade() { + log_daemon_msg "Peforming Quiet Upgrade $DESCRIPTION" + + isRunning + isAlive=$? + + if [ "${isAlive}" -eq $TRUE ]; then + kill -USR2 `cat $PIDSPATH/$PIDFILE` + kill -WINCH `cat $PIDSPATH/$PIDFILE.oldbin` + + isRunning + isAlive=$? + + if [ "${isAlive}" -eq $TRUE ]; then + kill -QUIT `cat $PIDSPATH/$PIDFILE.oldbin` + wait_for_pid 'removed' $PIDSPATH/$PIDFILE.oldbin + removePIDFile $PIDSPATH/$PIDFILE.oldbin + + log_end_msg $SCRIPT_OK + rc=0 + else + log_end_msg $SCRIPT_ERROR + + log_daemon_msg "ERROR! Reverting back to original $DESCRIPTION" + + kill -HUP `cat $PIDSPATH/$PIDFILE` + kill -TERM `cat $PIDSPATH/$PIDFILE.oldbin` + kill -QUIT `cat $PIDSPATH/$PIDFILE.oldbin` + + wait_for_pid 'removed' $PIDSPATH/$PIDFILE.oldbin + removePIDFile $PIDSPATH/$PIDFILE.oldbin + + log_end_msg $SCRIPT_OK + rc=0 + fi + else + log_end_msg $SCRIPT_ERROR + rc=7 + fi + + return + } + + terminate() { + log_daemon_msg "Force terminating (via KILL) $DESCRIPTION" + + PIDS=`pidof $PS` || true + + [ -e $PIDSPATH/$PIDFILE ] && PIDS2=`cat $PIDSPATH/$PIDFILE` + + for i in $PIDS; do + if [ "$i" = "$PIDS2" ]; then + kill $i + wait_for_pid 'removed' $PIDSPATH/$PIDFILE + removePIDFile + fi + done + + log_end_msg $SCRIPT_OK + rc=0 + } + + destroy() { + log_daemon_msg "Force terminating and may include self (via KILLALL) $DESCRIPTION" + killall $PS -q >> /dev/null 2>&1 + log_end_msg $SCRIPT_OK + rc=0 + } + + pidof_daemon() { + PIDS=`pidof $PS` || true + + [ -e $PIDSPATH/$PIDFILE ] && PIDS2=`cat $PIDSPATH/$PIDFILE` + + for i in $PIDS; do + if [ "$i" = "$PIDS2" ]; then + return 1 + fi + done + + return 0 + } + + action="$1" + case "$1" in + start) + start + ;; + stop) + stop + ;; + restart|force-reload) + stop + # if [ $rc -ne 0 ]; then + # script_exit + # fi + sleep 1 + start + ;; + reload) + $1 + ;; + status) + status + ;; + configtest) + $1 + ;; + quietupgrade) + $1 + ;; + terminate) + $1 + ;; + destroy) + $1 + ;; + *) + FULLPATH=/etc/init.d/$PS + echo "Usage: $FULLPATH {start|stop|restart|force-reload|reload|status|configtest|quietupgrade|terminate|destroy}" + echo " The 'destroy' command should only be used as a last resort." + exit 3 + ;; + esac + + exit $rc + ``` ++ 根据自己环境,配置文件路径,下面修改为Openresty下的Nginx启动项(Nginx 安装在/usr/local/openresty/目录下) + + ```bash + sudo vim /etc/init.d/nginx + NGINXPATH=${NGINXPATH:-/usr/local/openresty/nginx} + ``` ++ 开启服务 + + ```bash + sudo service nginx restart + [sudo] password for www: + * Stopping Nginx Server... [ OK ] + * Starting Nginx Server... [ OK ] + ``` +## Ubuntu 16.04.2 LTS 启动脚本 +### PHP-FPM 服务 ++ `php-fpm.sh`脚本代码 同上 ++ 注意,需要重新加载服务: + + ``` + sudo systemctl daemon-reload + ``` ++ 开启服务 + + ```bash + sudo systemctl start php-fpm.service + ``` ++ 停止服务 + + ```javascript + sudo systemctl stop php-fpm.service + ``` ++ 重启服务 + + ```javascript + sudo systemctl restart php-fpm.service + ``` ++ 服务状态 + + ```bash + sudo systemctl status php-fpm.service + ● php-fpm.service - LSB: starts php-fpm + Loaded: loaded (/etc/init.d/php-fpm; bad; vendor preset: enabled) + Active: active (running) since Sun 2017-10-22 11:16:06 CST; 1 day 5h ago + Docs: man:systemd-sysv-generator(8) + CGroup: /system.slice/php-fpm.service + ├─ 7670 php-fpm: pool www + ├─ 7711 php-fpm: pool www + ├─ 7752 php-fpm: pool www + └─18244 php-fpm: master process (/usr/local/php-7.1.8/etc/php-fpm.conf) + + Oct 22 11:16:06 TinywanAliYun php-fpm[18232]: Stopping PHP-FPM Server ... . [OK] + Oct 22 11:16:06 TinywanAliYun systemd[1]: Stopped LSB: starts php-fpm. + Oct 22 11:16:06 TinywanAliYun systemd[1]: Starting LSB: starts php-fpm... + Oct 22 11:16:06 TinywanAliYun php-fpm[18239]: Starting PHP-FPM Server ... [OK] + Oct 22 11:16:06 TinywanAliYun systemd[1]: Started LSB: starts php-fpm. + ``` +### Nginx 服务 ++ [Debian/Ubuntu Nginx init Script](http://kbeezie.com/debian-ubuntu-nginx-init-script/) + + > [1]通常情况下,如果你从存储库安装Nginx,这个初始化脚本已经包含在内。但是,如果您从源代码安装,或者没有使用标准路径,您可能需要这个。 + [2]如果发现停止/重新启动等不起作用,则您的pid文件位置可能不正确。您可以将其设置在nginx.conf中,也可以在此处更改init脚本以指向正确的pid位置 + ++ `nginx.sh`代码: + + ```javascript + #!/bin/sh + + ### BEGIN INIT INFO + # Provides: nginx + # Required-Start: $all + # Required-Stop: $all + # Default-Start: 2 3 4 5 + # Default-Stop: 0 1 6 + # Short-Description: starts the nginx web server + # Description: starts nginx using start-stop-daemon + ### END INIT INFO + + PATH=/opt/bin:/opt/sbin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin + DAEMON=/usr/local/openresty/nginx/sbin/nginx + NAME=nginx + DESC=nginx + + test -x $DAEMON || exit 0 + + # Include nginx defaults if available + if [ -f /etc/default/nginx ] ; then + . /etc/default/nginx + fi + + set -e + + case "$1" in + start) + echo -n "Starting $DESC: " + start-stop-daemon --start --quiet --pidfile /var/run/nginx.pid \ + --exec $DAEMON -- $DAEMON_OPTS + echo "$NAME." + ;; + stop) + echo -n "Stopping $DESC: " + start-stop-daemon --stop --quiet --pidfile /var/run/nginx.pid \ + --exec $DAEMON + echo "$NAME." + ;; + restart|force-reload) + echo -n "Restarting $DESC: " + start-stop-daemon --stop --quiet --pidfile \ + /var/run/nginx.pid --exec $DAEMON + sleep 1 + start-stop-daemon --start --quiet --pidfile \ + /var/run/nginx.pid --exec $DAEMON -- $DAEMON_OPTS + echo "$NAME." + ;; + reload) + echo -n "Reloading $DESC configuration: " + start-stop-daemon --stop --signal HUP --quiet --pidfile /var/run/nginx.pid \ + --exec $DAEMON + echo "$NAME." + ;; + *) + N=/etc/init.d/$NAME + echo "Usage: $N {start|stop|restart|force-reload}" >&2 + exit 1 + ;; + esac + + exit 0 + ``` ++ CP到默认开启的服务脚本: + + ``` + sudo cp nginx.sh /etc/init.d/nginx + ``` ++ 给予权限: + + ``` + sudo chmod +x /etc/init.d/nginx + ``` ++ 设置为开机启动项: + + ``` + sudo update-rc.d nginx defaults + ``` ++ 重新加载服务: + + ``` + sudo systemctl daemon-reload + ``` ++ 开启服务 + + ```javascript + sudo systemctl start nginx.service + ``` ++ 停止服务 + + ```javascript + sudo systemctl stop nginx.service + ``` ++ 重启服务 + + ```javascript + sudo systemctl restart nginx.service + ``` ++ 服务状态 + + ```bash + sudo systemctl status nginx.service + ● nginx.service + Loaded: loaded (/etc/init.d/nginx; bad; vendor preset: enabled) + Active: active (running) since Mon 2017-10-23 16:48:24 CST; 1min 28s ago + Docs: man:systemd-sysv-generator(8) + Process: 19089 ExecStop=/etc/init.d/nginx stop (code=exited, status=0/SUCCESS) + Process: 19138 ExecStart=/etc/init.d/nginx start (code=exited, status=0/SUCCESS) + CGroup: /system.slice/nginx.service + ├─19142 nginx: master process /usr/local/openresty/nginx/sbin/nginx -c /usr/local/openresty/nginx/conf/nginx.con + ├─19143 nginx: worker process + └─19144 nginx: cache manager process + + Oct 23 16:48:24 TinywanAliYun systemd[1]: Starting nginx.service... + Oct 23 16:48:24 TinywanAliYun nginx[19138]: Starting NGINX Web Server: ok + Oct 23 16:48:24 TinywanAliYun systemd[1]: Started nginx.service. + ``` ++ 参考文章: + + [Nginx官方参考](https://www.nginx.com/resources/wiki/start/topics/tutorials/solaris_11/#startup-script) + + [linux wget 命令用法详解(附实例说明)](http://www.jb51.net/LINUXjishu/86326.html) + + [理解Linux系统/etc/init.d目录和/etc/rc.local脚本](http://blog.csdn.net/acs713/article/details/7322082) + + [Ubuntu启动项设置——之update-rc.d 命令使用](http://blog.csdn.net/typ2004/article/details/38712887) diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 0000000..c419263 --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-cayman \ No newline at end of file diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..3acf873 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,114 @@ +![Author](https://img.shields.io/badge/Author-Tinywan-green.svg) +[![Conda](https://img.shields.io/conda/pn/conda-forge/python.svg)]() +[![GitHub license](https://img.shields.io/github/license/Tinywan/tinywan-react-app.svg)](https://github.com/Tinywan/tinywan-react-app/blob/master/LICENSE) +## 目录 + +#### Nginx 教程 (Nginx tutorial) + +* [Nginx编译安装](/Nginx/nginx-install.md) + +* [Nginx.conf详解和配置](/Nginx/nginx-base-config.md) + +* [location 详解](/Nginx/location-detail.md) + +* [Nginx基础知识](/Nginx/nginx-basic.md) + +* [Nginx高性能WEB服务器详解](/Nginx/nginx-high-basic.md) + +* [Nginx高并发系统内核优化和PHP7配置文件优化](/Nginx/nginx-parameter-config.md) + +* [Nginx和PHP-FPM启动脚本](/Nginx/nginx-start-script.md) + +* 项目案例 (Project notes) + + * [Nginx 同一个IP上配置多个HTTPS主机](/Nginx/more-domain-config.md) + + * [Nginx 如何配置一个安全的HTTPS网站服务器](http://www.cnblogs.com/tinywan/p/7542629.html) + + * [Nginx 配置启用 HTTP/2](http://www.cnblogs.com/tinywan/p/7860774.html) + +* 扩展模块 (Third-party module) + + * [nginx-vod-module](http://www.cnblogs.com/tinywan/p/7879559.html) + + * [nginx-module-vts](http://www.cnblogs.com/tinywan/p/7872366.html) + + * [ngx_cache_purge](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/Nginx-Web/Nginx-8-proxy_cache.md) + + * [lua-nginx-module](http://www.cnblogs.com/tinywan/p/6538006.html) + + * [nginx-rtmp-module](http://www.cnblogs.com/tinywan/p/6639360.html) + +#### Lua 教程 (Lua tutorial) + +* [Lua 基础语法](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Lua-Script/lua-basic.md) + +* [luajit 执行文件默认安装路径](#Nginx_base_knowledge) + +* [lua中self.__index = self 详解](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Lua-Script/oop/self__index.md) + +#### Redis 教程 (Redis tutorial) + +* [Redis 安装](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Redis/redis-install.md) + +* [Redis 配置详解](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Redis/redis-config.md) + +* [Redis 基础知识](#Redis_base_knowledge) + +* [Redis 开发与运维](#Redis-DevOps) + +* [Redis执行Lua脚本基本用法](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Redis/redis-lua.md) + +#### Openresty 教程 (Openresty tutorial) + ++ [安装默认配置信息](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Openresty/openresty-basic.md) + ++ [ngx_lua APi 方法和常量](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Openresty/openresty-api.md) + ++ [ngx_lua 扩展模块学习](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Openresty/openresty-resty-module.md) + ++ [lua-resty-upstream-healthcheck使用](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Openresty/lua-resty-upstream-healthcheck.md) + ++ [Openresty与Nginx_RTMP](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Openresty/openresty-rtmp.md) + ++ [自己写的一个简单项目lua_project_v0.01](https://github.com/Tinywan/lua_project_v0.01) + +#### PHP7 教程 (PHP7 tutorial) + ++ [PHP脚本运行Redis](#PHP_Run_Redis) + ++ [PHP7中php.ini/php-fpm/www.conf的配置,Nginx和PHP-FPM的开机自动启动脚本](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/PHP/PHP-FPM/config.md) + ++ [PHP 脚本执行一个Redis 订阅功能,用于监听键过期事件,返回一个回调,API接受改事件](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Redis-PHP/Php-Run-Redis-psubscribe/nohupRedisNotify.php) + +#### Linux 教程 (Linux tutorial) + ++ [Linux 基础知识](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Linux/linux-basic.md) + +#### Shell 教程 (Shell tutorial) + ++ [编写快速安全Bash脚本的建议](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Shell/write-shell-suggestions.md) + ++ [shell脚本实现分日志级别记录日志](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx-Rtmp/Shell_Log.sh) + ++ [Nginx日志定时备份和删除](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx-Rtmp/Shell_Nginx_Log_cut.sh) + ++ [SHELL脚本小技巧](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx-Rtmp/Shell_script.md) + ++ [Mysql 自动备份脚本安全加锁机制](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx-Rtmp/backup_mysql.sh) + +#### 流媒体教程 + ++ [Nginx配置Rtmp支持Hls的直播和点播功能](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx-Rtmp/HLS-live-vod.md) + ++ [HLS视频直播和点播的Nginx的Location的配置信息(成功)](https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx-Rtmp/HLS-live-vod-locatiuon-config.md) + +#### Ngx-Lua-Module + +![Markdown](/Images/Nginx-Phase.png) + +#### 打赏 + +|支付宝打赏|微信打赏| +|:----:|:----:| +|![image2](/Images/alipay.png)|![image1](/Images/wechat.png)| pFad - Phonifier reborn

      Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

      Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


      Alternative Proxies:

      Alternative Proxy

      pFad Proxy

      pFad v3 Proxy

      pFad v4 Proxy