发布于 2015-01-04 09:05:57 | 680 次阅读 | 评论: 0 | 来源: PHPERZ

这里有新鲜出炉的精品教程,程序狗速度看过来!

aLiLua 高性能Web服务器

aLiLua 是一套基于 epoll/kqueue/Lua 构建的网络服务开发框架 aLiLua 使用 epoll 进行网络/文件IO事件读写,对Lua协程进行调度,其性能高效并且简单 Lua 语言就像 PHP 那么简单容易理解


本文为大家提供的是一份aLiLua撰写的入门手册,非常实用,感兴趣的同学参考下。

aLiLua简介

aLiLua 是一套基于 epoll/kqueue/Lua 构建的网络服务开发框架
aLiLua 使用 epoll 进行网络/文件IO事件读写,对Lua协程进行调度,其性能高效并且简单
Lua 语言就像 PHP 那么简单容易理解

入门

aLiLua 例子

一个页面代码(index.lua):

header('HTTP/1.1 200 OK')
local sock = cosocket.tcp()
local r,e = sock:connect(host, port)
r, e = sock:send(...)
r, e = sock:read(...)
echo(r)

aLiLua-coevent-module 例子

一个Lua脚本例子(cotest.lua):

local L = require('coevent') --同时引入 cosocket
L(function() --切换程序主体到 epoll loop,并执行 Lua 代码片段
-- 异步IO操作
local sock = cosocket.tcp()
local r,e = sock:connect(host, port)
r, e = sock:send(...)
r, e = sock:read(...)
print(r, e)
end)
--如把异步IO操作放在 epoll loop 所运行的代码块之外是错误的
local sock = cosocket.tcp()
local r,e = sock:connect(host, port) -- 错误,并会结束执行

运行该脚本:

$lua cotest.lua

两种模式的不同之处

其实 aLiLua 中的 cosocket 也同样依赖 coevent-module 的支持,但因程序架构不一样,aLiLua-coevent-module 模式下因为运行主体在 Lua 上,这样一来 epoll loop 就无法进行异步IO的处理,所以我们专门提供了一个接口方法,以便把程序运行主体切换到 epoll loop 上,如:L = require('coevent') 切换方法就是 L(),当然也可以用其他名字。而操作异步IO的Lua脚本需要 epoll loop 对其进行调度,所以必须把代码片段传递到 epoll loop,让其以协程的形式执行

aLiLua 模式下运行主体是 epoll loop,所有 Lua 代码均以协程的形式执行,所以不需要切换运行主体

  • aLiLua-coevent-module 专门用于书写客户端代码
  • aLiLua 则专门用于书写服务端代码

安装

aLiLua

$tar zxf alilua-*.tar.gz
$cd alilua-*
$make install clean

如需支持 luajit 请用以下 make 命令 (v0.4 开始已内置 luajit 无需单独安装,并不再支持 Lua 官方版本)

$sudo make install LUAJIT=/usr/local/lib

启动方式

$/usr/local/alilua/alilua process=4 bind=8080 daemon

process 启动多个进程 (建议以 CPU 核心数量进行设置)
bind 绑定IP端口 bind=127.0.0.1:8080
daemon 以守护进程模式运行
log 错误日志文件
accesslog 访问日志文件
host-route 虚拟主机的路由脚本
app 默认主机的脚本

aLiLua-coevent-module

$tar zxf alilua-coevent-module-*.tar.gz
$cd alilua-coevent-module-*
$make install clean

使用方式:

在 Lua 脚本中 require('coevent')

$lua 脚本.lua

也支持 luajit

$luajit 脚本.lua

aLiLua Web 开发框架

*注: 以下方法仅在 aLiLua 模式下被支持

header

语法: header(string|table)

向客户端输出 HTTP 头信息,该方法只支持一个调用参数,但参数类型可以是 string 或一个包含 string 的 table

如:

header('HTTP/1.1 200 OK')
header({'HTTP/1.1 200 OK', 'Content-Type: text/html'})

echo

语法: echo(string|table)

向客户端输出 HTTP 头信息,该方法支持多个调用参数,如只传入一个参数,那么参数类型可以是 string 或一个包含 string 的 table

如:

echo('hello world')
echo('hello', 'world')
echo({'hello', 'world'})

die

语法: die(string|table)

结束当前页面处理逻辑并输出结果到客户端,如有传入参数将转入 echo 方法处理

sendfile

语法: sendfile(path)

向客户端发送文件内容,如发送的是文本文件且客户端支持 gzip 压缩协议则会自动压缩后再发送

setcookie

语法: setcookie( name [, value [, expire = 0 [, path [, domain [, bool secure [, bool httponly]]]]]]

定义一个 cookie 和 header 一起发送到浏览器。如 value 为 nil 则表示删除某个 cookie

session

语法: t = session()

如果是客户端第一次请求将自动生成一个 session table,可以在该 table 中设置具体内容,并会自动存储于 aLiLua server 的共享内存中。以备第二次请求来访时可使用。如:

local _SESSION = session() --如是第一次访问,会自动创建
print(_SESSION.name) --如是第一次访问,_SESSION.name == nil
_SESSION.name = 'hello' --对 table 进行任何修改都将自动存储于共享内存中

session 数据最长可存储 1800 秒,这具体因缓存大小而决定,如开设的缓存大小不足以存放全部数据,会自动删除少访问或时间过长的数据

注意: aLiLua server 并不对 session 数据在并发设置的情况下提供一致性支持

环境变量

headers

访问请求的头信息,如:

headers = {
'accept-language'='zh-CN,zh;q=0.8',
'connection'='keep-alive',
'accept'='text/html,application/xhtml+xml',
'accept-encoding'='gzip,deflate,sdch',
'host'='localhost',
'method'='GET',
'uri'='/',
'query'='key=value'
'cache-control'='max-age=0',
'user-agent'='Mozilla/5.0 Chrome/28.0.1500.36 Safari/537.36',
}

_GET

访问请求 URL 上的变量参数,如:

-- GET http://localhost/?key=value&key2=2
_GET = {
'key' = 'value',
'key2' = '2',
}

_COOKIE

访问请求中的cookies变量参数,如:

-- curl -H 'Cookie: key=value; key2=2;' http://localhost/
_COOKIE = {
'key' = 'value',
'key2' = '2',
}

_POST

POST表单中的变量参数,如:

-- curl -d 'key=value&key2=2' http://localhost/
_POST = {
'key' = 'value',
'key2' = '2',
}

_POST 变量参数支持普通表单 x-www-form-urlencoded 格式和 multipart/form-data 文件上传的格式。表单的文件域则以table形式访问,如:

{
'key' = 'value',
'file field' = {
'filename' = 'text.txt',
'type' = 'text/paint',
'size' = 10,
'data' = 'abcdefghij',
}
}

get_post_body

语法: get_post_body()

获取该请求的原始内容(注意: 已被解析的表单请求无法获取原始内容),如:

-- PUT http://localhost/text.txt
local body = get_post_body()
print(body) -- == 'abcdefghij'

jsonrpc_handle

语法: jsonrpc_handle(json, api table)

提供 Json RPC 接口服务

例子:

--File: /api-demo.lua
local apis = {
demo = { -- json-rpc v2.0
hello = function(a, b)
return a+b
end,
},
hello = function(a, b) -- json-rpc v1.0
return a+b
end,
}
jsonrpc_handle(json_decode(get_post_body()), apis)

JavaScript 调用例子:

$.jsonRPC.setup({endPoint: '/api-demo'});
$.jsonRPC.request('hello', {
params: [123, 456],
success: function(result) {console.log(result.result);},
error: function(result) {console.log('error');}
});

cosocket 网络IO扩展

cosocket.tcp

语法: cok, err = cosocket.tcp([ssl = true])

创建一个TCP连接,并提供以下操作方法:

  • connect
  • send
  • receive
  • close
  • settimeout

如需创建 ssl 连接,请传入 boolean 参数 true

cok:connect

语法: ok, err = cok:connect(host, port, [pool_size, 'pool_key'])

连接到服务器,支持IP连接也支持 UNIX Domain Socket 连接。cosocket 默认所有连接是持久的,并支持连接池功能,如需开启连接池可在 connect 方法调用时传入 pool_size 连接池大小即可。但对于 MySQL 类对连接有身份验证要求,则可设置 pool_key 连接池组名来避免错误分配

注: 连接池中的连接生命时间为60秒,会自动关闭过期的连接

cok:send

语法: ok, err = cok:send(data)

在当前连接发送数据,调用参数支持 string 和 table 或多个 string 参数

cok:receive

语法: data, err = cok:receive(pattern?)

在当前连接读取数据,调用参数可以是 'l', 'a' 或 大小(数字)

    '*l': 读取一行数据,以 \n 分割(或者是连接已断开,缓冲区中剩余且不带\n分割的数据)
    '*a': 读取缓冲区中的数据,如缓冲区为空则发起一次网络读取的系统操作,协程休眠到数据返回时
    num: 读取指定字节大小的数据,如连接已断开则可能返回少于指定的读取大小

**如不指定参数,默认以 'a'读取缓冲区数据的模式执行

cok:colse

语法: ok, err = cok:close()

关闭当前连接,如该连接有设置连接池则会放入连接池内,并不会真正关闭

当然这个操作不是必须的,当该连接所在的协程结束也将自动关闭

cok:settimeout

语法: cok:settimeout(sec)

设置连接超时(单位: 秒),请在创建连接后设置超时时间,超时时间对网络连接、读取和写入都有效

**注: 连接超时时间是 settimeout 设定值的一半

cok:setkeepalive

语法: cok:setkeepalive(size, ['pool key']) 设置长连接的连接池大小和组名

WebSocket

websocket_accept

语法: websocket_accept([function])

把当前请求升级为 WebSocket。可传入 function 作为事件循环的协程。

websocket_send

语法: r,err = websocket_send(string [, typ] [,fin])

向客户端发送消息。注意: 检查 err 值,因一个 WebSocket 不允许同时发送消息,如遇到错误,请再次发送

typ 为 boolean 值,typ == true 表示发送二进制数据 fin 为 boolean 值,用于发送连续多帧操作

on

语法: function on(data ,typ ,fin ) ... end

typ 为 int 值,1 表示 data 是文本数据,2 为二进制数据。 typ = 0 则表示此为中间帧,数据并未完全接收完整,需等待 fin = 1 的结束帧

fin 为 boolean 值, true 表示此帧为结束帧,数据已完成接收

接收客户端传来的消息

例子:

function on(data) --接收客户端消息,注意: 必须声明在 websocket_accept 调用之前
websocket_send('['..data..']') --向客户端发送消息
end
function loop() --事件循环,可以不断的向客户端发送消息
while true do
websocket_send('text')
sleep(1)
--发送一个多帧操作
websocket_send(data_part_1, true, false) -- fin = 0
websocket_send(data_part_2)
websocket_send(data_part_3)
-- ... 可重复多次发送
websocket_send(data_part_n, true, true) -- fin = 1 表示结束
end
end
--升级为 WebSocket,注意: 为保障安全,你需要先检验 cookie 或 session 以判断是否应该接收该请求
websocket_accept(loop)

协程

newthread

语法: newthread(function)

创建一个协程并运行,使用该方法创建协程才能保障数据交互不会混乱

wait

语法: wait(thread|table)

等待协程结束,并获取返回结果。该方法支持多种协程传递方式,如:

local t1 = newthread(function)
local t2 = newthread(function)
local t2 = newthread(function)
 
-- 方式 1
local rt, er = wait(t1)
local rt, er = wait(t2)
local rt, er = wait(t3)
 
-- 方式 2
local rts = wait(t1, t2, t3)
rt, er = rts[1][1], rts[1][2]
 
-- 方式 3
local rts = wait({t1, t2, t3})
rt, er = rts[2][1], rts[2][2]
 
-- 对于方式 2 和 3,所获取到的协程结果以 table 的形式返回

swop

语法: swop()

短暂睡眠 (大概 0.001秒),使得大的循环体中可以把 CPU 运行权限交予 epoll loop 处理异步IO事件,以避免堵塞

如:

local j = 0
for i=1,100000 do
swop()
j = j + 1
end

字符串处理

trim

语法: s = trim(string)

语法: s = string:trim()

去除头尾空字符

startsWith

语法: r = string:startsWith(prefix [, true])

判断字符串是否以某字符串开头。第二个参数为 true 表示不区分大小写

去除头尾空字符

endsWith

语法: r = string:endsWith(suffix [, true])

判断字符串是否以某字符串结尾。第二个参数为 true 表示不区分大小写

strip

语法: s = strip(html)

去除HTML标记

md5

语法: s = md5(string)

对字符串进行 md5 加密

iconv

语法: s = iconv(string, form charset [, to charset])

转换字符串编码(to charset 默认为 utf-8)

iconv_strlen

语法: len = iconv_strlen(string, charset)

获取字符串长度(charset 默认为 utf-8)

iconv_substr

语法: str = iconv_substr(string, start [, length [, charset]])

截取字符串(charset 默认为 utf-8)

如 length 为负数,则表示截取到字符串长度向前移 n 位

转换字符串编码

escape

语法: s = escape(string)

对特殊字符进行编码,NULL (ASCII 0), \n, \r, \, ', ", 和 Control-Z

escape_uri

语法: s = escape_uri(string)

对字符串进行 URL 编码

unescape_uri

语法: s = unescape_uri(string)

对字符串进行反 URL 编码

base64encode

语法: s = base64encode(string)

对字符串进行 base64 编码

base64decode

语法: s = base64decode(string)

对字符串进行 base64 反编码

json_encode

语法: s = json_encode(value)

对对象进行 JSON 格式编码

json_decode

语法: t = json_decode(string)

对字符串进行 JSON 格式反编码

nl2br

语法: s = nl2br(string)

把 \n 换行符转换为 HTML 代码 <br/>

explode

语法: t = explode(string, regex)

对字符串以 regex 中的字符进行切分,支持多个字符,就像 strtok。如:

local t = explode('aaa:bbb;ccc' , ':;')
-- t = {'aaa', 'bbb', 'ccc'}

implode

语法: s = implode(table, sep)

对 table 中的内容进行拼接。如:

local s = implode({'aaa','bbb', 'ccc'}, ':')
-- s = 'aaa:bbb:ccc';

random_string

语法: s = random_string([length])

产生一个随机字符串,如不设置 length 默认产生一个由 32 个 [0~9|a-f] 字符组成的字符串。(length > 0 && length < 4096)

printf

语法: printf(formatstring, [values ...])

输出具有特定格式的字符串, 函数的第一个参数是格式(formatstring), 之后是对应格式中每个代号的各种数据. 由于格式字符串的存在, 使得产生的长字符串可读性大大提高了. 这个函数的格式很像C语言中的printf().函数string.format在用来对字符串进行格式化的时候,特别是字符串输出,是功能强大的工具。 这个函数有两个参数,你完全可以照C语言的printf来使用这个函数。第一个参数为格式化串:由指示符和控制格式的字符组成。指示符后的控制格式的字符 可以为:十进制'd';十六进制'x';八进制'o';浮点数'f';字符串's'。在指示符'%'和控制格式字符之间还可以有其他的选项:用来控制更详 细的格式,比如一个浮点数的小数的位数:

格式字符串可能包含以下的转义码:

%c - 接受一个数字, 并将其转化为ASCII码表中对应的字符

%d, %i - 接受一个数字并将其转化为有符号的整数格式

%o - 接受一个数字并将其转化为八进制数格式

%u - 接受一个数字并将其转化为无符号整数格式

%x - 接受一个数字并将其转化为十六进制数格式, 使用小写字母

%X - 接受一个数字并将其转化为十六进制数格式, 使用大写字母

%e - 接受一个数字并将其转化为科学记数法格式, 使用小写字母e

%E - 接受一个数字并将其转化为科学记数法格式, 使用大写字母E

%f - 接受一个数字并将其转化为浮点数格式

%g(%G) - 接受一个数字并将其转化为%e(%E, 对应%G)及%f中较短的一种格式

%q - 接受一个字符串并将其转化为可安全被Lua编译器读入的格式

%s - 接受一个字符串并按照给定的参数格式化该字符串

sprintf

语法: s = sprintf(formatstring, [values ...])

生成具有特定格式的字符串,具体可参考上面的 printf

dump

语法: s = dump(obj [,true])

遍历对象并返回构造字符串,或打印输出。

内存缓存

cache_set

语法: r = cache_set(key, value)

添加一条新缓存记录,返回 true or false。value 值可以是 string / number / boolean 或 table 类型

cache_get

语法: r = cache_get(key)

获取一条缓存记录,返回内容或 nil

cache_del

语法: r = cache_del(key)

删除一条缓存记录,返回 true or false

模板引擎

dotemplate

语法: rts, err = dotemplate(html file [, true])

渲染模板文件并输出,如第二参数为 true 则返回 HTML 内容

HTML 模板例子:

<body>
{{if user then}}
Hi, {{=user}}.
{{else}}
<a href="/login">Login</a>
{{end}}

#for loop
{{for k,v in ipairs(datas) do}}
{{=v.name}}<br/>
{{end}}
</body>
{{include footer.html}}

钩子和过滤器

hook

语法: hook(target fun, func)

把一个函数追加到目标函数结束时运行,业务函数中如有返回值将自动替换原始函数的返回内容

例子:

function tf(p1)
return p1*2
end

hook(tf, function(rt1) -- hooker
print(rt1) -- 2
end)

hook(tf, function(rt1) -- filter
return rt1*2
end)

print(tf(1)) -- 4

文件目录操作

is_dir

语法: r = is_dir('/a')

判断路径是否为目录,返回 true 表示是目录,或 false

is_file

语法: r = is_file('/a')

判断路径是否为文件,返回 true 表示是文件,或 false

mkdir

语法: r = mkdir('/a')

创建目录,返回 true 表示创建成功,或 nil

rmdir

语法: r = rmdie('/a')

删除目录,返回 true 表示删除成功,或 nil

readdir

语法: t = readdir('/a')

读取目录下的文件列表,以 table 类型返回

stat

语法: infos = stat('/a')

获取文件信息,以 table 类型返回

unlink

语法: r = unlink('/a')

删除文件,返回 true 为成功

crypto 加密扩展

具体可参阅扩展官网文档 [http://luacrypto.luaforge.net/manual.html#reference]



最新网友评论  共有(0)条评论 发布评论 返回顶部

Copyright © 2007-2017 PHPERZ.COM All Rights Reserved   冀ICP备14009818号  版权声明  广告服务