OpenResty-Lua模块开发
原创地址:http://jinnianshilongnian.iteye.com/blog/2190344
常用命令:
# vi /usr/local/nginx/conf/nginx.conf
# vi /usr/server/example/example.conf
# /usr/local/nginx/sbin/nginx -s reload & tail -f /usr/local/nginx/logs/error.log
目录:
1.OpenRestry(Nginx+Lua)开发环境
2.OpenRestry开发入门
3.Lua模块开发
3.1 常用Lua开发库1-redis、mysql、http客户端 附-Redis/SSDB+Twemproxy安装与使用
3.2 常用Lua开发库2-JSON库、编码转换、字符串处理
3.3 常用Lua开发库3-模板渲染
4.实战
4.1 Web开发实战1——HTTP服务
4.2 Web开发实战2——商品详情页
4.3 流量复制/AB测试/协程
一.Lua模块开发
在实际开发中,不可能把所有代码写到一个大而全的lua文件中,需要进行分模块开发;而且模块化是高性能Lua应用的关键。
使用require第一次导入模块后,所有Nginx进程全局共享模块的数据和代码,每个Worker进程需要时会得到此模块的一个副本(Copy-On-Write),即模块可以认为是每Worker进程共享而不是每Nginx Server共享;
另外注意之前我们使用init_by_lua中初始化的全局变量是每请求复制一个;
如果想在多个Worker进程间共享数据可以使用ngx.shared.DICT或如Redis之类的存储。
1.大量第三方开发库如cjson、redis客户端、mysql客户端:/usr/server/example/lualib
# ls -l /usr/server/example/lualib
cjson.so
resty/
aes.lua
core.lua
dns/
lock.lua
lrucache/
lrucache.lua
md5.lua
memcached.lua
mysql.lua
random.lua
redis.lua
……
2.如何使用
1.在使用前需要将库在nginx.conf中导入:#lua模块路径,其中”;;”表示默认搜索路径,默认到/usr/local/nginx下找
lua_package_path "/usr/server/example/lualib/?.lua;;"; #lua 模块
lua_package_cpath "/usr/server/example/lualib/?.so;;"; #c模块
2.使用方式是在lua中通过如下方式引入
local cjson = require(“cjson”)
local redis = require(“resty.redis”)
3.案例:
(1) 开发一个简单的lua模块。
# vi /usr/server/example/lualib/module1.lua
local count = 0
local function hello()
count = count + 1
ngx.say("count : ", count)
end
local _M = {
hello = hello
}
return _M
开发时将所有数据做成局部变量/局部函数;通过 _M导出要暴露的函数,实现模块化封装。
(2) 使用该模块
test_module_1.lua
# vi /usr/server/example/lua/test_module_1.lua
local module1 = require("module1")
module1.hello()
使用 local var = require("模块名"),该模块会到 lua_package_path 和 lua_package_cpath 声明的的位置查找我们的模块,对于多级目录的使用require("目录1.目录2.模块名")加载。
(3)配置example.conf
# vi /usr/server/example/example.conf
location /lua_module_1 {
default_type 'text/html';
lua_code_cache on;
content_by_lua_file /usr/server/example/lua/test_module_1.lua;
}
访问如 http://192.168.1.106/lua_module_1 进行测试,会得到类似如下的数据,count会递增
count : 1
count :2
……
count :N
此时可能发现count一直递增,假设我们的worker_processes 2,我们可以通过kill -9 nginx worker process 杀死其中一个Worker进程得到count数据变化。
假设我们创建了vi /usr/example/lualib/test/module2.lua模块,可以通过local module2 = require("test.module2")加载模块
基本的模块开发就完成了,如果是只读数据可以通过模块中声明local变量存储;如果想在每Worker进程共享,请考虑竞争;如果要在多个Worker进程间共享请考虑使用ngx.shared.DICT或如Redis存储。
目前对于互联网公司不使用Redis的很少,Redis不仅仅可以作为key-value缓存,而且提供了丰富的数据结果如set、list、map等,可以实现很多复杂的功能;但是Redis本身主要用作内存缓存,不适合做持久化存储,因此目前有如SSDB、ARDB等,还有如京东的JIMDB,它们都支持Redis协议,可以支持Redis客户端直接访问;而这些持久化存储大多数使用了如LevelDB、RocksDB、LMDB持久化引擎来实现数据的持久化存储;京东的JIMDB主要分为两个版本:LevelDB和LMDB,而我们看到的京东商品详情页就是使用LMDB引擎作为存储的,可以实现海量KV存储;当然SSDB在京东内部也有些部门在使用;另外调研过得如豆瓣的beansDB也是很不错的。具体这些持久化引擎之间的区别可以自行查找资料学习。
二.常用Lua开发库1-redis、mysql、http客户端 附-Redis/SSDB+Twemproxy安装与使用
1.附-Redis/SSDB+Twemproxy安装与使用
1.1Redis安装与使用
见:Redis介绍及安装集群使
1.2 twemproxy
见:Twemproxy-缓存代理分片机制
因为我们所有的Twemproxy配置文件规则都是一样的,因此我们应该将其移到我们项目中。
cp /usr/servers/twemproxy-0.4.0/conf/nutcracker.yml /usr/example/
6.Redis执行Lua脚本
Redis客户端支持解析和处理lua脚本,因为Redis的单线程机制,我们可以借助Lua脚本实现一些原子操作,如扣减库存/红包之类的。此处不建议使用EVAL直接发送lua脚本到客户端,因为其每次都会进行Lua脚本的解析,而是使用SCRIPT LOAD+ EVALSHA进行操作。未来不知道是否会用luajit来代替lua,让redis lua脚本性能更强。
到此基本的Redis知识就讲完了。
1.3 SSDB
见:SSDB介绍与使用