:rose: 基于OpenResty编写一个MVC模式的WEB项目 V0.01
https://github.com/Tinywan/lua_project_v0.01/wiki
.
├── application -- 业务代码
│ ├── api
│ │ ├── live
│ │ └── users.lua
│ ├── controller -- 业务控制器
│ │ ├── live_redis_cache.lua
│ │ ├── upstream_backend.lua
│ │ └── web_socket_redis.lua
│ ├── model
│ │ └── Products.lua
│ ├── swoole
│ │ └── websocket-server.php
│ ├── waf
│ │ ├── init.lua
│ │ ├── install.sh
│ │ ├── wafconf
│ │ └── waf.lua
│ └── zabbix
│ └── install-record01.conf
├── bin -- 启动脚本
│ ├── doc2unix.sh
│ └── start.sh
├── conf -- 配置文件
│ ├── domains
│ │ └── nginx_live.conf
│ ├── fastcgi_params
│ ├── nginx.conf
│ ├── nginx.conf.default
│ └── nginx_lua_upstream.conf
├── logs -- 日志文件
│ ├── 123.txt
│ ├── error.log
│ └── nginx.pid
├── lualib -- 官方Lua库
│ ├── cjson.so
│ ├── item
│ │ ├── common.lua
│ │ └── items.lua
│ ├── live -- 自定义公共部分Lua文件
│ │ └── common.lua
│ ├── ngx
│ │ ├── ssl
│ │ └── ssl.lua
│ ├── rds
│ │ └── parser.so
│ ├── redis
│ │ └── parser.so
│ ├── resty
│ │ ├── http_headers.lua
│ │ ├── http.lua
│ │ ├── upstream
│ │ └── websocket
│ └── vendor -- 第三方Lua库
│ ├── config.lua
│ ├── dkjson.lua
│ ├── helper.lua
│ ├── ip_check.lua
│ ├── ip_location.lua
│ └── mysql_fun.lua
├── public -- 公共静态文件
│ ├── bootstrap
│ │ ├── css
│ │ ├── fonts
│ │ ├── images
│ │ └── js
│ ├── data
│ │ └── location_ip_db.dat
│ ├── images
│ │ ├── github
│ │ ├── tinywan_title.png
│ │ └── yinzhang.png
│ ├── video-js
│ │ ├── videodemo.png
│ │ ├── video.js
│ │ └── video-js.swf
│ └── websocket
│ └── public
├── README.md
└── template -- 静态模板
├── index
│ └── index.html
├── live
│ ├── 404.html
│ ├── crossdomain.xml
│ ├── index.html
│ ├── videodemo.png
│ └── wesocket.html
├── product
│ ├── footer.html
│ ├── header.html
│ ├── index2.html
│ ├── index.html
│ ├── item.html
│ └── product_list.html
├── waf
│ ├── index.html
│ └── waf.php
└── websocket
├── index.html
├── index.php
├── js
└── websocket.html
apt-get install libreadline-dev libncurses5-dev libpcre3-dev \
libssl-dev perl make build-essential
wget https://openresty.org/download/openresty-1.11.2.3.tar.gz
tar xvf openresty-1.11.2.1.tar.gz
cd openresty-1.11.2.1
./configure --prefix=/opt/openresty \
--with-luajit \
--without-http_redis2_module \
--with-http_iconv_module \
--with-http_postgres_module
make -j2
sudo make install
you need to have ldconfig in your PATH env when enabling luajit.
sudo apt-get install luajit
whereis luajit
luajit: /usr/bin/luajit /usr/share/man/man1/luajit.1.gz
./configure --prefix=/opt/openresty \
--with-luajit=/usr/bin/luajit \
--without-http_redis2_module \
--with-http_iconv_module \
--with-http_postgres_module
make -j2
sudo make install
user www www;
worker_processes 8;
pid logs/nginx.pid;
error_log log/error.log error;
worker_rlimit_nofile 204800;
events {
use epoll;
worker_connections 204800;
}
http {
include /opt/openresty/nginx/conf/mime.types;
default_type text/html;
charset utf-8;
lua_package_path "/mnt/hgfs/Linux-Share/Lua/lua_project_v0.01/lualib/?.lua;;"; #lua 模块
lua_package_cpath "/mnt/hgfs/Linux-Share/Lua/lua_project_v0.01/lualib/?.so;;"; #c模块
include "/mnt/hgfs/Linux-Share/Lua/lua_project_v0.01/conf/domains/*";
}
# 切换到 该目录,如果没有请重新建立
www@iZ238xopqw6Z:~$ pwd
/home/www
git clone -b v0.03 https://github.com/Tinywan/lua_project_v0.01.git
lua_project_v0.01/conf/nginx.conf
的路径,以下的/home/
修改为项目所在路径
lua_package_path "/home/lua_project_v0.01/lualib/?.lua;/home/lua_project_v0.01/application/controller/?.lua";#lua 模块
lua_package_cpath "/home/lua_project_v0.01/lualib/?.so;;"; # c模块
include "/home/lua_project_v0.01/conf/domains/*";
test.conf
set $project_path /home/;
access_log "/home/lua_project_v0.01/logs/demo_access.log";
chmod +x /start.sh
,/home/lua_project_v0.01/conf# ../bin/start.sh start
[ Stop OK ]
nginx: the configuration file /home/lua_project_v0.01/conf/nginx.conf syntax is ok
nginx: configuration file /home/lua_project_v0.01/conf/nginx.conf test is successful
[ Start OK ]
curl http://127.0.0.1/
,输出:Hello! lua_project_v0.01
,表示环境配置成功company
分支home
分支api.conf
nginx_demo.conf
nginx_live.conf
waf.conf
nginx_product.conf
[crit] 3478#0: *5 connect() to unix:/var/run/php7.0.9-fpm.sock failed (13: Permission denied)
listen.owner = nginx
listen.group = nginx
listen.mode = 0660
helper.http_args()
helper.split()
helper.ltrim() / rtrim() / trim()
helper.cjson_decode(str2)
local helper = require 'vendor.helper'
local str1 = '{"hobby":{"name":"tinywan","age":24},"is_male":false}' -- 写法1
local str2 = [[ {"hobby":{"name":"tinywan","age":24},"is_male:false} ]] -- 写法2
local obj_obj = helper.cjson_decode(str2)
if not json_str then
ngx.say('cjson_decode error')
else
ngx.say(helper.format_table(json_str))
end
local mysql = require 'vendor.mysql_fun'
local cjson = require 'cjson'
data = { name = "TinyWAN", address = "HeilongJiang", age = "26" }
local result = mysql.add(data.name,data.address,data.age)
ngx.print(cjson.encode(result))
local result = mysql.select(3)
ngx.print(cjson.encode(result))
local result = mysql.update(3,"TinTinAIAI")
ngx.print(cjson.encode(result))
local result = mysql.delete(3)
ngx.print(cjson.encode(result))
{
"error_code": 200,
"message": "add successfully"
}
{
"error_code": 504,
"message": "failed to delete"
}
lua entry thread aborted: runtime error: attempt to yield across C-call boundary
/vendor/mysql_fun.lua
curl: (52) Empty reply from server
connect_db()
方法就可以了
function _M.update(id, name)
connect_db()
end
bad result: Data too long for column 'name' at row 1: 1406: 22001.
,插入字段超过表定义大小php /mnt/hgfs/Linux-Share/Lua/lua_project_v0.01/application/swoole/websocket-server.php
http://127.0.0.1/clinet.html
start.sh
/mnt/hgfs/Linux-Share/Lua/lua_project_v0.01/bin/start.sh
start.sh
可以重新加载配置文件stop.sh
/mnt/hgfs/Linux-Share/Lua/lua_project_v0.01/bin/stop.sh
/bin/bash^M: 解释器错误: 没有那个文件或目录
sed -i 's/\r$//' ../lua_project_v0.01/bin/start.sh
ngx_lua_waf 安装使用(ngx_lua_waf是一个基于lua-nginx-module(openresty)的web应用防火墙)
application
下面,同时重命名waf
cp -R ngx_lua_waf /mnt/hgfs/Linux-Share/Lua/lua_project_v0.01/application/waf
config.lua
文件到../lualib/vendor
下init.lua
文件的第一行:require 'config'
修改为require 'vendor.config'
domains
目录下,新建文件waf.conf
,添加以下内容
lua_shared_dict limit 10m;
init_by_lua_file "/mnt/hgfs/Linux-Share/Lua/lua_project_v0.01/application/waf/init.lua";
access_by_lua_file "/mnt/hgfs/Linux-Share/Lua/lua_project_v0.01/application/waf/waf.lua";
server {
listen 8082;
server_name localhost;
index index.php index.html index.htm;
access_log /mnt/hgfs/Linux-Share/Lua/lua_project_v0.01/logs/waf_access.log;
error_log /mnt/hgfs/Linux-Share/Lua/lua_project_v0.01/logs/waf_error.log error;
set $web_root /mnt/hgfs/Linux-Share/Lua/lua_project_v0.01/template/waf;
root $web_root;
location / {
try_files $uri $uri/ /index.php?$args;
}
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;
}
}
tinywan@tinywan:~$ /mnt/hgfs/Linux-Share/Lua/lua_project_v0.01/bin/start.sh
[ Nginx running ]
nginx: the configuration file /mnt/hgfs/Linux-Share/Lua/lua_project_v0.01/conf/nginx.conf syntax is ok
nginx: configuration file /mnt/hgfs/Linux-Share/Lua/lua_project_v0.01/conf/nginx.conf test is successful
[ Nginx has reload ]
nginx: [emerg] open() "/mnt/hgfs/Linux-Share/Lua/lua_project_v0.01/conf/fastcgi_params" failed
解决:
cp fastcgi_params /mnt/hgfs/Linux-Share/Lua/lua_project_v0.01/conf/fastcgi_params
FastCGI sent in stderr: "Primary script unknown" while reading response header from upstream
解决:查看
waf.conf
配置信息是否正确
修改文件:config.lua
black_fileExt={"php","jsp"}
ipWhitelist={"127.0.0.1"}
--ipBlocklist={"1.0.0.1"}
ipBlocklist={"192.168.127.133"}
测试一:http://127.0.0.1:8082/waf.php?id=../etc/passwd&name=Tinywan
测试二:http://192.168.127.133:8082/waf.php?id=../etc/passwd&name=Tinywan
:poop: 坑 :poop: 在提交代码的时候waf
目录一直提交不了,提示:modified: xxx(modified content, untracked content)
,
原来在waf
目录下有个.git 目录,删除.git目录,重新git add 就可以了
配置文件:nginx_product.conf
#upstream
upstream item_http_upstream {
server 192.168.1.1 max_fails=2 fail_timeout=30s weight=5;
server 192.168.1.2 max_fails=2 fail_timeout=30s weight=5;
}
#缓存 共享字典配置
lua_shared_dict item_local_shop_cache 600m;
# server product
server {
listen 8082;
server_name 127.0.0.1;
charset gbk;
index index.html index.htm;
access_log /mnt/hgfs/Linux-Share/Lua/lua_project_v0.01/logs/product_access.log;
error_log /mnt/hgfs/Linux-Share/Lua/lua_project_v0.01/logs/product_error.log error;
#加载模板文件
set $template_root /mnt/hgfs/Linux-Share/Lua/lua_project_v0.01/template;
#url映射
location ~* "^/product/(\d+)\.html$" {
rewrite /product/(.*) http://127.0.0.1:8082/$1 permanent;
}
# chapter
location ~* "^/(\d{6,12})\.html$" {
default_type text/html;
lua_code_cache on;
content_by_lua_file "/mnt/hgfs/Linux-Share/Lua/lua_project_v0.01/application/controller/ProductController.lua";
}
}
当我们访问页面:http://127.0.0.1:8082/13669361192.html
将交给lua_project_v0.01/application/controller/ProductController.lua
处理
curl -k http://127.0.0.1:8082/13669361192.html
Hello ProductController.lua
uri = /13669361192.html
项目入口搞定 :hibiscus: :hibiscus: :hibiscus: :hibiscus:
:bouquet: lua-resty-template 的使用
#加载模板文件
set $template_root "/mnt/hgfs/Linux-Share/Lua/lua_project_v0.01/template/product";
location /template_test {
default_type 'text/html';
content_by_lua '
local template = require "resty.template"
template.render(chatroom.html, { message = "Hello, World!" })
';
}
:blossom: resty.template
渲染模板,通过ngx API
输出内容到指定的html
页面
local helper = require 'vendor.helper'
local array = helper.split('a,b,v,b',',')
for key,value in ipairs(array)
do
ngx.say(key, value)
end
local dkjson = require 'vendor.dkjson'
--lua对象到字符串
local obj = {
id = 110,
name = "Tinywan",
age = 24,
is_male = false,
hobby = {"film", "music", "read"}
}
local str = dkjson.encode(obj, {indent = true})
ngx.say(str, "<br/>")
nginx_live.conf
LiveRedisCacheController.lua
http://127.0.0.1:8088/ad/133456
有缓存返回:{"content":"Redis Cache Data"}
http://127.0.0.1:8088/ad/13345
没有缓存返回:{"content":"MYSQL DATA \n"}
#配置Nginx动静分离,定义的静态页面直接从Nginx发布目录读取。
location ~ .*\.(html|htm|gif|jpg|jpeg|bmp|png|ico|txt|js|css)$
{
root /mnt/hgfs/Linux-Share/Lua/lua_project_v0.01/template/info;
#expires定义用户浏览器缓存的时间为7天,如果静态页面不常更新,可以设置更长,这样可以节省带宽和缓解服务器的压力
expires 7d;
}
http://192.168.127.133:8083/2017TinywanInfo
可以得到响应内容os.rename(oldname, newname)
:文件重命名os.remove(filename)
:删除一个文件os.execute(cmd)
:os.execute可运行一条系统命令,类似于C语言的system函数,os.execute("mkdir /tmp/cq")
os.exit(code)
:中止当前程序的执行,code参数默认值为true。os.getenv(variable)
:返回环境变量的值,如果不存在,返回nil,print(os.getenv('HOME')) -- /root
os.time(tb)
:返回一个指定时间点的UNIX时间戳,如不带参数调用的话,就返回当前时间点的UNIX时间戳0.1
8686
location ~ ^/app/([-_a-zA-Z0-9/]+)$ {
set $id $1;
content_by_lua_file /mnt/hgfs/Linux-Share/Lua/lua_project_v0.01/application/api/${id}.lua;
}
curl http://127.0.0.1:8686/0.1/app/web文件名
curl http://127.0.0.1:8686/0.1/app/web
,将会执行web.lua
文件curl -X POST \
-H "X-LC-Id: 558e20cbe4b060308e3eb36c" \
-H "X-LC-Key: cbe4b06030558e208e3eb36c20cbe4b060308e3eb36c" \
-H "Content-Type: application/json" \
-d '{"name": "Tinywan","age": "26","tel": 13669361192}' \
http://127.0.0.1:8686/0.1/users
curl -X GET \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-G \
--data-urlencode 'id=9090'
http://127.0.0.1:8686/0.1/users
curl -X PUT \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{appkey}}" \
-H "Content-Type: application/json" \
-d "name=tinywan&age=26" \
http://127.0.0.1:8686/0.1/users
curl -X DELETE \
-H "X-LC-Id: {{appid}}" \
-H "X-LC-Key: {{masterkey}},master" \
-G \
--data-urlencode 'id=10' \
http://127.0.0.1:8686/0.1/users
# 直播接口数据
location ~ ^/0.1/live/([-_a-zA-Z0-9/]+) {
content_by_lua_file /mnt/hgfs/Linux-Share/Lua/lua_project_v0.01/application/api/$1.lua;
}
# Redis 没有缓存数据则后端Mysql查询数据
location /sub {
internal;
proxy_pass http://127.0.0.1:8686/backend/mysql;
}
# Backend Mysql Data
location /backend/mysql {
content_by_lua_block {
ngx.say("backend post: Mysql ")
}
}
http://127.0.0.1:8686/0.1/live/live_redis_mysql?id=1122334
即可得到结果。而且注意观察日志,第一次访问时不命中Redis,回源到Mysql;第二次请求时就会命中Redis了。 live_redis_mysql.lua:122: redis not cache content, Content comes from mysql , id : 1122334,
live_redis_mysql.lua:125: Content comes from Redis, id = 1122334,
8687
server {
listen 8080;
# 定义项目根目录,在此本项目目录为虚拟机目录,你可以自定义为 /home/www/
set $project_path /mnt/hgfs/Linux-Share/Lua/;
location /cjson_decode_pcall {
content_by_lua_file "${project_path}lua_project_v0.01/application/demo/cjson.lua";
}
tinywan@tinywan:~$ curl http://127.0.0.1:8686/0.1/live/live_redis_to_mysql?id=3
{"1":{"age":"24","name":"tinywan","address":"China","id":"3"},"Data_Sources":"Redis Cache Content"}
local template = require "resty.template"
local mysql = require 'vendor.mysql_fun'
local helper = require "vendor.helper"
local arg = helper.http_args()
local live_id = arg.live_id
local res = mysql.select(live_id)
local address = ""
if res.error_code == 200 then
address = res.result.address
end
template.render(chatroom.html, { hls_address = address })
ITEM:${Product_Id}
)nginx_product.conf
ItemController.lua
http://127.0.0.1:8087/item/13669361192
即可把Redis的缓存数据渲染到html页面root /mnt/hgfs/Linux-Share/Lua/lua_project_v0.01/public;
<script src="/video-js/videojs-contrib-hls.min.js"></script>
curl http://127.0.0.1:8088/live/1108
,请注意查看Log日志文件的变化# 入口Lua文件的调用
location ~ ^/live/(\d+)$ {
set $id $1;
content_by_lua_file "lua_project_v0.01/application/controller/LiveRedisCacheController.lua";
}
# 请求后端服务器的API接口查询数据,接口返回数据格式为Json格式
location ~ /openapi/(.*) {
internal;
proxy_pass http://www.baidu.com;
}
netstat
查看Redis连接数,好多"TIME OUT"),测试很糟糕的,QPS好低哦!ngx.location.capture
转换为:resty.http
resty.http API request error :no resolver defined to resolve "sewise.baidu.com"
resolver 8.8.8.8 114.114.114.114 valid=3600s;
http://127.0.0.1:8088/live/1063
+-------------+ +-------------+ +-------------+ +-------------+
| 第一次访问 |--->| ngx_cache |--->| redis_cache |--->| backend API |
+-------------+ +-------------+ +-------------+ +-------------+
|
+-------------+ +-------------+ +-------------+ |
| 第二次访问 |--->| ngx_cache |--->| redis_cache |<---------+
+-------------+ +-------------+ +-------------+
|
+-------------+ +-------------+ |
| 第三次访问 |--->| ngx_cache |<----------+
+-------------+ +-------------+
[lua] CacheController.lua:145: ngx_cache not found content, request redis db , id : 1066,
[lua] CacheController.lua:61: read_redis(): get redis content error : LIVE_TABLE : 1066
[lua] CacheController.lua:151: redis not found content, back to backend API , id : 1066
[lua] CacheController.lua:133: read_http(): content from backend API id : 1066
[lua] CacheController.lua:145: ngx_cache not found content, request redis db , id : 1066
[lua] CacheController.lua:70: read_redis(): content from redis LIVE_TABLE:1066
[lua] CacheController.lua:39: get_cache(): content from ngx.cache id : LIVE_TABLE:1066
[lua] CacheController.lua:39: get_cache(): content from ngx.cache id : LIVE_TABLE:1066
[lua] CacheController.lua:39: get_cache(): content from ngx.cache id : LIVE_TABLE:1066
redis_iresty
没有使用resty.websocket.server
和resty.redis
实现聊天室功能http://192.168.18.180:8088/live/1138
lua tcp socket read timed out
问题, ngx.thread.spawn
轻线程知识的学习,set_by_lua
: 流程分支判断,判断变量初始哈rewrite_by_lua
: 用lua脚本实现nginx rewriteaccess_by_lua
: ip准入,是否能合法性访问,防火墙content_by_lua
: 内存生成header_filter_by_lua
:过滤http头信息,增加头信息body_filter_by_lua
: 内容大小写,内容加密log_by_lua
: 本地/远程记录日志content_by_lua
,所有功能都在该阶段完成,也是可以的curl http://127.0.0.1/get_redis_iresty
curl http://127.0.0.1/test_ip_location?ip='122.228.95.112'
curl http://127.0.0.1/shell_test
lua_websocket_server.lua
,客户端接受文件public/index_default.html
,JS代码public/static/stats_default.js
location /lua_websocket_server {
default_type text/html;
content_by_lua_file /mnt/hgfs/Linux-Share/Lua/lua_project_v0.01/application/lua_websocket_server.lua;
}
console.log(e.data)
打印接收到的数据,这类我们可以接受到服务端发送的消息-- 服务端发送文本
bytes, err = wb:send_text("Hello world Tinywan")
--客户端接受数据,通过浏览器Console 可以看到以下数据
Hello world Tinywan
stats_default.js:70 Blob {size: 17, type: ""}
stats_default.js:65 disconnect
stats_default.js:60 connect
local shell = require 'vendor.websocket_shell'
lua-resty-shell
库,有时间整合到一起去http://192.168.127.133:8082/2017TinywanInfo
+-------------+
| uplink |
+-------------+
|
+
MASTER keep|alived BACKUP
172.29.88.224 172.29.88.222 172.29.88.225
+-------------+ +-------------+ +-------------+
| nginx01 |----| virtualIP |----| nginx02 |
+-------------+ +-------------+ +-------------+
|
+------------------+------------------+
| | |
+-------------+ +-------------+ +-------------+
| web01 | | web02 | | web03 |
+-------------+ +-------------+ +-------------+
+-------------+ +-------------+ +-------------+ +-------------+
| 第一次访问 |--->| ngx_cache |--->| redis |--->| backend API |
+-------------+ +-------------+ +-------------+ +-------------+
+-------------+ +-------------+ +-------------+
| 第二次访问 |--->| ngx_cache |--->| redis |
+-------------+ +-------------+ +-------------+
+-------------+ +-------------+
| 第三次访问 |--->| ngx_cache |
+-------------+ +-------------+