AJAX 化和 Service 化的趋势让所有东西开始讲 –RESTful
天下武功,无坚不破,唯快不破!Nginx的看家本领就是速度,Lua的拿手好戏亦是速度,这两者的结合在速度上无疑有基因上的优势。
- nginx 是最不烂的一个
- 其它真心都特别烂
- Apache 最大的问题是其 I/O 模型,无法完成非常高效的响应; 但是优点是:开发接口规整,基于它来写 mod 非常方便;
- Lighttpd 正好相反,其 I/O 非常高效,但是开发接口不怎么友好
- Nginx 融合了两者的优点
使用了 lighttpd 多路复用的 I/O 模型
借鉴了 apache 的模块开发支持
Nginx采用多进程模型,单Master—多Worker,
由Master处理外部信号、配置文件的读取及Worker的初始化,
Worker进程采用单线程、非阻塞的事件模型(Event Loop,事件循环)来实现端口的监听及客户端请求的处理和响应,
同时Worker还要处理来自Master的信号。由于Worker使用单线程处理各种事件,
所以一定要保证主循环是非阻塞的,否则会大大降低Worker的响应能力。
- 简单 + 轻量级(160k) + 动态性 + 高性能 + 协程 + 内存开销小 + VM可中断/重入 + 可扩展性性---号称性能最高的脚本
- 通过闭包和table可支持数据抽象,虚函数,继承和重载,面向对象编程、面向过程编程、函数式编程、自动内存管理
- 用同步的语义来实现异步的调用
- 组合优于继承的编程哲学
- Nginx无缝对接
- 充分利用 Nginx 的非阻塞 I/O 模型
- 不仅仅对 HTTP 客户端请求,甚至于对远程后端诸如MySQL,~Redis 等都进行一致的高性能响应,非阻塞的处理数据库层调用
- Lua 脚本可与Nginx 支持的各种C以及Lua 模块非常好的互动
- 快速构造出足以胜任 10K+ 并发连接响应的超高性能Web 应用系统
- 最小心智负担原则
– 最小特性 – 最少惊异 – 最少犯错机会 - 少就是指数级的多
–最少特性原则。
–如果一个功能不对解决任何问题有显著价值,那么就不提供。 - 功能内聚:组合优于继承的编程哲学
- 极度简化但完备的 OOP
- 显式表达
–任何封装都是有漏洞的。
–最佳的表达方式就是最直白的表达方式。
–不试图去做任何包装。
–所写即所得的语言。
协程类似一种多线程,与多线程的区别有:
- 协程并非os线程,所以创建、切换开销比线程相对要小。
- 协程与线程一样有自己的栈、局部变量等,但是协程的栈是在用户进程空间模拟的,所以创建、切换开销很小。
- 多线程程序是多个线程并发执行,也就是说在一瞬间有多个控制流在执行。而协程强调的是一种多个协程间协作的关系, 只有当一个协程主动放弃执行权,另一个协程才能获得执行权,所以在某一瞬间,多个协程间只有一个在运行。
- 由于多个协程时只有一个在运行,所以对于临界区的访问不需要加锁,而多线程的情况则必须加锁。
- 多线程程序由于有多个控制流,所以程序的行为不可控,而多个协程的执行是由开发者定义的所以是可控的。 Nginx的每个Worker进程都是在epoll或kqueue这样的事件模型之上,封装成协程,每个请求都有一个协程进行处理。 这正好与Lua内建协程的模型是一致的,所以即使ngx_lua需要执行Lua,相对C有一定的开销,但依然能保证高并发能力。
- ngx_lua实现Proactor模型
– 业务逻辑以自然逻辑书写
– 自然获得高并发能力
– 不会因I/O阻塞等待而浪费CPU资源 - 每Nginx工作进程使用一个Lua VM,工作进程内所有协程共享VM
- 将Nginx I/O原语封装后注入Lua VM,允许Lua代码直接访问
- 每个外部请求都由一个Lua协程处理,协程之间数据隔离
- Lua代码调用I/O操作接口时,若该操作无法立刻完成,则打断相关协程的运行幵保护上下文数据
- I/O操作完成时还原相关协程上下文数据并继续运行
- 业务更加稳定
– Nginx大连接数目支持非常好
– Nginx本身的内存占用很少,更不会吃swap
– 业务性能更高 - QPS比Apache要好
- 节省机器数目
- 基于Nginx的模块性能往往是之前业务的数倍
[lua@vm-10-154-156-34 ~]$ lua
Lua 5.2.2 Copyright (C) 1994-2013 Lua.org, PUC-Rio
> print("Hello, World")
Hello, World
>
/* shell一样运行: */
[lua@vm-10-154-156-34 ~]$ cat hello.lua
#!/usr/local/bin/lua
print("Hello, World")
[lua@vm-10-154-156-34 ~]$ chmod +x hello.lua
[lua@vm-10-154-156-34 ~]$ ./hello.lua
Hello, World
...
/* 单行注释 */
-- 两个减号是行注释
/* 块注释: */
--[[
这是块注释
这是块注释
--]]
Lua的数字只有double型,64bits,你不必担心Lua处理浮点数会慢(除非大于100,000,000,000,000),或是会有精度问题。
你可以以如下的方式表示数字,0x开头的16进制和C是很像的。
num = 1024
num = 3.0
num = 3.1416
num = 314.16e-2
num = 0.31416E1
num = 0xff
num = 0x56
字符串你可以用单引号,也可以用双引号,还支持C类型的转义,比如: ‘\a’ (响铃), ‘\b’ (退格), ‘\f’ (表单), ‘\n’ (换行), ‘\r’ (回车), ‘\t’ (横向制表), ‘\v’ (纵向制表), ‘\\’ (反斜杠), ‘\”‘ (双引号), 以及 ‘\” (单引号)
下面的四种方式定义了完全相同的字符串(其中的两个中括号可以用于定义有换行的字符串)
a = 'alo\n123"'
a = "alo\n123\""
a = '\97lo\10\04923"'
a = [[alo
123"]]
C语言中的NULL在Lua中是nil,比如你访问一个没有声明过的变量,就是nil,
布尔类型只有nil和false是 false,数字0啊,‘’空字符串(’\0’)都是true!
另外,需要注意的是:lua中的变量如果没有特殊说明,全是全局变量,那怕是语句块或是函数里。变量前加local关键字的是局部变量。
theGlobalVar = 50
local theLocalVar = "local variable"
下面的关键字是保留的,不能用作名字:
and break do else elseif
end false for function if
in local nil not or
repeat return then true until while
Lua 是一个大小写敏感的语言: and 是一个保留字,但是 And 和 AND 则是两个不同的合法的名字。
一般约定,以下划线开头连接一串大写字母的名字(比如 _VERSION)被保留用于 Lua 内部全局变量。
下面这些是其它的 token :
+ - * / % ^ #
== ~= <= >= < > =
( ) { } [ ]
; : , . .. ...
类型 说明
nil 全局变量默认值,如果要删除一个全局变量可以赋值为nil
boolean 布尔值
number 数字型
string 字符串型
userdata 用户自定义类型,一般是C/C++中的类型
function 函数
thread 线程
table 表
逻辑运算符认为false和nil是假(false),其他为真,0也是true.
a and b -- 如果a为false,则返回a,否则返回b
a or b -- 如果a为true,则返回a,否则返回b
x = x or v -- 如果x为false或者nil时则给x赋初始值v
-- 等价于
if not x then
x = v
end
-- 三元运算符
a ? b : c => a and b or c -- and 的优先级别比 or 高
not -- not 的结果只返回false或true,作用类似于"非" "!"取反的意思
print(not nil) -- true
print(not false) -- true
print(not 0) -- false
/* while循环 */
sum = 0
num = 1
while num <= 100 do
sum = sum + num
num = num + 1
end
print("sum =",sum)
/* for 循环 */
sum = 0
for i = 1, 100 do
sum = sum + i
end
sum = 0
for i = 1, 100, 2 do
sum = sum + i
end
sum = 0
for i = 100, 1, -2 do
sum = sum + i
end
/* until循环 */
sum = 2
repeat
sum = sum ^ 2 --幂操作
print(sum)
until sum >1000s
/* if-else分支 */
if age == 16 and sex =="Female" then
print("16岁那年追过的女孩")
elseif age > 30 and sex ~="virgin" then
print("old Female without virgin!")
elseif age < 14 then
io.write("too young, too naive!\n")
else
local age = io.read()
print("Your age is "..age)
end
上面的语句不但展示了if-else语句,也展示了
1)“~=”是不等于,而不是!=
2)io库的分别从stdin和stdout读写的read和write函数
3)字符串的拼接操作符“..”
另外,条件表达式中的与或非为分是:and, or, not关键字。
/* 函数 */
-- Lua的函数和Python,Javascript的很像
/* 递归 */
function fib(n)
if n < 2 then return 1 end
return fib(n - 2) + fib(n - 1)
end
function newCounter() local i = 0 return function() i = i + 1 -- anonymous function return i end endc1 = newCounter() print(c1()) --> 1 print(c1()) --> 2
</code></pre> <pre><code class="javascript">function myPower(x) return function(y) return y^x end end
power2 = myPower(2)
power3 = myPower(3)
print(power2(4)) --4的2次方 print(power3(5)) --5的3次方
和Go语言一样,可以一条语句上赋多个值,如:
name, age, bGay = "luaer", 25, false, "[email protected]"
上面的代码中,因为只有3个变量,所以第四个值被丢弃。
函数也可以返回多个值:
function getUserInfo(id)
print(id)
return "luaer", 25, "[email protected]", "https://github.com/fuhao715"
end
name, age, email, website, bGay = getUserInfo()
注意:上面的示例中,因为没有传id,所以函数中的id输出为nil,因为没有返回bGay,所以bGay也是nil。
函数前面加上local就是局部函数,其实,Lua中的函数和Python Or Javascript中的一个德行。
比如:下面的两个函数是一样的:
function foo(x) return x^2 end
foo = function(x) return x^2 end
table是Lua中唯一的数据结构
lua 的 table 充当了数组和映射表的双重功能,通过组合模式可以实现面向对象编程、实现各种数据结构。--这一点和Go语言很像
所谓Table其实就是一个Key Value的数据结构,它很像Javascript中的Object,或是Python中的Dict,在别的语言里Map,Table长成这个样子:
siva = {site="http://fuhao715.github.io/", age=10, passion=True}
下面是table的CRUD操作:
siva.ceo="许QQ" -- 增
siva.site=nil -- 删
siva.passion = false -- 改
local age = siva.age --查
上面看上去像Go/C中的结构体 Or Java中的Bean,但是site,age, passion, ceo都是key。
还可以像下面这样写义Table:
t = {[20]=100, ['name']="Luaer", [3.14]="PI"}
这样就更像Key Value了。于是你可以这样访问:t[20],t[“name”], t[3.14]。
我们再来看看数组:
arr = {10,20,30,40,50}
这样看上去就像数组了。但其实其等价于:
arr = {[1]=10, [2]=20, [3]=30, [4]=40, [5]=50}
所以,你也可以定义成不同的类型的数组,比如:
arr = {"string", 100, "luaer", function() print("www.lua.org") end}
注:其中的函数可以这样调用:arr[4]()。
mt = {} -- 创建矩阵matrix
for i = 1, N do -- 创建N行
mt[i] = {} -- 每行都是一个数组(table元素)
for j = 1, M do -- 创建M列
mt[i][j] = "a" -- 第N行第M行的值
end
end
list = nil
list = {next = list, value = "hello3"}
list = {next = list, value = "hello2"}
list = {next = list, value = "hello1"}
-- 遍历
local l = list
while l do
print(l.value)
l = l.next
end
-- 记录下表的增查记录
local index = {} -- 私有的key,用来记录原始表在代理表中的下标
local mt = { -- 创建元表
__index = function(t, k)
print("访问了" .. tostring(k) .. "元素")
return t[index][k] -- 从代理表中获取原始表中k下标的数据
end,
__newindex = function(t, k, v)
print("更新了 " .. tostring(k) .. " 元素的值为 " .. tostring(v))
t[index][k] = v -- 更新代理表中下标为index的原始表中的元素
end
}
function setProxy(t)
local proxy = {} -- 创建代理表
proxy[index] = t -- 把原始表加到代理表的index下标中
setmetatable(proxy, mt) -- 设置代理表的元表
return proxy -- 返回代理表,即所有操作都是直接操作代理表
end
p = setProxy({})
p[2] = 'abcdefg' -- 更新了 2 元素的值为 abcdefg
print(p[2]) -- 访问了2元素
Table的下标不是从0开始的,是从1开始的。
for i=1, #arr do -- #表示table长度 or table.getn(arr)
print(arr[i])
end
注:前面说过,Lua中的变量,如果没有local关键字,全都是全局变量,
Lua也是用Table来管理全局变量的,Lua把这些全局变量放在了一个叫“_G”的Table里。table.foreach(_G, print)
用如下的方式来访问一个全局变量(假设我们这个全局变量名叫globalVar):
_G.globalVar
_G["globalVar"]
我们可以通过下面的方式来遍历一个Table。
for k, v in pairs(t) do
print(k, v)
end
MetaTable主要是用来做一些类似于C++重载操作符式的功能。
比如,我们有两个分数:
fraction_a = {numerator=2, denominator=3}
fraction_b = {numerator=4, denominator=7}
我们想实现分数间的相加:2/3 + 4/7,我们如果要执行: fraction_a + fraction_b,会报错的。
所以,我们可以动用MetaTable,如下所示:
fraction_op={}
function fraction_op.__add(f1, f2)
ret = {}
ret.numerator = f1.numerator * f2.denominator + f2.numerator * f1.denominator
ret.denominator = f1.denominator * f2.denominator
return ret
end
为之前定义的两个table设置MetaTable:(其中的setmetatble是库函数)
setmetatable(fraction_a, fraction_op)
setmetatable(fraction_b, fraction_op)
于是你就可以这样干了:(调用的是fraction_op.__add()函数)
fraction_s = fraction_a + fraction_b
至于__add这是MetaMethod,这是Lua内建约定的,其它的还有如下的MetaMethod:
__add(a, b) 对应表达式 a + b
__sub(a, b) 对应表达式 a - b
__mul(a, b) 对应表达式 a * b
__div(a, b) 对应表达式 a / b
__mod(a, b) 对应表达式 a % b
__pow(a, b) 对应表达式 a ^ b
__unm(a) 对应表达式 -a
__concat(a, b) 对应表达式 a .. b
__len(a) 对应表达式 #a
__eq(a, b) 对应表达式 a == b
__lt(a, b) 对应表达式 a < b
__le(a, b) 对应表达式 a <= b
__index(a, b) 对应表达式 a.b
__newindex(a, b, c) 对应表达式 a.b = c
__call(a, ...) 对应表达式 a(...)
上面我们看到有__index这个重载,这个东西主要是重载了find key的操作。
这操作可以让Lua变得有点面向对象的感觉,让其有点像Javascript的prototype,python的__dict__,Go的struct
所谓__index,说得明确一点,如果我们有两个对象a和b,我们想让b作为a的prototype只需要:
setmetatable(a, {__index = b})
例如下面的示例:你可以用一个Window_Prototype的模板加上__index的MetaMethod来创建另一个实例:
Window_Prototype = {x=0, y=0, width=100, height=100}
MyWin = {title="Hello"}
setmetatable(MyWin, {__index = Window_Prototype})
于是:MyWin中就可以访问x, y, width, height的东东了。
(注:当表要索引一个值时如table[key], Lua会首先在table本身中查找key的值,
如果没有并且这个table存在一个带有__index属性的Metatable, 则Lua会按照__index所定义的函数逻辑查找)
Person={}
function Person:new(p)
local obj = p
if (obj == nil) then
obj = {name="Luaer", age=25, handsome=true}
end
self.__index = self
return setmetatable(obj, self)
end
function Person:toString()
return self.name .." : ".. self.age .." : ".. (self.handsome and "handsome" or "ugly")
end
1)self 就是 Person,Person:new(p),相当于Person.new(self, p)
2)new方法的self.__index = self 的意图是怕self被扩展后改写,所以,让其保持原样
3)setmetatable这个函数返回的是第一个参数的值。
me = Person:new()
print(me:toString())
luaer = Person:new{name="Luaer", age=25, handsome=false}
print(luaer:toString())
继承如下,Lua和Javascript很相似。
Student = Person:new()
function Student:new()
newObj = {year = 2013}
self.__index = self
return setmetatable(newObj, self)
end
function Student:toString()
return "Student : ".. self.year.." : " .. self.name
end
local function createAccount(_name) -- 工厂方法
local self = {name = _name}
local function _setName(name)
self.name = name
end
local function _getName()
return self.name
end
-- 公有方法表
local public = {
setName = _setName,
getName = _getName,
--name = self.name -- 不公开私有成员变量
}
return public
end
local account = createAccount('Tom')
print(account.name) -- 无法访问,因为没有公开
require(“model_name”)来载入别的lua文件,文件的后缀是.lua。载入的时候就直接执行那个文件了。比如:
注意:
1)require函数,载入同样的lua文件时,只有第一次的时候会去执行,后面的相同的都不执行了。
2)如果你要让每一次文件都会执行的话,你可以使用dofile(“hello”)函数
3)如果你要玩载入后不执行,等你需要的时候执行时,你可以使用 loadfile()函数,如下所示:
local hello = loadfile("hello")
... ...
hello()
... ...
loadfile("hello")后,文件并不执行,我们把文件赋给一个变量hello,当hello()时,才真的执行。
标准的玩法如下:
module('helloModel', package.seeall)
local HelloModel = {}
local function _hello()
return "hello world lua"
end -- 私有局部函数
function HelloModel.hello()
return _hello()
end -- 对外公开的函数
function HelloModel:getName()
return "luaer"
end
return HelloModel
于是我们可以这样使用:
local hello_model = require("mymod")
hello_model.hello()
print(hello_model:getName())
其实,require干的事就如下:
local hello_model = (function ()
--mymod.lua文件的内容--
end)()
Lua 代码可以显式的调用 error 函数来产生一条错误。
如果你需要在 Lua 中捕获发生的错误, 你可以使用 pcall 函数。
eg error():
print "enter a number:"
n = io.read("*number")
if not n then error("invalid input") end
... ...
eg error():
local status, err = pcall(function () error("my error") end)
---垃圾回收
collectgarbage() -- 调用gc,清除引用为0的内存空间
-- 类似golang的goroutine
-- 创建协同
co = coroutine.create(function () -- 创建一个协同函数,接收一个匿名函数,返回thread类型
print("hi")
end)
print(co) -- thread: 0x7fe1834127d0
-- 协同的三个状态:挂起态(suspended)、运行态(running)、停止态(dead)。
print(coroutine.status(co)) -- 查看协同的状态,默认状态是挂起态 suspended
coroutine.resume(co) -- 改变协同的状态为运行太 hi
print(coroutine.status(co)) -- 协同运行完以后将变量停止态 dead
-- 如此挂起正在运行的协同
co = coroutine.create(function ()
print("hi")
coroutine.yield() -- 协同运行到此状态将变成挂起
print("你好")
end)
coroutine.resume(co) -- hi
coroutine.resume(co) -- 你好
coroutine.resume(co) -- false,协同结束后将不能再使用
--协同数据交换
co = coroutine.create(function (x, y) -- 接收两个参数
print("hi", coroutine.yield(x + y)) -- 返回一个值,同时参数也传递给了coroutine.yield
return 100 -- 第三种返回值的方式
end)
print(coroutine.resume(co, 12, 87)) -- 传递两个参数并接收返回值(true, 99)
-- 执行coroutine.yield(x + y)之前协同被挂起,但值被返回,因此print函数未被执行,下面执行
print(coroutine.resume(co, 12, 87)) -- 传递两个参数并接收返回值(true, 100)
函数 说明
math.abs (x) 求绝对值
math.acos (x) 求反余弦
math.asin (x)
math.atan (x)
math.atan2 (y, x)
math.ceil (x)
math.cos (x)
math.cosh (x)
math.deg (x)
math.exp (x)
math.floor (x)
函数 说明
table.concat (table [, sep [, i [, j]]]) 拼接成字符串,sep代表连接符,i开始位置,j结束位置
table.insert (table, [pos,] value) 插入一个元素,默认是最后一个,pos指定位置
table.maxn (table) 获取最大长度
table.remove (table [, pos]) 删除一个元素,默认删除最后一个,pos指定位置
table.sort (table [, comp]) 排序
函数 说明
string.byte (s [, i [, j]]) 把字符转换成ASCII码
string.char (…) 把ASCII码转换成字符
string.dump (function)
string.find (s, pattern [, init [, plain]]) 查找,pattern查找的字符串,init从那里开始默认为1,plain
string.format (formatstring, …) 格式化字符串
函数 说明
io.close ([file]) 等效file:close(),如果没有file则关闭默认输出
io.flush () 等效file:flush()
io.input ([file])
io.lines ([filename]) 等效io.input():lines()
io.open (filename [, mode]) 打开一个文件,模式:r,w,a,r+,w+,a+
io.output ([file])
io.popen (prog [, mode]) 依赖系统的,不是所有平台都能用
io.read (…) 等效io.input():read
io.tmpfile () 创建一个临时文件,当程序退出时自动删除
io.type (obj) obj的类型是file是打开的文件句柄,close file是关闭的文件句柄,nil不是文件
io.write (…) 等效io.output():write
file:close () 关闭文件,会自动gc掉,但时间不确定
file:flush () 保存任何数据到文件中
file:lines () 迭代文件的每一行
file:read (…) 读取文件,*n,*a,*l,number
file:seek ([whence] [, offset]) 指定位置,默认是cur,1,set,end
file:setvbuf (mode [, size]) 设置buff缓存,no,full,line
file:write (…) 写文件,参数必须是string或者number
函数 说明
os.clock () 返回程序所使用的cpu时间
os.date ([format [, time]]) 当前系统日期,或格式化某个日期
os.difftime (t2, t1) 时间差
os.execute ([command]) 执行shell命令
os.exit ([code]) 调用C的exit函数
os.getenv (varname) 获取系统环境变量,变量名,不包含$
os.remove (filename) 删除文件,文件名
os.rename (oldname, newname) 修改文件名
os.setlocale (locale [, category])
设置地区,"all", “collate”, “ctype”, “monetary”, “numeric”, or “time”
os.time ([table]) 返回当前时间或把时间保存在table中,
os.tmpname () 临时文件名
Nginx 的配置文件记法就是一种小语言
location = '/hello' {
set_unescape_uri $person $arg_person;
set_if_empty $person 'anonymous';
echo "hello, $person!";
}
curl 'http://localhost/hello?person=luaer'
hello, luaer
# nginx.conf
location = /hello {
content_by_lua '
ngx.say("Hello World")
';
}
# nginx.conf
location = /hello {
content_by_lua_file conf/hello.lua;
}
在Nginx 世界里有两种类型的“请求”,一种叫做“主请求”(main request),而另一种则叫做“子请求”(subrequest)。
“主请求”,就是由 HTTP 客户端从 Nginx 外部发起的请求。比如,从浏览器访问Nginx就是一个“主请求”。 “子请求”则是由 Nginx 正在处理的请求在 Nginx 内部发起的一种级联请求, 外观上很像 HTTP 请求,但是,其实没有http的开销,因为,这是c 级别的内部调用!。 目的是把“主请求”分解为多个较小粒度的“内部请求”,并发或串行地访问多个 location 接口, 然后由这些 location 接口通力协作,共同完成整个“主请求”。当然,“子请求”的概念是相对的.
location /main { # curl location/main
echo_location /foo; # echo_location发送子请求到指定的location
echo_location /bar;
}
location /foo {
echo foo;
}
location /bar {
echo bar;
}
基于OpenResty(ngx_lua)+Moochine
通过upstream机制已经可以支持对mysql、redis、postgreSQL、memcached 等数据库的访问(全都是异步无阻塞的);
跟lua扩展有关的模块,提供给lua脚本调用的库,api非常丰富,涉及各种的操作;
- OpenResty 安装
参看:http://openresty.org/#Installation 编译时选择luajit, ./configure --with-luajit
- Siva_Nginx_lua 安装
Checkout Siva_Nginx_lua 代码
- 配置环境变量
export OPENRESTY_HOME=/usr/local/openresty
export SIVA_NGX_LUA_HOME=/path/to/siva_ngx_lua
将以上两个环境变量 加到 ~/.bash_profile 里,下次登陆自动生效
vim ~/.bash_profile
使环境变量立即生效
source ~/.bash_profile
cd /path/to/siva_ngx_lua/bin
./siva new
siva-cluster #程序根目录
|-- app1 #应用2目录
| |`-- app #应用主目录
| `-- test.lua #请求处理函数
| |-- routing.lua # URL Routing配置
| |-- application.lua # app 描述文件
|-- app2 #应用2目录
| |`-- app #应用主目录
| `-- test.lua #请求处理函数
| |-- routing.lua # URL Routing配置
| |-- application.lua # app 描述文件
|-- bin #脚本目录
| |-- debug.sh #关闭服务->清空error log->启动服务->查看error log
| |-- reload.sh #平滑重载配置
| |-- start.sh #启动
| |-- stop.sh #关闭
| |-- console.sh #控制台。注意:控制台需要安装Python2.7或Python3.2。
| `-- cut_nginx_log_daily.sh #Nginx日志切割脚本
|-- conf #配置目录
| `-- nginx.conf #Nginx配置文件模版。需要配置 `set $SIVA_APP_NAME 'siva-demo';` 为真实App的名字。
|-- appname.log.yyyy-mm-dd.log #调试日志文件。在 application.lua 可以配置路径和Level。
`-- nginx_runtime #Nginx运行时目录。这个目录下的文件由程序自动生成,无需手动管理。
|-- conf
| `-- p-nginx.conf #Nginx配置文件(自动生成),执行 ./bin/start.sh 时会根据conf/nginx.conf 自动生成。
`-- logs #Nginx运行时日志目录
|-- access.log #Nginx 访问日志
|-- error.log #Nginx 错误日志
`-- nginx.pid #Nginx进程ID文件
./bin/start.sh #启动
./bin/stop.sh #停止
./bin/reload.sh #平滑重载配置
./bin/debug.sh #关闭服务->清空error log->启动服务->查看error log
注意:以上命令只能在程序根目录执行,类似 ./bin/xxx.sh 的方式。
debug={
on=false,
to="response", -- "ngx.log"
}
logger = {
file = "siva_ngx_lua_demo.log",
level = "DEBUG",
}
redis = {
host = "10.154.252.153",
port = 6379,
timeout= 10000,
poolsize= 2000
}
mysql= {
host = "10.154.252.153",
port = 3306,
db= "taps",
charset = "UTF8",
username = "root",
passwd= "siva@2014",
timeout= 10000,
max_packet_size= 1024*1024,
poolsize= 2000
}
#!/usr/bin/env lua
-- -*- lua -*-
local router = require('siva.router')
router.setup()
map('^/ip%?ip=(.*)', 'test.getIP')
module("test", package.seeall)
local JSON = require("cjson")
function getIP(req,resp)
local exc_begin = os.time()
local ip = req.uri_args["ip"]
......
local red = redis_conn();
local startSet, err = red:zrevrangebyscore(key_ip_lib, offset, 0, "limit", 0, 1);
local startAry = string.split(startSet[1], ":");
local ip_info = red:hmget(key_start, "country", "countryId", "area", "areaId", "region", "regionId", "city", "cityId", "isp", "ispId");
local result = {
code= "A000000",
data= {
area= ip_info[3],
................
},
timestamp= os.date('%Y%m%d%H%M%S', os.time())
}
resp:writeln(JSON.encode(result))
resp:finish()
logger:i("-ip--last--"..(os.time()-exc_begin))
end
--属性
method = ngx.var.request_method -- http://wiki.nginx.org/HttpCoreModule#.24request_method
schema = ngx.var.schema -- http://wiki.nginx.org/HttpCoreModule#.24scheme
host = ngx.var.host -- http://wiki.nginx.org/HttpCoreModule#.24host
hostname = ngx.var.hostname -- http://wiki.nginx.org/HttpCoreModule#.24hostname
uri = ngx.var.request_uri -- http://wiki.nginx.org/HttpCoreModule#.24request_uri
path = ngx.var.uri -- http://wiki.nginx.org/HttpCoreModule#.24uri
filename = ngx.var.request_filename -- http://wiki.nginx.org/HttpCoreModule#.24request_filename
query_string = ngx.var.query_string -- http://wiki.nginx.org/HttpCoreModule#.24query_string
user_agent = ngx.var.http_user_agent -- http://wiki.nginx.org/HttpCoreModule#.24http_HEADER
remote_addr = ngx.var.remote_addr -- http://wiki.nginx.org/HttpCoreModule#.24remote_addr
remote_port = ngx.var.remote_port -- http://wiki.nginx.org/HttpCoreModule#.24remote_port
remote_user = ngx.var.remote_user -- http://wiki.nginx.org/HttpCoreModule#.24remote_user
remote_passwd = ngx.var.remote_passwd -- http://wiki.nginx.org/HttpCoreModule#.24remote_passwd
content_type = ngx.var.content_type -- http://wiki.nginx.org/HttpCoreModule#.24content_type
content_length = ngx.var.content_length -- http://wiki.nginx.org/HttpCoreModule#.24content_length
headers = ngx.req.get_headers() -- http://wiki.nginx.org/HttpLuaModule#ngx.req.get_headers
uri_args = ngx.req.get_uri_args() -- http://wiki.nginx.org/HttpLuaModule#ngx.req.get_uri_args
post_args = ngx.req.get_post_args() -- http://wiki.nginx.org/HttpLuaModule#ngx.req.get_post_args
socket = ngx.req.socket -- http://wiki.nginx.org/HttpLuaModule#ngx.req.socket
--方法
request:read_body() -- http://wiki.nginx.org/HttpLuaModule#ngx.req.read_body
request:get_uri_arg(name, default)
request:get_post_arg(name, default)
request:get_arg(name, default)
request:get_cookie(key, decrypt)
request:rewrite(uri, jump) -- http://wiki.nginx.org/HttpLuaModule#ngx.req.set_uri
request:set_uri_args(args) -- http://wiki.nginx.org/HttpLuaModule#ngx.req.set_uri_args
--属性
headers = ngx.header -- http://wiki.nginx.org/HttpLuaModule#ngx.header.HEADER
--方法
response:set_cookie(key, value, encrypt, duration, path)
response:write(content)
response:writeln(content)
response:ltp(template,data)
response:redirect(url, status) -- http://wiki.nginx.org/HttpLuaModule#ngx.redirect
response:finish() -- http://wiki.nginx.org/HttpLuaModule#ngx.eof
response:is_finished()
response:defer(func, ...) -- 在response返回后执行
在 application.lua 里定义log文件的位置和Level
logger:i(format, ...) -- INFO
logger:d(format, ...) -- DEBUG
logger:w(format, ...) -- WARN
logger:e(format, ...) -- ERROR
logger:f(format, ...) -- FATAL
-- format 和string.format(s, ...) 保持一致:http://www.lua.org/manual/5.1/manual.html#pdf-string.format
tail -f siva_demo.log
tail -f nginx_runtime/logs/error.log #查看 Nginx 错误日志和调试日志 的输出
- siva URL Mapping Error
- Error while doing defers
- siva ERROR
多个 siva-app 可以运行与同一nginx进程中,只要将本例子中nginx.conf内siva-app相关段配置多份即可。
某siva-app可以作为另一app的sub-app运行,在主app的application.lua内配置:
subapps={
subapp1 = {path="/path/to/subapp1", config={}},
...
}
- 简介
luaer是一个lua库,提供了一组实用函数处理迭代器,数组,表和函数以及map、reduce、chain等magic函数式编程的支持。
- 快速使用指南
_ = require 'luaer'
_.each({1,2,3}, print)
=> 1
=> 2
=> 3
- Iterator Functions and Arrays
function sq(n)
return coroutine.wrap(function()
for i=1,n do
coroutine.yield(i*i)
end
end)
end
_.each(sq(5), print)
=> 1
=> 4
=> 9
=> 16
=> 25
- 面向对象和函数式风格
_.map({1, 2, 3}, function(n) return n * 2 end)
_({1, 2, 3}):map(function(n) return n * 2 end)
使用面向对象的风格方法允许书写链式结构的调用。调用chain把对象包装成wrapped object传递给后续的所有的方法调用,并且返回wrapped objects。当你完成了计算,使用values来获取最终的价值。如下例子
local lyrics = {
{ line = 1, words = "I'm a lumberjack and I'm okay" },
{ line = 2, words = "I sleep all night and I work all day" },
{ line = 3, words = "He's a lumberjack and he's okay" },
{ line = 4, words = "He sleeps all night and he works all day" }
}
_(lyrics):chain()
:map(function(line)
local words = {}
for w in line.words:gmatch("%S+") do
words[#words+1] = w
end
return words
end)
:flatten()
:reduce({}, function(counts, word)
counts[word] = (counts[word] or 0) + 1
return counts
end):value()
还可以写得更加简洁,更有luaer范
_(lyrics):chain()
:map(function(line) return _.to_array(line.words:gmatch("%S+")) end)
:flatten()
:reduce({}, function(counts, word)
counts[word] = (counts[word] or 0) + 1
return counts
end):value()
- map _.map(iter, func) Aliases: collect
_.map({1,2,3}, function(i) return i*2 end)
=> { 2,4,6 }
- each _.each(iter, func) Aliases: for_each
_.each({1,2,3}, print)
=> {1,2,3}
- select _.select(iter, func) Aliases: filter
_.select({1,2,3}, function(i) return i%2 == 1 end)
=> {1,3}
- reject _.reject(iter, func)
_.reject({1,2,3}, function(i) return i%2 == 1 end)
=> {2}
- 快速使用指南
_ = require 'luaer'
_.each({1,2,3}, print)
- invoke _.invoke(foo, blah)
Person = {}
Person.__index = Person
function Person:new(name)
return setmetatable({ name=name }, self)
end
function Person:print()
print(self.name)
end
_.invoke({ Person:new("Tom"), Person:new("Dick"), Person:new("Harry") }, "print")
=> Calls person:print() on each Person
- pluck _.pluck(iter, property_name)
_.pluck({ {id=1}, {id=2}, {id=3} }, 'id')
=> { 1, 2, 3 }
- reduce _.reduce(iter, memo, func) Aliases: inject,foldl
_.reduce({1,2,3}, 0, function(memo, i) return memo+i end)
=> 6
- combine _.combine(iters, func)
_.combine( { {1,2,3}, {3,2,1}, {1,1,1} }, function(a,b,c) return a+b+c end)
=> {5,5,5}
_.combine( { {1,2,3}, {1,2,3}, {1,2,3} }, function(a,b,c) return a+b+c end)
=> {3,6,9}
_.combine({ {3,2,1}, {1,1,1,1,1} }, function(a,b) return a+b end)
=> {4,3,2}
- max _.max(iter, [func])
_.max({1,2,3,4})
=> 4
_.max({ {age=15}, {age=12}, {age=19} }, function(p) return p.age end)
=> {age=19}
- min _.min(iter, [func])
_.max({1,2,3,4})
=> 1
_.max({ {age=15}, {age=12}, {age=19} }, function(p) return p.age end)
=> {age=12}
- include _.include(iter, value)
_.include({1,2,3,4}, 2)
=> true
_.include({1,3,5}, 2)
=> false
_.include({1,2,3,4}, function(i) return i%2 == 0 end)
=> true
_.include({1,3,5}, function(i) return i%2 == 0 end)
=> false
- detect _.detect(iter, func)
_.detect({1,2,3,4}, func(i) return i > 3 end)
=> 4
_.detect({1,2,3,4}, func(i) return i > 7 end)
=> nil
- all _.all(iter, [func]) Aliases: every
_.all({2,4,8}, function(i) return i%2 == 0 end)
=> true
_.all({1,2,3,4}, function(i) return i%2 == 0 end)
=> false
- any _.any(iter, [func]) Aliases: some
_.any({1,2,3,4}, function(i) return i%2 == 0 end)
=> true
_.any({1,3,5}, function(i) return i%2 == 0 end)
=> false
- to_array _.to_array(iter)
_.to_array(string.gmatch("dog cat goat", "%S+"))
=> { "dog", "cat", "goat" }
- sort _.sort(iter, [comparison_func])
-- 默认比较是<
_.sort({ 3, 1, 2})
=> { 1, 2, 3 }
- reverse _.reverse(iter, [comparison_func])
_.reverse({ 1, 2, 3})
=> { 3, 2, 1 }
- flatten _.flatten(array)
_.flatten({1, {2}, {3, {{{4}}}}})
=> { 1, 2, 3, 4 }
- first _.first(array, [length]) Aliases: head
_.first({1,2,3})
=> 1
_.first({1,2,3}, 2)
=> {1,2,}
- rest _.rest(array, [start_index]) Aliases: tail
_.rest({1,2,3})
=> {2,3}
_.rest({1,2,3}, 2)
=> {3}
- slice _.slice(array, start_index, length)
_.slice({ 1, 2, 3, 4, 5 }, 2, 3)
=> { 2, 3, 4 }
- push _.push(array, item)
_.push({1,2,3}, 4)
=> {1,2,3,4}
- pop _.pop(array)
_.pop({1,2,3})
=> 3
- shift _.shift(array)
-- 删除并且返回第一个元素
_.shift({1,2,3})
=> 1
- unshift _.unshift(array, item)
-- 在数组开头插入元素
_.push({1,2,3}, 4)
=> {4,1,2,3}
- join _.join(array)
_.join({'c','a','t'})
=> "cat"
_.join({'c','a','t'}, '/')
=> "c/a/t"
- extend _.extend(destination, source)
_.extend({ name = 'moe' }, { age = 50 })
=> { name = 'moe', age = 50 }
- keys _.keys(object)
_.keys { name = "John", age = 25 }
=> { "name", "age" }
- values _.values(object)
_.values { name = "John", age = 25 }
=> { "John", 25 }
- is_empty _.is_empty(object)
_.is_empty({})
=> true
-.is_empty({ name = "moe" })
=> false
- curry _.curry(func, arg)
-- 创建curry函数
function f(a,b,c,d) return a + b^2 + c^3 + d^4 end
g = curry(f, 1)
g = g(2,3,5)
print(g) -- 657
g = curry(f, 1)
g = curry(g,2)
g = curry(g,3)
g = g(5)
print(g) -- 657
- wrap _.wrap(func, wrapper)
hello = function(name)
return "hello: "..name
end
hello = _.wrap(hello, function(func, ...)
return "before, "..func(...)..", after"
end)
hello('moe')
=> before, hello: moe, after
- compose _.compose(func1, func2, [...])
greet = function(name)
return "hi: "..name
end
exclaim = function(statement)
return statement.."!"
end
welcome = _.compose(print, greet, exclaim) -- = p(g(e())).
welcome('moe')
=> hi: moe!
-
functions _.functions()
返回此library的函数列表 -
iter _.iter()
for i in _.iter({1,2,3}) do
print(i)
end
- range _.range(start_or_length, [end], [step])
_.range(5,10):to_array()
=> { 5,6,7,8,9,10 }
_.range(10):to_array()
=> { 1,2,3,4,5,6,7,8,9,10 }
_.range(2,10,2):to_array()
=> { 2,4,6,8,10 }
_.range(10,2,-2):to_array()
=> { 10,8,6,4,2 }
- s_equal _.is_equal(o1,o2,[ignore_mt])
_.is_equal({1,2,3},{1,2,3})
=> true
_.is_equal({a=1,b=2},{a=1,b=2})
=> true
_.is_equal({a=1,b=2},{a=2,b=3})
=> false
- chain _.chain()
_({1,2,3,4}):chain():map(function(i) return i+1 end):select(function(i) return i%2 == 0 end):value()
=> { 2,4 }
- value _.value()
_({1,2,3,4}):chain():map(function(i) return i+1 end):select(function(i) return i%2 == 0 end):value()
=> { 2,4 }
- http://wiki.nginx.org/HttpLuaModule
- http://wiki.nginx.org/HttpCoreModule
- http://openresty.org
- https://github.com/fuhao715/nginx_lua_framework
• 协作式调度模型的问题
• Lua代码死循环
• I/O操作受限于Nginx模型
• 调试功能
https://github.com/fuhao715 email:[email protected] site:http://fuhao715.github.io 微信公众号:golang-python






