openresty: превращаем nginx в полноценный сервер приложений /...
TRANSCRIPT
Разработчик облачных сервисов в ||
Владимир Протасов
OpenRestyПревращаем NGINX в полноценный сервер приложений
2
О себе
• ¾ жизни пишу код• ¼ жизни – промышленная разработка• 2+ ТБ базы• 20+ языков
3
О компании
• 13 офисов по всему миру• 300+ сотрудников• Разработка в Москве, Таллине и на Мальте
4
На чем мы пишем?
• Python 2• Django• MySQL• Redis• NGINX
5
Коротко о главном
• Зачем изобретать велосипед?• Что это и с чем едят?
• Примеры из жизни
6
OpenResty
7
NGINX
• Полноценный веб-сервер• Скорость• Асинхронный I/O• Кеширование, статический контент, …• Удобство деплоя• Структурированная обработка запросов
8
OpenResty
+
9
Lua
• Размер• Скорость• Простота в освоении
10
OpenResty
11
Hello world
location /hello { content_by_lua_block { local hello = string.format('Hello, %s!', ngx.var.remote_addr) ngx.say(hello); }
}
12
Hello world
location /hello { content_by_lua_block { local hello = string.format('Hello, %s!', ngx.var.remote_addr) ngx.say(hello); }
}
13
Hello world
location /hello { content_by_lua_block { local hello = string.format('Hello, %s!', ngx.var.remote_addr) ngx.say(hello); }
}
14
Hello world
location /hello { content_by_lua_block { local hello = string.format('Hello, %s!', ngx.var.remote_addr) ngx.say(hello); }
}
15
Hello world
location /hello { content_by_lua_block { local hello = string.format('Hello, %s!', ngx.var.remote_addr) ngx.say(hello); }
}
16
Hello world
location /hello { content_by_lua_block { local hello = string.format('Hello, %s!', ngx.var.remote_addr) ngx.say(hello); }
}
17
Hello world
$ curl http://127.0.0.1/helloHello, 127.0.0.1!
18
Возвращаемся в реальный мир
19
Реальное приложение
• Поиск картинок• Расширенный список ключевых слов для поиска
20
Получаем данные
https://awesome.example.com/search?query=котята
location /search { content_by_lua_block { local args = ngx.req.get_uri_args() local search = args.search -- … }}
21
Расширение набора ключевых слов
• База данных• Слово -> набор схожих
котята -> котики, коты, пушистики
22
Расширение набора ключевых слов
local mysql = require "resty.mysql”
local db, err = mysql:new()local ok, err, errcode, sqlstate = db:connect{ path = "/path/to/mysql.sock", database = ”keywords",}
23
Расширение набора ключевых слов
local sql = string.format([[ SELECT kwd FROM keywords WHERE search = %s]], ngx.quote_sql_str(search))
local res, err, errcode, sqlstate = db:query(sql, 10)
-- res = [ { kwd = ‘котики’}, {kwd = ‘пушистики’} ]
24
Находим картинки по всем запросам
local reqs = {}
for , keyword in ipairs( keywords_list ) do table.insert (reqs, { '/fetch', { args = { search_q = keyword } } })end
resps = { ngx.location.capture_multi( reqs ) }
25
Находим картинки по всем запросам
location /fetch { internal; proxy_pass http://example.com/search?$request_args; -- кеширование и т.п.}
26
Выводим данные пользователю
local links = parse_responses(resps)
ngx.headers['Content-Type'] = 'application/json’ngx.say(cjson.encode(links))
27
Выводим данные пользователю
• Пользователь не хочет читать JSON• SEO-специалисты негодуют• Что делать?• Отдаем пользователю HTML
28
Выводим данные пользователю
$ opm install lua-resty-template
local links = parse_responses(resps)
local template = require "resty.template"template.render("view.html", { links = links })
29
Выводим данные пользователю
30
Фазы обработки запроса
• access• rewrite• content• headers filter• body filter• log
31
Авторизация
access_by_lua_block { -- …}
32
Авторизация
local ck = require "resty.cookie”local cookie, err = ck:new() local token = cookie:get('token')
if not token then return ngx.redirect("/auth") end
33
Авторизация
local redis = require "resty.redis”
local r = redis:new()local ok, err = r:connect("127.0.0.1", 6379)
local res, err = r:get(string.format('token:%s', token))if not res then return ngx.redirect("/auth")end
34
Авторизация
location /auth { content_by_lua_file "/path/to/auth.lua";}
35
Авторизация
ngx.req.read_body()local args, err = ngx.req.get_post_args()
if args.login ~= "admin" and args.password ~= "secret" then return ngx.redirect("/auth")end
36
Авторизация
local redis = require "resty.redis”
local r = redis:new()local ok, err = red:connect("127.0.0.1", 6379)
local token = "admintoken”red:set(string:format('token:%s', token), '1')
37
Авторизация
local ck = require "resty.cookie”
local cookie, err = ck:new()local token = cookie:set{ key='token', value=token,}
return ngx.redirect("/")
38
Что ещё можно сделать?
• Минималистичный бекенд• Препроцессинг данных• Фасад для микросервиса• Статистика и аналитика• Многопользовательские системы• Фильтрация запросов (WAF)
39
Сообщество
• NGINX-коммьюнити• Lua-разработчики
• Рассылка на китайском
• Github• Список рассылки (google groups)
40
Итого
• Удобный фреймворк, заточенный под веб• Низкий порог вхождения• Асинхронный I/O без коллбеков
• Большое отзывчивое сообщество• Легкий деплой
41
Спасибо за внимание