同步操作将从 开源技术小栈/lua-nginx-redis 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
location /test {
set $first "hello ";
echo "${first}world";
}
a = nil
,当且仅当一个变量不为nil 时,这个变量存在Boolean
类型:在控制条件中除了false
和nil
为假,其他值都为真,所以lua认为0和空字符串也是真String
类型:
string.gsub()
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
and
的优先级比or
高( a and b) or c
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') -- 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
function max(a,b)
if a > b then
return a
else
return b
end
end
print(max(10,20)) -- 20
function more()
return 10 , 20 ,30
end
a , b , c = more()
print(a,b,c) -- 10 20 30
function more()
return 10 , 20 ,30
end
-- 当函数位于最后一位的时候,返回全部值,否则值返回一个数值
a , b , c ,d = 100, more()
print(a,b,c,d) -- 100 10 20 30
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()
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
pairs
ipairs
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
ipairs
arr = {host = '127.0.0.1',port = '3306','Tinywan'}
-- 如果没有找到下标为整数的则直接退出,是整数的则直接输出,如上面的'Tinywan'
for k, v in ipairs(arr) do -- 只能遍历key 为整数的下标
print(k,v) -- 1 Tinywan
end
res, err, errcode, sqlstate = db:query("select * from tb_ngx_test order by id asc", 10)
ngx.say("result: ", cjson.encode(res))
result: [{"age":"24123","name":"tinywan123","address":"China","id":"1"},{"age":"24","name":"tinywan","address":"China","id":"2"}]
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
]]
local json_str = '{"is_male":"nan","name":"zhangsan","id":1}'
local t = json.decode(json_str)
ngx.say(format_table(t))
local t = [[{key="table key",value="table value"}]]
local json_str = json.encode(t)
ngx.say(json_str) -- "{key=\"table key\",value=\"table value\"}"
local t = {keys={"list1","list2","list3"},num=1}
local str = json.encode(t)
ngx.say(str) -- {"keys":["list1","list2","list3"],"num":1}
local name = "Lua1"
if name ~= "Lua"
then
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
__add
元方法 #demo1
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
--]]
__add
元方法 #demo2
-- 创建一个元表 (是创建一个类吗?)
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)
-- 输出结果 一个男孩和一个女孩的家庭
__index
元方法 #demo1
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
---- ]]
__index
元方法 #demo2
local t = {
name = "Tinywan"
}
local mt = {
money = 808080
}
mt.__index = mt
setmetatable(t, mt)
print(t.money)
-- 输出结果 808080
__index
元方法 #demo3
local t = {
name = "Tinywan"
}
local mt = {
__index = {
money = 909090
}
}
setmetatable(t, mt)
print(t.money)
-- 输出结果 909090
__index
元方法 #demo4
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 大家好
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, ", "))
fruits = {"banana","orange","apple"}
-- 返回 table 连接后的字符串 value = banana orange apple
print("连接后的字符串 ",table.concat(fruits))
-- 指定连接字符 value = banana, orange, apple
print("指定连接字符连接后的字符串 ",table.concat(fruits,", "))
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
Nginx [emerg]: bind() to 0.0.0.0:80 failed (98: Address already in use)
,执行:sudo fuser -k 80/tcp
location /live {
alias /home/tinywan/HLS/;
}
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
/live/index.html
,那么Nginx将会在服务器上查找/home/tinywan/HLS/index.html
文件url
中的location
后面的部分会被追加到alias
指定的目录后面,而location
后面的/live
路径将会别自动抛弃 location ~ ^/live/(.*)$ {
alias /home/tinywan/HLS/$1;
}
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
/live/txt.txt
那么Nginx将会在服务器上查找/home/tinywan/HLS/txt.txt
文件alias
换成 root
location ~ ^/live/(.*)$ {
root /home/tinywan/HLS/$1;
}
tinywan@tinywan:~/HLS$ curl http://127.0.0.1/live/txt.txt
<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>openresty/1.11.2.1</center>
</body>
</html>
/home/tinywan/HLS/txt.txt/live/txt.txt
alias
指定的目录是当前目录root
指定的是根目录
location /
中通过root
命令配置目录,其他目录匹配的位置使用alias
命令 location ~ ^/live/(\w+)/(.*) {
alias /home/tinywan/HLS/live/$1/$2;
}
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/ { … }
=
:严格匹配。如果这个查询匹配,那么将停止搜索并立即处理此请求。~
:为区分大小写匹配(可用正则表达式)!~
:为区分大小写不匹配!~*
:为不区分大小写不匹配^~
:如果把这个前缀用于一个常规字符串,那么告诉nginx 如果路径匹配那么不测试正则表达式正则中需要转义的特殊字符小结
$
匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 ‘\n' 或 ‘\r'。要匹配 $ 字符本身,请使用 $。 ( )
标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 和。*
匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 *。 +
匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 +。 .
匹配除换行符 \n之外的任何单字符。要匹配 .,请使用 \。 [ ]
标记一个中括号表达式的开始。要匹配 [,请使用 [。?
匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用 ?。\
将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如, ‘n' 匹配字符 ‘n'。'\n' 匹配换行符。序列 ‘\' 匹配 “\”,而 ‘(' 则匹配 “(”。 ^
匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合。要匹配 ^ 字符本身,请使用 ^。 { }
标记限定符表达式的开始。要匹配 {,请使用 {。|
指明两项之间的一个选择。要匹配 |,请使用 |。正则表达式 (Regular expression) 匹配location
location ~* \.(gif|jpg|jpeg)$ { }
:匹配所有以 gif,jpg或jpeg 结尾的请求location ~ /documents/Abc { }
:匹配任何以 /documents/ 开头的地址,匹配符合以后,还要继续往下搜索(static/lib)
(/home/HLS/stream123/index.m3u8)
(/home/HLS/stream123.m3u8)
http://127.0.0.1/live/stream123/index.m3u8
# 匹配任何以/live/ 开头的任何查询并且停止搜索。任何正则表达式将不会被测试
location ^~ /live/ {
root /home/tinywan/HLS/;
}
# 以上匹配成功后的组合:/home/tinywan/HLS/live/....
后缀匹配
gif|jpg|jpeg|png|css|js|ico|m3u8|ts
结尾的请求http://127.0.0.1/live/stream123/11.ts
http://127.0.0.1/live/stream123/index.m3u8
http://127.0.0.1/hls/123.m3u8
location ~* \.(gif|jpg|jpeg|png|css|js|ico|m3u8|ts)$ {
root /home/tinywan/HLS/;
}
HSL直播目录匹配实际案例(请测试上线)
http://127.0.0.1/live/stream123/index.m3u8
location ^~ /live/ {
root /home/tinywan/HLS/;
}
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 | 乱码 | 二进制格式的客户端地址 |
https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/config.md
https://github.com/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/config.md
/Tinywan/Lua-Nginx-Redis/blob/master/Nginx/config.md
Rewrite 正则匹配uri
参数接收
curl http://192.168.18.143/live/tinywan123/index.m3u8
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";
}
args === tinywan123
$1==tinywan123 $2==index $3==m3u8
Total_numbser :: index
URI /live/tinywan123/index.m3u8
Total_numbser ::
(\w+)
(\D+)
(m3u8|ts)
.
需要用转义字符转义\.
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
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
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
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
location /proxy/ {
proxy_pass http://127.0.0.1:8000/video;
}
http://127.0.0.1:8000/proxy/index.html
会被匹配到:/usr/local/nginx/html/proxy/index.html
location /proxy/ {
root /usr/local/nginx/html;
index index.html index.htm;
}
.
├── 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
[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
[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']
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']
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']
ngx.req.http_version()
ngx.req.get_method()
ngx.req.raw_header()
ngx.req.get_body_data()
/opt/openresty/luajit/bin/luajit
,这样我们直接可以这样运行一个Lua文件:luajit test.lua
tinywan@tinywan:~/Lua$ luajit test.lua
The man name is Tinywan
The man name is Phalcon
代码引入:lua_package_path "/opt/openresty/nginx/lua/lua-resty-redis/lib/?.lua;;";
Lua脚本实现一个CDN的反向代理功能(智能查找CDN节点)(测试成功,可上线)
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脚本结合 Nginx+Lua+Local Redis+Mysql服务器缓存
Nginx+Lua+Local Redis+Mysql集群架构
Lua脚本结合 Redis 统计直播流播放次数、链接次数等等信息
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;
}
}
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/;
}
}
http://192.168.18.143/live/tinywan123/index.m3u8
lua_package_path "/opt/openresty/nginx/lua/lua-resty-websocket/lib/?.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;
}
}
}
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)
}
}
root@tinywan:/opt/openresty/nginx/conf# curl http://127.0.0.1/cjson
{"some_object":{"tel":13669313112,"age":24},"name":"tinywan","some_array":[]}
/opt/openresty/lualib/resty
tinywan@tinywan:/opt/openresty/nginx/lua/lua-resty-session/lib/resty$ ls
session session.lua
/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 的所有库文件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
location /start {
content_by_lua_block {
local session = require "resty.session".start()
session.data.name = "OpenResty Fan Tinywan"
session:save()
ngx.say("<html><body>Session started. ",
"<a href=/test>Check if it is working</a>!</body></html>")
ngx.say(session.data.name,"Anonymous")
}
}
tinywan@tinywan:/opt/openresty/nginx/conf$ curl http://192.168.18.143/start
<html><body>Session started. <a href=/test>Check if it is working</a>!</body></html>
OpenResty Fan Tinywan Anonymous
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;
}
if ngx.req.get_uri_args()["wsSecret"] ~= "e65e6a01cf26523e206d5bb0e2a8a95a" then
return ngx.exit(403)
end
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"
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.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
ngx.STDERR
ngx.EMERG
ngx.ALERT
ngx.CRIT
ngx.ERR
ngx.WARN
ngx.NOTICE
ngx.INFO
ngx.DEBUG
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
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)
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)
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
(非阻塞的)
,而不是Lua自身的API(阻塞的)
,Lua 自身API会阻塞掉的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 -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"}
lua_shared_dict
├── 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
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;
}
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
nginx -s reload
会不起作用sudo killall nginx
重启即可sbin/nignx
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;
}
}
}
max_clients = worker_processes * worker_connections/2
max_clients = worker_processes * worker_connections/4
max_clients = worker_processes * worker_connections
max_clients = worker_processes * worker_connections/2
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
redis.call() 与 redis.pcall()的区别
Lua网络编程
man.lua
local _name = "Tinywan"
local man = {}
function man.GetName()
return _name
end
function man.SetName(name)
_name = name
end
return man
测试封装,test.lua
local man = require('man')
print("The man name is "..man.GetName())
man.SetName("Phalcon")
print("The man name is "..man.GetName())
EVAL script numkeys key [key ...] arg [arg ...]
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将回复发回给客户端
KEYS[i]
来获取外部传入的键值ARGV[i]
来获取外部传入的参数EVAL
执行一个简单的Lua脚本文件
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"
-- 获取键值/参数
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]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
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}))
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;
}
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"}
local key=KEYS[1]
local list=redis.call("lrange",key,0,-1);
return list;
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)<tonumber(nums)) then
table.insert(result,1,v);
end
end
return result;
在命令行输入以下命令
git config --global credential.helper store
这一步会在用户目录下的.gitconfig文件最后添加:
```
[credential]
helper = store
```
push 代码
push你的代码 (git push), 这时会让你输入用户名和密码, 这一步输入的用户名密码会被记住, 下次再push代码时就不用输入用户名密码!这一步会在用户目录下生成文件.git-credential记录用户名密码的信息。
Markdown 的超级链接技术
【1】需要链接的地址:
```
[解决向github提交代码不用输入帐号密码](#githubpush)
```
【2】要链接到的地方:
```
<a name="githubpush"/> 解决向github提交代码不用输入帐号密码
```
通过【1】和【2】可以很完美的实现一个连接哦!
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 ./ -mindepth 1 -maxdepth 3 -type f -name "*.flv" -mmin +10080 | xargs rm -rf
-type f
按类型查找-mmin +10080
7天之前的文件find ~ -type f | xargs ls -l
find . -name '*.p' -type f -mmin -30
find . -name '*.phtml' -type f -mmin -30 -ls
find . -type f -mtime -1
find . -type f -mtime +1
Changes are automatically rendered as you type.
dangerouslySetInnerHTML
is used! Yay!This blockquote will change based on the HTML settings above.
var React = require('react');
var Markdown = require('react-markdown');
React.render(
<Markdown source="# Your markdown here" />,
document.getElementById('content')
);
Pretty neat, eh?
Read usage information and more on GitHub
A component by VaffelNinja / Espen Hovlandsdal
This module is licensed under the BSD license
Copyright (C) 2017, by Wanshaobo "Tinywan".
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。