Андрей Роенко, Яндекс
Андрей Роенко, Разработчик API Яндекс.Карт
const express = require('express');
const app = express();
app.get('/...', (req, res) => {
// ...
});
app.listen(port);
stuff_by_lua_file path/to/lua/file.lua;
stuff_by_lua_block {
-- lua code
}
stuff_by_lua '
-- lua code
';
init_by_lua
- проинициализировать, один раз при старте nginxset_by_lua
- поменять значение переменнойrewrite_by_lua
- изменить URLaccess_by_lua
- проверить права доступаcontent_by_lua
- сгенерировать тело ответаlog_by_lua
- залогироватьssl_certificate_by_lua
app.get('/01/', (req, res) => {
res.end('Hello from Node.js!\n');
});
$ curl node/01/
Hello from Node.js!
location /01/ {
content_by_lua_block {
ngx.say('Hello from Lua!')
}
}
$ curl nginx/01/
Hello from Lua!
$ ab -n 50000 -c 400 node/01/
Requests per second: 6954.47 [#/sec] (mean)
Time per request: 57.517 [ms] (mean)
$ ab -n 50000 -c 400 nginx/01/
Requests per second: 10117.13 [#/sec] (mean)
Time per request: 39.537 [ms] (mean)
cluster
на 4 воркераnginx-extras
, полностью из коробки)app.get('/02/:id', (req, res) => {
res.end(`${req.hostname} ${req.params.id}, ${req.query.filter}`);
});
$ curl node/02/42?filter=xxx
localhost, 42, xxx
location ~ ^/02/(?<id>[^/]*) {
content_by_lua_block {
ngx.say(ngx.var.http_host, ', ', ngx.var.id, ', ', ngx.var.arg_filter)
}
}
$ curl nginx/02/42?filter=xxx
localhost, 42, xxx
$ ab -n 50000 -c 400 node/02/
Requests per second: 6831.91 [#/sec] (mean)
Time per request: 58.549 [ms] (mean)
$ ab -n 50000 -c 400 nginx/02/
Requests per second: 9612.95 [#/sec] (mean)
Time per request: 41.611 [ms] (mean)
app.get('/03/', (req, res) => {
if (req.headers.magic === 'unicorn') {
res.sendFile(__dirname + '/welcome.html');
} else {
res.status(403);
res.end();
}
});
$ curl node/03/
403
$ curl node/03/ -H magic:unicorn
Welcome!
location /03/ {
access_by_lua_block {
if ngx.var.http_magic ~= 'unicorn' then
ngx.exit(ngx.HTTP_FORBIDDEN)
end
}
try_files /welcome.html =404;
}
$ curl nginx/03/
403
$ curl nginx/03/ -H magic:unicorn
Welcome!
$ ab -n 50000 -c 400 node/03/
Requests per second: 8124.70 [#/sec] (mean)
Time per request: 49.233 [ms] (mean)
$ ab -n 50000 -c 400 nginx/03/
Requests per second: 10164.75 [#/sec] (mean)
Time per request: 39.352 [ms] (mean)
$ ab -n 50000 -c 400 -H magic:unicorn node/03/
Requests per second: 3923.42 [#/sec] (mean)
Time per request: 101.952 [ms] (mean)
$ ab -n 50000 -c 400 -H magic:unicorn nginx/03/
Requests per second: 9957.32 [#/sec] (mean)
Time per request: 40.171 [ms] (mean)
const fsReadFile = util.promisify(fs.readFile);
app.get('/04/', wrap(async (req, res) => {
const content = await fsReadFile(__dirname + '/04.js', 'utf8');
res.end(`(function() { ${content} })()\n`);
}));
$ curl node/04/
(function() { console.log('hi'); })()
location /04/ {
content_by_lua_block {
local f = io.open('.../file', 'r')
local content = f:read('*all');
-- . . .
стоп
location /04/ {
content_by_lua_block {
local res = ngx.location.capture('/@04/')
ngx.say('(function() { ' .. res.body .. ' })()')
}
}
location /@04/ {
internal;
try_files /04.js = 404;
}
$ curl nginx/04/
(function() { console.log('hi'); })()
$ ab -n 50000 -c 400 node/04/
Requests per second: 5702.76 [#/sec] (mean)
Time per request: 70.142 [ms] (mean)
$ ab -n 50000 -c 400 nginx/04/
Requests per second: 9373.70 [#/sec] (mean)
Time per request: 42.673 [ms] (mean)
let hits = 0;
app.get('/05/', (req, res) => {
hits++;
res.end(`hits: ${hits}\n`);
});
$ curl node/05/
hits: 1
$ curl node/05/
hits: 2
lua_shared_dict hits 1m;
init_by_lua_block {
local hits = ngx.shared.hits
hits:set('value', 0)
}
location /05/ {
content_by_lua_block {
local hits = ngx.shared.hits
local value = hits:get('value') + 1
hits:set('value', value)
ngx.say('hits: ' .. tostring(value))
}
}
lua_shared_dict hits 1m;
location /05-ideomatic/ {
content_by_lua_block {
local hits = ngx.shared.hits
local value = hits:incr('value', 1)
ngx.say('hits: ' .. tostring(value))
}
}
$ curl nginx/05/
hits: 1
$ curl nginx/05/
hits: 2
$ ab -n 50000 -c 400 node/05/
Requests per second: 7186.32 [#/sec] (mean)
Time per request: 55.661 [ms] (mean)
$ ab -n 50000 -c 400 nginx/05/
Requests per second: 10128.66 [#/sec] (mean)
Time per request: 39.492 [ms] (mean)
lua_shared_dict referers 16m;
location /06/ {
access_by_lua_block {
local referers = ngx.shared.referers
local domain = (ngx.re.match(ngx.var.http_referer,
'([a-z]+://)?([^/]+)', 'i') or {})[2]
local hits = (referers:get(domain) or 0) + 1
referers:set(domain, hits, 30) -- 30 seconds
if hits > 30 then
ngx.say('Go away')
ngx.exit(ngx.HTTP_TOO_MANY_REQUESTS)
end
}
$ curl nginx/06/ -H referer:example.com
Welcome
...
$ curl nginx/06/ -H referer:example.com
Welcome
$ curl nginx/06/ -H referer:example.com
Go away
$ sleep 30
$ curl nginx/06/ -H referer:example.com
Welcome
$ ab -n 50000 -c 400 _h referer:example.com nginx/06/
Requests per second: 10300.81 [#/sec] (mean)
Time per request: 38.832 [ms] (mean)
Percentage of the requests served within a certain time (ms)
50% 38
66% 39
75% 40
80% 41
90% 42
95% 43
98% 44
99% 44
100% 1061 (longest request)
location /07/ {
content_by_lua_block {
ngx.sleep(math.random())
}
log_by_lua_block {
local statistics = ngx.shared.statistics
local time = ngx.var.request_time
statistics:incr('request_time-sum', time)
statistics:incr('request_time-total', 1)
}
}
location = /07-status/ {
content_by_lua_block {
local statistics = ngx.shared.statistics
local sum = statistics:get('request_time-sum')
local total = statistics:get('request_time-total')
ngx.say('average request time: ', sum / total, ' for ', total, ' requests')
}
}
$ curl localhost/07/
$ curl localhost/07/
$ curl localhost/07/
$ curl localhost/07/
$ curl localhost/07-status/
average request time: 0.74 for 4 requests
init_by_lua_block {
local function save ()
ngx.location.capture('/my-upstream/save', {
method = ngx.HTTP_POST,
body = concat_my_stuff()
})
end
ngx.timer.every(60, save)
}
ngx.log(ngx.NOTICE, ...)
<script src="//api-maps.yandex.ru/2.1.62/?lang=ru_RU"></script>
<script>
ymaps.ready().then(() => {
const map = new ymaps.Map(mapEl, { /* state */ }, { /* options */ });
// . . .
});
</script>
lua_package_path '/blah/blah/?.lua;;'
init_by_lua_block {
require('jsapi')
}
location ~ ^/2\.1(?<version>[-.]\w+) {
set_by_lua_block $real_version {
local jsapi = require('jsapi')
-- . . .
return jsapi.rewrite(ngx.var.version)
}
proxy_pass ...2.1$real_version;
}
location /isalive {
content_by_lua {
local jsapi = require('jsapi')
local responses = ngx.location.capture_multi(jsapi.versions_urls)
for i, reponse in ipairs(reponses) do
if response.status ~= ngx.HTTP_OK then
ngx.exit(reponse.status)
end
end
}
}
Both modules are not built by default, they should be either compiled from the sources or installed as a Linux package.
js_set
и js_content
Of course, it’s slower than LuaJIT, and it’s slower than JavaScript implementations like V8, SpiderMonkey, and JavaScriptCore which use JIT.
nginScript: A New and Powerful Way to Configure NGINX
Целый фреймворк вокруг Nginx Lua