OpenResty连接mysql
一、Openresty数据库请求理念
Openresty这个nginx变种产品之前早有耳闻,直到最近花了点时间读了下《OpenResty最佳实践》(OpenResty-Best-Practices)一书。其实Openresty引入lua实现的一个比较强的一个功能就是和后端mysql、redis 等数据库的连接更直接。减少了中间其他动态语言再去处理的过程,可以通过nginx直接返回或修改数据,也便于实现API。
1常规请求步骤:
2nginx ---> php / tomcat ---> mysql
3openresty请求步骤:
4openresty ---> mysql
上面只是简单描述了下单向请求,同样数据返回也一样。而动态语言在通过页面请求时,其处理的过程相对于nginx这种非阻塞模式是很慢的。
懒得画图了,这里盗用JD架构里的一张图。可以看到nginx+lua取标签类数据时,直接从redis去取,在redis缓存中不存在的,才会回源到tomcat再去取,tomcat从后端数据库查询到相关数据库再更新到redis里去。便于下次查询命中。
二、Openresty连接mysql
Openresty和mysql连接有两种方法:一种是使用其早期加入的Drizzle 模块(c语言实现),另一种是使用lua mysql模块。
1、Drizzle 模块
先看下Drizzle模块,官方给出的操作示例如下:
1http {
2 upstream backend {
3 drizzle_server 127.0.0.1:3306 protocol=mysql dbname=ngx_test user=ngx_test password=ngx_test;
4 drizzle_keepalive max=10 overflow=ignore mode=single;
5 }
6 server {
7 listen 8080;
8 location @cats-by-name {
9 set_unescape_uri $name $arg_name;
10 set_quote_sql_str $name;
11 drizzle_query 'select * from cats where name=$name';
12 drizzle_pass backend;
13 rds_json on;
14 }
15 location @cats-by-id {
16 set_quote_sql_str $id $arg_id;
17 drizzle_query 'select * from cats where id=$id';
18 drizzle_pass backend;
19 rds_json on;
20 }
21 location = /cats {
22 access_by_lua '
23 if ngx.var.arg_name then
24 return ngx.exec("@cats-by-name")
25 end
26 if ngx.var.arg_id then
27 return ngx.exec("@cats-by-id")
28 end
29 ';
30 rds_json_ret 400 "expecting \"name\" or \"id\" query arguments";
31 }
32 }
33}
可以看出其调用非常简单,不过其默认在安装时并未集成该模块“ This ngx_drizzle module is not enabled by default. You should specify the –with-http_drizzle_module option while configuring OpenResty ”,其安装步骤有点麻烦,具体可以参看官方手册。为防止SQL注入等,上面有调用两个方法:set_unescape_uri、set_quote_sql_str ,这两个方法不是nginx本身的方法,是openresty里实现的方法。该模块更新并不频繁,感觉也并不是openresty官方主推的模块。更多也可以参看openresty在github上的关于该模块的部分: 。
2、lua实现的方法
先看下一个CRUD代码,如下:
1[root@localhost ~]# cat /usr/local/openresty/nginx/lua/test_mysql.lua
2local function close_db(db)
3 if not db then
4 return
5 end
6 db:close()
7end
8local mysql = require("resty.mysql")
9local db, err = mysql:new()
10if not db then
11 ngx.say("new mysql error : ", err)
12 return
13end
14db:set_timeout(1000)
15local props = {
16 host = "127.0.0.1",
17 port = 3306,
18 database = "test",
19 user = "root",
20 password = "asdf"
21}
22local res, err, errno, sqlstate = db:connect(props)
23if not res then
24 ngx.say("connect to mysql error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate)
25 return close_db(db)
26end
27local drop_table_sql = "drop table if exists test"
28res, err, errno, sqlstate = db:query(drop_table_sql)
29if not res then
30 ngx.say("drop table error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate)
31 return close_db(db)
32end
33local create_table_sql = "create table test(id int primary key auto_increment, ch varchar(100))"
34res, err, errno, sqlstate = db:query(create_table_sql)
35if not res then
36 ngx.say("create table error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate)
37 return close_db(db)
38end
39local insert_sql = "insert into test (ch) values('hello')"
40res, err, errno, sqlstate = db:query(insert_sql)
41if not res then
42 ngx.say("insert error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate)
43 return close_db(db)
44end
45res, err, errno, sqlstate = db:query(insert_sql)
46ngx.say("insert rows : ", res.affected_rows, " , id : ", res.insert_id, "")
47local update_sql = "update test set ch = 'hello2' where id =" .. res.insert_id
48res, err, errno, sqlstate = db:query(update_sql)
49if not res then
50 ngx.say("update error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate)
51 return close_db(db)
52end
53ngx.say("update rows : ", res.affected_rows, "")
54local select_sql = "select id, ch from test"
55res, err, errno, sqlstate = db:query(select_sql)
56if not res then
57 ngx.say("select error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate)
58 return close_db(db)
59end
60for i, row in ipairs(res) do
61 for name, value in pairs(row) do
62 ngx.say("select row ", i, " : ", name, " = ", value, "")
63 end
64end
65ngx.say("")
66local ch_param = ngx.req.get_uri_args()["ch"] or ''
67local query_sql = "select id, ch from test where ch = " .. ngx.quote_sql_str(ch_param)
68res, err, errno, sqlstate = db:query(query_sql)
69if not res then
70 ngx.say("select error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate)
71 return close_db(db)
72end
73for i, row in ipairs(res) do
74 for name, value in pairs(row) do
75 ngx.say("select row ", i, " : ", name, " = ", value, "")
76 end
77end
78--[[ 为了便于看到效果,我把删除这段代码先注释了
79local delete_sql = "delete from test"
80res, err, errno, sqlstate = db:query(delete_sql)
81if not res then
82 ngx.say("delete error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate)
83 return close_db(db)
84end
85ngx.say("delete rows : ", res.affected_rows, "")
86--]]
87close_db(db)
在nginx.conf中增加如下增配置并重载生效:
1server {
2listen 8081;
3location /lua_mysql {
4 default_type 'text/html';
5 lua_code_cache on;
6 content_by_lua_file /usr/local/openresty/nginx/lua/test_mysql.lua;
7 }
8}
通过URL访问后,可以发现数据库中已正常建表和插入了数据。
而且在web前台也会返回如下信息:
1insert rows : 1 , id : 2
2update rows : 1
3select row 1 : ch = hello
4select row 1 : id = 1
5select row 2 : ch = hello2
6select row 2 : id = 2
不过这里并未涉及到传参,如果需要传参,可以使用ngx.req.get_uri_args、ngx.req.get_post_args方法。方法的使用,可以参看:https://github.com/openresty/lua-nginx-module#nginx-api-for-lua ,注意,无论是get还是post,一定要使用set_unescape_uri、set_quote_sql_str 进行处理。避免非法参数传给数据库。而且现网实现API时,建议主要以select 读为主,而且尽量调用都在内部实现(能过访问控制,禁止外网访问)。
三、小结
Openresty本身是和tengine项目差不多同时代的产品,而且作者本人也是当年参与tengine项目的主要人员。Openresty目前在很多大厂都有应用,这自不必细说。关于更多功能,在《OpenResty最佳实践》一书和官方的readme文档都比较详尽。而且对于官方未实现的一些功能,也很方便的通过lua代码实现,lua代码也花了一点时间看了下,其操作起来并不难。具体应用还是看自身的应用场景,如果公司并没有API这类需求,也不需要实现很复杂的URL处理逻辑,nginx和tengine就已经OK了。
捐赠本站(Donate)
如您感觉文章有用,可扫码捐赠本站!(If the article useful, you can scan the QR code to donate))
- Author: shisekong
- Link: https://blog.361way.com/openresty-mysql/5689.html
- License: This work is under a 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议. Kindly fulfill the requirements of the aforementioned License when adapting or creating a derivative of this work.