与OpenResty及其原作者邂逅
OpenResty是一个优秀的开源项目,作者是章亦春。官网是http://openresty.org/en/。这已经是我第二次在公司项目中使用它展开业务了。分享使用经历的时候,顺便帮春哥推广一下:
其实,我想说的是春哥真的像知乎上传的一样,热情,专业。同时也希望大家都到邮件列表里问问题,像我一样莽莽撞撞直接私发邮件,确实显得不大矜持,汗!
我们后台选用的第三方库和文件
我们后台采用: openresty框架+lua脚本+c函数+mongoDB数据库.这其中的技术细节我们都论证过,现把他们都链接粘出来。
- Lua调用mongo的驱动 https://github.com/bigplum/lua-resty-mongol
- OpenResty官网下载 http://openresty.org/en/download.html
- Lua调用C函数http://blog.csdn.net/wuxiangege/article/details/52220015
架构思想和坑
想成为高手,得首先学会跟高手一样思考问题,我觉得这也是每一个有志向的程序员练级所必须经历的。下面是我跟架构师的工作交流时,从正面和侧面所总结的,即为什么要采用:openresty框架+lua脚本+c函数+mongoDB数据库作为后台的原因。
- 对于openresty,我的理解是openresty = web服务器nginx + lua虚拟机。它的好处官网都有,如”能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关”
- 。对于我们的项目,架构师看中的似乎是openresty对nginx的lua扩展,因为接下来我们会调用第三方提供的dll文件,即lua调C。在操作数据库方面,openresty提供的很多精良的数据库驱动,如memcached、mysql、redis。虽然官方没有提供mongodb,但bigplum大神已经做到了,见上方链接。经过测试,bigplum大神提供的测试文件可用,对文档的数据库、集合和文档的增删改查均可用,点个赞。
- openresty官方已经实现了redis的驱动,我们为什么选用mongodb而不是redis?架构师是这么回答我的,1、redis太重了,mongodb稍微轻一点点,而且我们的项目是在win上部署的对内存有一些要求,架构师希望它在普通的pc上也能运行;2、项目要求数据库同步,也就是a/b/c/d/四台机器数据保持数据的一致性。在这一点上,我们采取的是mongodb的副本集。
讨论太廉价了,直接上代码吧
nginx.conf文件
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
lua_package_path '/c/Users/wuxian/Desktop/BlackHole/openresty-1.9.15.1-win32/lualib/resty/mongol/init.lua;;';
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
location / {
#解决跨域问题
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Headers' 'Content-Type';
return 204;
}
if ($request_method = 'POST') {
add_header 'Access-Control-Allow-Origin' '*';
}
#处理业务的文件
default_type text/html;
content_by_lua_file './lua_script/app.lua';
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
app.lua文件
local mongo = require "resty.mongol"
local cjson = require "cjson.safe"
-----------------------------------------------------------------------------
--lua call c module
square = package.loadlib("./ADD.dll", "isquare")
alert = package.loadlib("./ADD.dll", "alert")
add = package.loadlib("./ADD.dll", "l_add")
sub = package.loadlib("./ADD.dll", "l_sub")
-----------------------------------------------------------------------------
--Connect mongodb database
conn = mongo:new()
ok, err = conn:set_timeout(5000)
ok, err = conn:connect("127.0.0.1", 27017)
if not ok then
ngx.say("connect failed: "..err)
end
local db = conn:new_db_handle("tamigroup")
if not db then
ngx.say(db)
end
local col = db:get_col("number")
-----------------------------------------------------------------------------
-- Process request data
local function post_request(data)
local result = {}
local request = cjson.decode(data)
if not request or not request["cmd"] then
result["result"] = "failed"
result["reason"] = "no 'cmd' field or not a json struct"
return result
end
local sql_str = ""
--测试
if request["cmd"] == "demo" then
local result = {}
result["result"] = "ok"
result["reason"] = "This is a test cmd"
return result
end
--插入人员
if request["cmd"] == "add" then
local result = {}
local t = {}
table.insert(t, {name=request["name"], id=request["id"]})
r, err = col:insert(t, nil, true)
if not r then
result["result"] = "insert failed: "..err
else
result["result"] = "ok"
end
return result
end
--删除人员
if request["cmd"] == "delete" then
local result = {}
r, err = col:delete({name=request["name"]}, nil, true)
if not r then
request["result"] = "delete failed: "..err
else
result["request"] = "ok"
end
return result
end
--修改人员
if request["cmd"] == "update" then
local result = {}
r,err = col:update({name=request["old"]},{name=request["new"]}, nil, nil, true)
if not r then
request[result] = "update failed: "..err
else
request[result] = "ok"
end
return result
end
--查询人员
if request["cmd"] == "find" then
local result = {}
r = col:find({name=result["name"]})
r:limit(1)
for i , v in r:pairs() do
result["data"] = v["name"];
end
if not result["data"] then
result["result"] = "He or she don't exist"
else
result["result"] = "ok"
end
return result
end
--调用c模块
if request["cmd"] == "lua_call_c" then
local result = {}
result["data"] = square(12)
result["add"] = add(100, 50)
result["result"] = "ok"
return result
end
end
-----------------------------------------------------------------------------
-- Parse request body
ngx.req.read_body()
local method = ngx.var.request_method
if method ~= "POST" then
local result = {}
result["result"] = "failed"
result["reason"] = "use POST method"
ngx.say(cjson.encode(result))
else
local data = ngx.req.get_body_data()
ngx.say(cjson.encode(post_request(data)))
end
后记
整个项目的大致思路就是这酱紫的。在这里面,我们曾调研lua在win下怎样调用c;openresty怎样驱动mongodb;mongodb搭建集群数据怎么保持一致…….也傻乎乎的给春哥和云风写过邮件等等。最终前期调研终于顺利完成,累。