Մենք կրկին հրապարակում ենք կոնֆերանսի զեկույցի վերծանումը 2016, որը տեղի է ունեցել մոսկովյան Սկոլկովոյում 2017 թվականի նոյեմբերի 7-8-ին: պատմում է, թե ինչպես ընդլայնել NGINX-ի λειτουργությունները OpenResty-ի և Lua-ի միջոցով:
Բարև всем, իմ անունը Վլադիմիր Պրոտասով է, ես աշխատում եմ Parallels-ում: Թե՛ մի քիչ ինքս ինձ մասին: 三四 տարվա կյանքի մեծ մասը գրել եմ կոդ: Ես իսկապես պրոֆեսիոնալ ծրագրավորող եմ, և երբեմն կոդ եմ տեսնում քնի ընթացքում: Իմ կյանքի 25%-ը՝ արտադրական մշակման մեջ, չեմ գրում կոդ, որը գնում է արտադրանք: Տվյալ կոդը, որով որոշներն օգտագործում են, բայց չեն պատմում դրա մասին.
Որպեսզի դուք հասկանում եք, թե որքան վատ էր: Երբ ես փոքրիկ սկսնակ էի, ես եկա այստեղ, և ինձ տվեցին երկու տերաբայթանոց տվյալների բազաներ: Այսօր ամեն միկրոառաջարկել այստեղ խոշոր պրոբլեմներ են: Ես գնում էի կոնֆերանսներ, հարցնում էի. «Մի կատարեք, ասեք, դուք ունեք մեծ տվյալներ, ի՞նչպես եք»,- նրանք պատասխանում էին. «Մենք ունենք 100 գիգաբայթ»: Ես ասում էի. «Կայուն, 100 գիգաբայթ»: Իսկ ես էի մտածում, թե ինչպես մենակ պահել դեմքի նորմալ արտահայտությունը: Դուք կարծում եք, հա, տղաներ եզակի են, իսկ դուք վերադարձել եք այս տերաբայթանոց տվյալների բազաներով: Եվ դա՝ սկսնակ լինելով: Պատկերացնո՞ւմ եք, թե որքան հաճելի հարված է:
Ես գիտեմ 20-ից ավելի ծրագրավորման լեզուներ: Սա այն է, ինչի հետ պետք էր զբաղվել աշխատանքի ընթացում: Դուք ստանում եք կոդ Erlang, C, C++, Lua, Python, Ruby և այլ լեզուներով, ու պետք է որեւէ մեկի տակ աշխատեք: Ընդհանուր առմամբ, ստիպված եղա: Ցանկացած կոնկրետ թվից դժվար էր հաշվել, բայց մոտավորապես 20 լեզուների շուրջ կորել է:
Քանի որ բոլորը գիտեն, թե ինչ է Parallels-ը և ինչով ենք զբաղվում, չեմ ուզում խոսել մեր առավելություններին և արածին, այլ միայն պատմեմ, որ մենք միջազգային 13 գրասենյակ ունենք, ավելի քան 300 աշխատակից: Մշակումը Մոսկվայում, Թալլինում և Մալթայում է: Այլ բան, եթե ցանկանում եք, կարող եք տեղափոխվել Մալթա, եթե ձմռան ցուրտ է, և պետք է ձեր մեջն էլ տաքացնեք:
Մեր բաժինը աշխատում է Python 2-ով: Մենք բիզնեսով ենք զբաղված, ու մեզ հետ չէ նոր ֆեշն տարծելը, ուստի մենք տառապում ենք: Ունենք Django, որովհետև այնտեղ ամեն ինչ կա, իսկ ավելորդն ենք դուրս նետել: Ունենք նաև MySQL, Redis և NGINX: Անվել այլ շարքեր ենք գնել: Ունենք MongoDB, ունենք փ Rabbit մուլ էլ չենք, բայց սա չի իմ: Ես չգիտեմ:
OpenResty
Ես արդեն խոսեցի ինձ մասին: Հիմա եկեք հասկանալ, թե ինչ թեմաների մասին եմ այսօր խոսելու:
- Ինչ է OpenResty-ն և ինչի համար է պետք:
- Ինչու նորից ստեղծել ևս մեկ մեծագործություն, երբ ունենք Python, NodeJS, PHP, Go և այլ հիմեր, որոնցով բոլորը կբարի:
- Եվ մի քանի օրինակներ կյանքից: Ես ստիպված եղա շատ կրճատել զեկույցը, քանի որ այն 3,5 ժամ էր, ուստի օրինակները քիչ կլինեն:
OpenResty — սա NGINX է: Այն շնորհիվ ստեղծված լիարժեք վեբ սերվեր է, որը լավ է մշակված, արագ է աշխատում: Կարծում եմ՝ մեզանից շատերն օգտագործում են NGINX պրոդաքցիոնում: Դուք բոլորդ գիտեք, որ այն արագ և զարմանալի է: Այդտեղ ստեղծվել է հիանալի սարքային մուտք/արտք, այնպես որ մեզ չի հարկավոր ամաչել, ինչպես Python-ում gevent-ի հետ: Gevent-ը հիանալի է, առողջ, բայց եթե դուք գրեք C-ով կոդ, և այնտեղ ինչ-որ բան սխալ է գնում, դուք կգժվեք, երբ դա դեբաժիտեք: Ես ունեի փորձ, որի արդյունքում պահանջվեց երկու օր հասկանալու համար, որ ինչ-որ բան սխալ էր ամբողջովին: Եթե ոչ մի մարդ մինչ այդ մի քանի շաբաթ չհայտնաբերում էր խնդիրը, չէր գրել ինտերնետում, և Google-ն չէր գտնում դա, մենք ընդհանրապես կգժվեինք:
NGINX-ում արդեն կա քեշավորում և ստատիկ բովանդակություն: Դուք պետք չի հոգ տանել, թե ինչպես դա ուղիղ անել, որպեսզի ձեզ որևէ տեղ չստիպեն դանդաղել, որպեսզի ձեզ որևէ տեղ դեսկրիպտորներ չկորցնեն: Nginx-ը շատ հարմար է ինստալացնելու համար, ձեզ պետք չի մտածել, թե ինչ վերցնել՝ WSGI, PHP-FPM, Gunicorn, Unicorn: Nginx-ը տեղադրել եք, ադմիններին եք տվել, նրանք գիտեն, թե ինչպես աշխատել դրա հետ: Nginx-ը դասակարգված է հուշումներին: Ես դրա մասին կպատմեմ մի քիչ ավելի ուշ: Կարճաժամկետ NginX-ում կա փուլ, երբ այն միայն ընդունում է հարցում, երբ այն մշակել է և երբ ուզում է բովանդակություն վերադարձնել օգտվողին:
Nginx-ը հրաշալի է, բայց մեկ խնդիր կա՝ այն բավականին ճկուն չէ, նույնիսկ երբ բոլոր այլ հիանալի առանձնահատկությունները բոլորը տեղադրված են կարգավորումներում, այնուամենայնիվ, սահմանափակ է: Այս հզորությունը չի բավարարում: Հետևաբար, Taobao-ից երիտասարդները մեկ ժամանակ, կարծես, ութ տարի առաջ, Lua-ն ներառել են: Որն է դրա նպատակը:
- Չափսեր. Այն փոքրիկ է: LuaJIT-ը տալիս է մոտ 100-200 կիլոբայթ օվերհեդ հիշողության և նվազագույն օվերհեդ կատարողականության:
- Արագություն. LuaJIT-ը շատ իրավիճակներում մոտ է C-ին, մի քանի իրավիճակներում նա պարտվում է Java-ին, մի քանի՝ հաղթում նրան: Որոշ ժամանակ այն համարվել է state of art, լավագույն JIT-հավասարեցնող: Այժմ կան ավելի լավներ, բայց նրանք շատ ծանր են, օրինակ՝ V8-ը: Որոշ JS-ների հրաշալիքներ և Java-ական HotSpot որոշ կետերում ավելի արագ են, բայց որոշ տեղերում դեռ պարտվում են:
- Ուսումնասիրելու պարզություն. Եթե ձեզ մոտ, ասենք, ծրագրային բազա կա Perl-ի վրա, և դուք չեք Booking, դուք չեք գտնի Perl ծրագրավորողներ: Որովհետև նրանց չկա, նրանց բոլորը վերցրել են, իսկ սովորելը երկար և բարդ է: Եթե դուք ուզում եք այլ բաների համար ծրագրավորողներ, հնարավոր է, նրանց ևս վերապատրաստել կամ գտնել: Lua-ի դեպքում ամեն ինչ պարզ է: Lua-ն կսովորի ցանկացած նորեկ երեք օրվա ընթացքում: Ինձ մոտ պահանջվեց մոտ երկու ժամ հասկանալու համար: Երկու ժամ անց ես արդեն գրում էի կոդ պրոդաքցիոնում: Քանի որ մեկ շաբաթ հետո նա ուղղակի պրոդաքցիոն գնաց:
Վերջաբանում դա выглядит так:

Աստեղ շատ բան կա: OpenResty-ում հավաքվել է մի ամբողջ մոդուլների հավաքածու, մի քանիսի թե Lua-ի, թե հրամայակաների: Եվ لديك все готовое — задеплоил и работает.
Օրինակներ
Քիչ խոսակցություն, անցնենք կոդին: Ահա փոքրիկ Hello World:

Ինչ կա այստեղ? սա NGINX-ի location է: Մենք չենք անհանգստանում, ոչ մի.routing չենք գրում, ոչ էլ ինչ-որ պատրաստի ենք վերցնում՝ արդեն ունենք NGINX-ում, ապրում ենք լավ և հանգիստ.
content_by_lua_block – սա այն բլոկն է, որը նշում է, որ մենք մատուցում ենք բովանդակություն Lua-script օգտագործելով. Վերցնում ենք NGINX-ի փոփոխականը: remote_addr և դնում այն string.format. Սա նույնն է, ինչ և sprintf, միայն Lua-ի համար, միայն ճիշտ. Եվ մատուցում ենք հաճախորդին.
Արդյունքում, սա կտեսնվի այսպես:

Բայց վերադառնանք իրական աշխարհ: պրոդաքշնում ոչ ոք չի շահագործում Hello World-ը. Մեր դիմումը սովորաբար գնում է տվյալների բազա կամ այլ վայրեր և մեծ մասամբ ժամանակ սպասում է պատասխանի.

Սովորաբար նստած է ու սպասում: Սա շատ լավ չէ. Երբ գալիս են 100.000 օգտատերեր, մեզ շատ դժվար է. Այնպես որ, եկեք որպես օրինակ ստեղծենք պարզագույն դիմում: Որպես օրինակ, կփնտրենք նկարներ, ասենք, կատուներ. Аммо մենք պարզապես չենք փնտրի, կширայուցենք ключевые слова и если пользователь поискал «котята», мы ему найдём котиков, пушистиков и прочее. Для начала нам нужно получить данные запроса на бэкенде. Выглядит это так:

Երկու տողերս թույլ են տալիս ձեզ վերցնել GET-параметры, никаких сложностей. Дальше мы, допустим, из базы данных с табличкой по ключевому слову и расширению получаем обычным SQL-запросом эту информацию. Всё просто. Выглядит это так:

Միացնում ենք բիբլիոտեկը resty.mysql, որը մենք արդեն ունենք комплектում. Մեզ ոչինչ պետք չէ բեռնել, ամեն ինչը պատրաստ է. Նշում ենք, թե ինչպես միանալ, և կատարում SQL-з запрос:

Ուրեմն, այս փոքրիկ վախենալու է, բայց ամեն ինչ աշխատում է. Այստեղ 10-ը՝ դա սահմանափակումն է. Մենք 10 գրառումներ ենք վերցնում, մենք ծույլ ենք, չենք ուզում ավելին ցուցաբերել. SQL-ում սահմանափակման մասին մոռացել եմ:
Ապա մենք գտնում ենք նկարներ բոլոր հարցումների համար. Մենք հավաքում ենք հարցումների փունջ և լրացնում Lua-թերթիկը, որը կոչվում է reqs, և կատարում ենք ngx.location.capture_multi.

Ամբողջ այս հարցումները դուրս են գալիս параллель, և մեզ վերադարձնում են պատասխաններ. Երբ աշխատելու ժամանակը հավասար է ամենաերկար պատասխանող ժամանակին. Եթե մենք ունենք բոլոր հարցումները 50 միլիսեկունդում, և ուղարկել ենք հարյուր հարցումներ, ապա պատասխանն ուրեմն կգա 50 միլիսեկունդում.
Քանի որ մենք ծույլ ենք և չենք ուզում գրել HTTP մշակումը և կշռումը, մենք ստիպում ենք NGINX-ին անել ամեն ինչ մեզ համար: Ինչպես տեսաք, այնտեղ հարցում էր на url/fetch, ահա նա:

Մենք կատարում ենք պարզ запрос, նշում ենք, թե որտեղ պետք է կշռել, ինչպես դա անել, և մենք ամեն ինչ աշխատում է. proxy_passԲայց դա բավական չէ, մենք դեռ պետք է մատուցենք տվյալները օգտագործողին. Ամենացածր գաղափարը՝ դա ամեն ինչ սերիալացնել JSON-ում, հեշտ, երկու տողում. Մատուցում ենք Content-Type, մատուցում ենք JSON.
Բայց դա բավարար չէ, մենք նույնպես պետք է տվյալները փոխանցել օգտվողին: Ուղղակի գաղափարը՝ սրանք բոլորովին սերիալացնել JSON-ով, հեշտ, երկու տողով: Փոխանցում ենք Content-Type-ը, փոխանցում ենք JSON-ը:
Բայց կա մի դժվարություն. օգտատերը չի ցանկանում կարդալ JSON: Պետք է գրավել ֆրոնտենդերներին: Иногда нам не хочется этого поначалу делать. Да и сеошники скажут, что если мы картинки ищем, то им без разницы. А если мы им какой-то контент выдаём, то они скажут, что у нас поисковики ничего не индексируют.
Ինչ արեցինք այս մասին? Իհարկե, մենք կփոխանցենք HTML օգտվողին: Հանդիպում ենք ձեռքով՝ դա չի ընդունվում, հետևաբար ցանկանում ենք օգտագործել նմուշներ: Դրա համար կա գրադարանը lua-resty-template.

Դուք, հավանաբար, տեսել եք երեք սարսափելի տառերը OPM: OpenResty-ն գալիս է իր փաթեթային մենեջերով, որի միջոցով կարելի է տեղադրելու շատ այլ մոդուլներ, մասնավորապես, lua-resty-template. Դա պարզ նմուշների շարժիչ է, մոտավոր Django-ն մոդելներին: Կարող եք գրել կոդ և արել փոփոխականների պարունակություն:
Վերջում ամեն ինչ նմանաբար կլինի պատկերի նման.

Մենք վերցրել ենք տվյալները և կրկին երկու տողում ներկայացրել ենք նմուշը: Օգտագործողը երջանիկ է, ստացել է կատուներ: Որպեսզի փորձը բարդացրել ենք, նա կատնախորշ է ստացել նաև ծովային կատու: Ինչ-որ բան, եթե նա հենց դա էր փնտրում, բայց չէր կարողանում ճիշտ ձևակերպել հարցումը:
Դա ամեն ինչ է, բայց մենք դեռ զարգանում ենք և չենք ցանկանում դեռ ցուցադրել օգտվողներին: Եկեք տեղի ունենանք իրավացիություն. Դա անել համար, եկեք նայենք, թե ինչպես NGINX-ը անհրաժեշտ պահանջն է OpenResty-ի տեսանկյունից:
- Առաջին փուլը access, երբ օգտվողը միայն եկել է, և մենք նրան դիտում ենք վերնագրերով, IP հասցեով, այլ տվյալներով: Կարող ենք անմիջապես նրան ձգել, եթե մեզ չի դուր գալիս: Սա կարող է օգտագործվել որպես իրավացիություն, կամ, եթե մեզ շատ պահանջներ են գալիս, մենք կարող ենք հեշտությամբ կտրել դրանց այս փուլում:
- rewrite. Փոխարինում ենք որոշ տվյալներ պահանջի:
- content. Հանձնենք բովանդակություն օգտվողին:
- headers filter. Փոխարինում ենք պատասխանների վերնագրերը: Եթե մենք օգտագործել ենք
proxy_pass, կարող ենք փոխարինել որոշ վերնագրեր, նախքան տալ օգտվողին: - body filter. Մ можем подменить тело.
- log — արձանագրում: Կարող եք գրել արձանագրություններ elasticsearch-ի մեջ առանց լրացուցիչ շերտի:
Մեր իրավացիությունը կտեսնվի գրեթե כך:

Մենք դա ավելացնենք ինքը. location, որը մենք ներկայացրել ենք առաջ, և դրան կտեղադրենք այսպիսի կոդ.

Մենք նայում ենք, թե արդյոք մենք ունենք cookie token: Եթե ոչ, ապա ուղարկենք իրավացիության: Օգտագործողները խելացի են և կարող են կռահել, որ պետք է դնել cookie token: Հետևաբար, մենք ևս դրան դրել ենք Redis-ի մեջ:

Redis-ի հետ աշխատելու կոդը շատ պարզ է և ոչինչ չի տարբերվում այլ լեզուներից: Ընդ որում, ամբողջ մուտք/հրապարակում այնտեղ էլ, այստեղ էլ, դա չի արգելափակող: Եթե գրում եք համաժամանակյա կոդ, ապա աշխատում է ասինխրոն: Լար է նման gevent-ի, միայն արված է լավ:

Եկեք իրականացնենք ինքնարժեքը:

Խոսենք, որ մեզ անհրաժեշտ է կարդալ պահանջի մարմինը: Получаем POST-аргументы, проверяем, что логин и пароль правильные. Если неправильные, то кидаем на авторизацию. А если правильные, то записываем token в Redis:

Не մոռանանք դնել cookie-ները, դա էլ արվում է երկու տողում:

Այս պարզ, տեսական օրինակը: Մենք, իհարկե, չենք պատրաստվում անել ծառայություն, որը ցույց է տալիս մարդկանց կատուներ: Չնայած արդյո՞ք մենք գիտենք մեզ: Ուրեմն եկեք անցնենք այն տրամադրություններին, որոնք կարող ենք իրականացնել արտադրությունում:
- Մինիմալիստական բեքենդ. Иногда нам требуется в бэкенд выдать совсем чуть-чуть данных: где-то нужно дату подставить, где-то какой-то список вывести, сказать, сколько сейчас пользователей на сайте, прикрутить счётчик или статистику. Что-то такое небольшое. Минимальные какие-то кусочки можно очень легко сделать. На этом получится быстро, легко и здорово.
- Տվյալների նախշում. Иногда нам хочется встроить в нашу страничку рекламу, причём эту рекламу мы берём API-запросами. Такое очень легко сделать именно здесь. Мы не загружаем наш бэкенд, который и так сидит тяжело работает. Можно взять и собрать здесь. Мы можем слепить какие-то JS или, наоборот, разлепить, что-то препроцессить прежде, чем отдать пользователю.
- Միկրոսերվիսների ֆասադ. Это тоже очень хороший кейс, я его реализовывал. До этого я работал в компании Tenzor, которая занимается электронной отчётностью, обеспечивает отчётность примерно половины юрлиц в стране. Мы сделали сервис, там при помощи этого же механизма сделаны многие вещи: маршрутизация, авторизация и другое.
OpenResty можно использовать как клей для ваших микросервисов, который обеспечит единый доступ ко всему и единый интерфейс. Поскольку микросервисы могут быть написаны так, что вот здесь у вас Node.js, здесь у вас PHP, здесь Python, здесь стоит какая-то штука на Erlang, мы понимаем, что не хотим один и тот же код везде переписывать. Поэтому OpenResty можно воткнуть на фронт. - Ինտերեկտիվությունը և անալիտիկա. Обычно NGINX стоит на входе, и все запросы идут через него. Именно в этом месте очень удобно собрать. Можно что-то сразу посчитать и куда-нибудь закинуть, например, тот же Elasticsearch, Logstash или просто записать в лог и потом куда-нибудь отправить.
- Բազմաձայն համակարգեր. Например, онлайн-игры тоже очень хорошо делать. Сегодня в Кейптауне Александр Гладыш будет рассказывать, как быстро прототипировать многопользовательскую игру при помощи OpenResty.
- Հարցերի ֆիլտրացում (WAF). Այժմ նորաձևություն է դարձել տարբեր վեբ հավելվածների նախաքաղաքային firewall-ներ ստեղծելը, ուստի կան շատ ծառայություններ, որոնք դրանք առաջարկում են։ OpenResty-ի միջոցով կարող եք ստեղծել վեբ հավելվածի firewall, որը հեշտությամբ և արագ կհամապատասխանի ձեր պահանջներին։ Եթե դուք Python ունեք, ապա գիտեք, որ PHP-ն ձեզ հաստատ չի ձեր Ինջեկտի, եթե, իհարկե, կոնսոլից ոչ մի տեղ այն չսկսեք։ Դուք գիտեք, որ ունեք MySQL և Python։ Զարմանալի չէ, որ այստեղ կարող են փորձել directory traversal անել և ինչ-որ բան ներարկել բազայում։ Ուստի հնարավոր է արագ և մատչելի կերպով ֆիլտրացնել վարկաբեկված հարցումները անմիջապես առաջնային ցանկացած հետազոտությամբ։
- Ուղղադարձը։ Որպեսզի OpenResty-ն հիմնված է NGINX-ի վրա, ուստի ունի բնույթ՝ դա NGINX համայնք. Այն շատ մեծ է, և NGINX համայնքում ձեր բախած հարցերի մի մասը արդեն լուծված է։
Lua-ընկերներ. Մեկ օր ես շփվում էի այն տղաների հետ, ովքեր եկել էին HighLoad++ ուսումնական օրվա և լսել էին, որ Lua-ն գրում է միայն Tarantool։ Սա ճիշտ չէ, Lua-ին շատ բաներ են գրել։ Օրինակներ՝ OpenResty, XMPP-սերվեր Prosody, Love2D խաղային շարժիչ, Lua-ն սցենարվում է Warcraft-ում և այլուր։ Lua-ընկերների թիվը շատ մեծ է, նրանց միջև մեծ և արձագանքման սոցիալ միջավայր կա։ Մյուս կողմից, բոլորը իմ Lua հարցերը լուծվում էին մի քանի ժամվա ընթացքում։ Երբ գրում եք էլեկտրոնային դիմումի ցուցակում, գրեթե մի քանի րոպեի ընթացքում արդեն մի շարք պատասխաններ են, ասում են, թե ինչպես և ինչ։ Սա շատ լավ է։ Դե, ցավոք, ոչ ամենուր է, որ այդքան բարեհոգի հոգատար ընկերություններ կան։
OpenResty-ի վերաբերյալ կա GitHub, այնտեղ կարող եք բաներ կազմել, եթե ինչ-որ բան կոտրվեց։ Գոյություն ունի էլեկտրոնային սխեմայի ցուցակ Google Groups-ում, որտեղ կարող եք քննարկել ընդհանուր հարցեր, կա նաև չիներեն ցուցակ՝ չէինք կարծում, որ կարող եք չինական լեզվով, եթե անգլերեն չգիտեք կամ չեք կառավարվում։
Եզրակացություններ
- Հույս ունեմ, կարողացել եմ ցույց տալ, որ OpenResty-ն շատ հարմար ֆրեյմ워크 է, որը տրամադրելի է վեբի համար։
- Այն ունի ցածր մտածողության շեմ, քանի որ կոդը նման է այն, ինչ մենք գրում ենք, լեզվաընկալունակ է և հավասար երկրների բանալու բովանդակություն ունի։
- Այն տրամադրում է ասինխրոն I/O առանց callback-ների, մենք չենք ունենալու նոդերի պես, ինչը երբեմն կարող ենք գրել NodeJS-ում։
- Այն դյուրին տեղադրում ունի, քանի որ մեզ ընդամենը պետք է NGINX-ն անհրաժեշտ մոդուլով և մեր կոդն, և ամեն ինչ անմիջապես աշխատում է։
- Մեծ ու արձագանքող համայնք։
Ես մանրամասն չեմ պատմել, թե ինչպես է կատարվում ուղղորդումը, այնտեղ շատ երկար պատմություն էր ստացվում։
Շնորհակալություն ուշադրություն դնելու համար!

Ընտանիք: habr.com
