引言
公司需要做一個類似站長統計的項目,給了很多種方案;其中有個方案就是利用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'])) endend--擷取單個集合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.comnil資料: 2資料:哈哈www.baidu.comnil資料: 3資料:哈哈www.baidu.comnilok---- -1delete ok---1[root@leoleo apps]#
這個demo 就這樣寫好了
3.後話**
使用lua_nginx+mongodb這個方案最後被我們項目組否決了,原因呢。
雖然用lua 可以實現將網頁上收集到使用者操作行為通過的nginx寫入mongodb中;但是lua 直接操作mongodb 有很多缺陷,比如這裡是當執行個體,就是每次寫入都要擷取一個串連執行個體,開啟一個串連;網頁上的行為收集是一個高並發的,顯然這個demo 這樣的是不滿足的。