ngx_lua 是由 chaoslawful 和 agentzh 开发的用于 web 开发的 nginx 扩展,
其主要特点在于利用了 nginx 的非阻塞 IO 模型以及 lua VM 的灵活性。
node.js 是 ry 主导开发的基于强大高效的 v8 引擎,
提供了事件模型和各种基础设施的 web 开发平台,
其主要特点是内部各种 IO 操作是非阻塞的,为高并发提供了很好的基础。
同时,因为基于 js 语法,和 web 前端融合较紧密,
可以提供前后一致的编程体验(当然,客户端还比较难达到服务器端的爽)。
在传统的 php 编程中,当需要查询数据库时,当前的
apache 进程(或线程)(或 php-fpm 之类)在数据结果返回前一直处于等待状态,
如果大量的请求都在做数据库查询操作,那么服务器就没法处理更多请求。
因为 apache 能开启的进程(或线程)数是有限的(受内存限制)。
<code>
<?php
// 连接数据库时,进程是在等待的
$c = mysql_connect('mysql_host', 'user', '****');
mysql_select_db('my_database');
$query = 'SELECT col1 FROM my_table LIMIT 10';
// 查询时,进程是在等待的
$result = mysql_query($query);
while ($line = mysql_fetch_array($result, MYSQL_ASSOC)) {
echo " * " . $line[0] . "\n";
}
mysql_free_result($result);
mysql_close($c);
?>
</code>
如上面的代码所示,在跟外部系统交互的时候,当前进程啥都没干,净等着了,
你还不能抱怨人家不干活,因为理由很充分:数据库忙着呢,我得等!
真的这样么?
我们看看 node.js 怎么做的。
<code>
// 仅仅给出部分代码,不能直接运行
// resp 是响应体对象
var c = new require('mysql').Client();
c.user = 'user';
c.password = '****';
// 开始连接数据库,同时注册了一个在连接成功后要执行的函数
// 注意,这时候 node.js 做了这件事情以后代码还在继续往下执行
// 细节参考 http://cnodejs.org/blog/?p=244
c.connect(function (err, results) {
if (err) {
resp.end("ERROR: " + err.message);
return;
}
// 这里发了一个数据库请求,并且注册了一个在请求完成后要
// 执行的函数,注册完毕后就跟刚才一样干其他事情去了
c.query(
'SELECT col1 FROM my_table LIMIT 10',
function (err, results, fields) {
if (err) {
resp.end("ERROR: " + err.message);
return;
}
for (var i = 0, iM = results.length; i < iM; ++i) {
resp.write(' * ' + results[i][0] + '\n');
}
resp.end();
c.end();
});
});
</code>
我们可以看到,node.js 对 IO 的做法是当要发生可能等待的事情时,
注册个函数在那里,然后继续做其他事情,当实际的数据到达或者事件完成时,
再调用之前注册的函数来处理。
整个 node.js 环境内漂浮着许多事件和函数,
在底层,有一些机制来保证这些事件的正确、准确触发。
在这种思路下,业务处理代码被切片,然后被注册到各种事件上面去,
这些由 node.js 统一管理,因此实现了非阻塞的处理。
对于 php 来说,一旦 apache 把控制权转交给 php 以后,
他们之间就很难转让控制权了,我们不可能把 php 代码切分到这样细致的地步,
因为即使切分了也没法注册给 apache ,而在 php 内部注册也是没有意义的:
一个 php 进程一般只处理一个客户端请求而已,在一个请求内添加事件的概念,
没法提升系统整体的并发能力。
node.js 还有一个很大的优势是 连接池。
在 php 里,连接可能仅仅是复用而已,连接池的意义应该不大。
而在 node.js 里,连接是可以在多个请求之间共享的,
只要 node.js 服务器不重启或关闭,那么这些连接便可以一直复用。
看到这里,你有没有激动或者兴奋呢?
是否要抛弃执着等待 IO 的 apache + php 这一对黄金搭档呢?
不过,我们稍等一下,如果为了非阻塞的特性,而要我人肉的拆分我的代码
成为很多函数(代码片段)然后注册到各种事件上去,会不会写起来很恶心?
或者,有没有从天上掉下来的什么东西,能够让我写像 php 那样从上到下的代码,
而又能够像 node.js 那样非阻塞呢?
ngx_lua 就是这样的东西。
那,ngx_lua 到底是神马东西呢?我们先看段代码。
<code>
upstream db {
drizzle_server mysql_host:3306 protocol=mysql
dbname=my_database user=user password=****;
}
http {
server {
location = /i-mysql {
internal;
drizzle_query $echo_request_body;
drizzle_pass db;
rds_json on;
}
location /test {
content_by_lua '
local yajl = require("yajl")
local sql = "SELECT col1 FROM my_table LIMIT 10"
local res = ngx.location.capture("/i-mysql",
{ method = ngx.HTTP_POST, body = sql })
if res.status ~= 200 then
ngx.say("error")
ngx.exit()
end
local result = yajl.to_value(res.body)
for i, v in ipairs(result) do
ngx.say(" * " .. v)
end
';
}
}
}
</code>
这是神马东西!!!!!!
好吧,其实这是一段 nginx 配置,
同时也是一段业务逻辑处理代码。
这里的写法跟 php 是类似的,从上到下,
但是呢,它对待 IO 的态度跟 node.js 是一样的,
当开始 IO 操作的时候,它会暂停当前代码的执行并发起数据请求,
当请求完成后,恢复之前暂停的代码,并把结果返回。
ngx_lua 的模型是一个 nginx 进程内可以同时处理不限数量(几乎)的请求,
他们按照上面描述的逻辑执行。
你可能会说,
那为什么要用 lua 这个没听说过的东西来搞,
而不用 php 或者 js 搞呢?
刚才说了,当发起请求的时候,需要暂停代码的执行,
暂时只有 lua 运行时支持这个特性(或者有其他什么主流语言支持,赶快说来听听)。
并且 lua 里面创建的进程(其实是协程)非常轻量,占用内存非常少,可以让服务器同时处理更多请求。
这篇文章是个开头,
后续会更多的介绍 ngx_lua 的周边和现状,
以及更多的使用示例。
* ngx_openresty http://agentzh.org/misc/nginx/ngx_openresty-0.8.54.0.tar.gz
* node.js https://github.com/ry/node
No comments:
Post a Comment