引言
公司需要做一个类似站长统计的项目,给了很多种方案;其中有个方案就是利用ngx_lua(openResty) 调用mongodb 来做日志的存储;在项目之前启动之前,自己研究了这个方案的可行性,编写写了一个demo。
1.准备
(1)看过openresty的都应该知道,openresty只提供redis 的module模块,没有提供mongoDB的驱动模块(不懂openresty;可以看下openresty最佳实现);所以我们这边要先提供一个驱动,去中文论坛找了一番;发现了几个不错的结合lua 的mongodb的libarary项目:
在github上有lua-resty-mongol、还有wuxianglong的lua-mongo等项目;综合考虑了之后,打算用lua-resty-mongol 这个项目;因为这个项目本身就是为了兼容openresty设计的。
(2)这个demo本身是基于openresty中编写的,所以在你的系统上需要安装openresty,我这里用的是centos 6.5 的系统,至于具体的下载 安装,这里就不详细介绍了;如果有需要的话可以看看这篇文章http://www.52yunwei.cn/?p=415;
(3) 前面说到过 lua-resty-mongol 项目,我们要下载github下的这个项目;下载后编译,
make install
把解压的包 解压到 lualib/resty/中下
如果你的openresty默认安装的话是在/usr/local/openresty/lialib 下,我这边的路径是在 opt/openresty/lualib/resty 下;
(4)就是对应的mongodb 服务,我这里的mongodb 服务是在自己的虚拟机中,如果没有的话,先去官网下载一个mongodb,然后启动
这里就不详细介绍了
2.实例
(1).之前lua-resty-mongol 项目还要有一个配置项,就是要在nginx.conf 中配置上 你mongol 的默认初始化路径
# 之前说过了,你要是是默认的openresty的话 这里就可以配置是/usr/local/openresty/lialib/?/了#
lua_package_path '/opt/opentresty/lualib/?/init.lua;;';
(2). 对于ngx中的整个nginx.conf 配置如下
user nginx;
worker_processes 4;
pid /opt/logs/nginx/nginx.pid;
error_log /opt/logs/nginx/error.log;
events {
use epoll;
worker_connections 10240;
}
http {
include mime.types;
#default_type 'text/html';
#定义日志格式
#default_type 'application/octet-stream';
#指定lua_mongol 初始化默认路径
default_type 'text/plain';
lua_package_path '/opt/opentresty/lualib/?/init.lua;;';
charset utf-8;
error_log /opt/logs/nginx/error.log;
access_log off;
#log_format main '$remote_addr\t$uid_got$uid_set\t$http_host\t$time_iso8601\t$request\t$status\t$body_bytes_sent\t$http_referer\t$request_time\t$http_user_agent';
#log_format tick '$msec^A$remote_addr^A$u_domain^A$u_url^A$u_title^A$u_referrer^A$u_sh^A$u_sw^A$u_cd^A$u_lang^A$http_user_agent^A$u_utrace^A$u_account';
log_format tick '$msec ^A^ $remote_addr ^A^ $u_domain ^A^ $u_url ^A^ $u_title ^A^ $u_referrer ^A^ $u_sh ^A^ $u_sw ^A^ $u_cd ^A^ $u_lang ^A^ $http_user_agent ^A^ $u_utrace ^A^ $u_account';
client_max_body_size 100m;
sendfile on;
keepalive_timeout 60;
fastcgi_intercept_errors on;
proxy_connect_timeout 60;
proxy_send_timeout 90;
proxy_read_timeout 1800;
large_client_header_buffers 4 128k;
proxy_ignore_client_abort on;
gzip on;
gzip_min_length 10k;
gzip_buffers 4 16k;
gzip_comp_level 2;
gzip_types text/plain text/javascript application/javascript application/x-javascript text/css application/xml application/octet-stream;
gzip_vary on;
#userid
userid on;
userid_name UUID;
userid_path /;
userid_expires max;
include _ext.conf;
include apps/*.conf;
}
(3).配置需要的location
从上面是nginx.conf 中可以看出,我这里的server都是在apps子文件夹创建,我在apps 文件中有个叫
_mongo.conf 的配置文件,里面配置这lua 调用mongodb 的location
server {
listen 8081;
server_name 192.168.1.128;
#关闭lua_code 缓存
lua_code_cache off;
location /lua {
content_by_lua_file /opt/openresty/lualib/resty/mongol/test_lua.lua;
}
location /lua_mongo {
content_by_lua_file /opt/openresty/lualib/resty/mongol/test_mongol.lua;
}
location /lua_test {
set $test "hello world";
#使用acess 阶段做准入条件处理
access_by_lua '
if (ngx.var.test =="hello world") then
ngx.say("验证通过"..ngx.var.test)
else
ngx.log(ngx.ERR,"验证失败","")
end
';
#业务处理
content_by_lua '
ngx.header.content_type ="text/plain";
local a, b =1;
ngx.say(ngx.var.test..a);
';
}
}
location lua_mongo 中content_by_lua_file 是lua调用了mongodb具体操作
执行在/opt/openresty/lualib/resty/mongol/下的test_mongol.lua 文件,该文件的内容是:
local mongo =require "resty.mongol"
local json = require "cjson"
--获取连接对象
local conn =mongo:new()
conn:set_timeout(1000)
--获取连接客户端
local ok,err =conn:connect("192.168.1.128",27017)
if not ok then
ngx.say("connect failed"..err)
end
--获取数据库
local db = conn:new_db_handle("leo")
--用户授权
local ok ,err = db:auth("zjf","zjf123456")
if ok then
ngx.say("user auth success"..ok)
end
--获取集合
local coll = db:get_col("leonardo")
--获取document集合
local cursor = coll:find({})
--json 转码
--
function json_decode( str )
local json_value =nil
pcall(function (str) json_value = json.decode(str) end, str)
return json_value
end
--循环
for index,item in cursor:pairs() do
ngx.say('数据: '..index)
if not item['url'] then
ngx.say('数据:'..item["title"])
else
ngx.say('数据:'..item["title"]..item['url'])
ngx.say(json_decode(item['url']))
end
end
--获取单个集合
local res =coll:find_one({key = 150})
if res then
ngx.say(res['title'])
end
--插入集合
local bson1 ={title ='哈哈',url = 'www.baidu.com',key = 300};
--插入table 表中
local docs ={bson1};
local rsOk,err =coll:insert(docs,0,0)
if err then
ngx.say('error--'..err)
else
ngx.say('ok---- '..rsOk)
end
--删除操作
local deOk,err = coll:delete({title ='你好'},0,0)
if err then
ngx.say('delete error--'..err)
else
ngx.say('delete ok--'..deOk)
end
--关闭连接
if conn then
conn:close()
end
到这里一个完整的例子就写好了 。
(4).启动mongodb 服务,启动nginx 服务
[root@leoleo apps]# curl 192.168.1.128:8081/lua_mongo
数据: 1
数据:哈哈www.baidu.com
nil
数据: 2
数据:哈哈www.baidu.com
nil
数据: 3
数据:哈哈www.baidu.com
nil
ok---- -1
delete ok---1
[root@leoleo apps]#
这个demo 就这样写好了
3.后话**
使用lua_nginx+mongodb这个方案最后被我们项目组否决了,原因呢?
虽然用lua 可以实现将网页上收集到用户操作行为通过的nginx写入mongodb中;但是lua 直接操作mongodb 有很多缺陷,比如这里是当实例,就是每次写入都要获取一个连接实例,开启一个连接;网页上的行为收集是一个高并发的,显然这个demo 这样的是不满足的。