אנו מפרסמים שוב את תמליל דו"ח הכנס 2016, שהתקיים בסקולקובו ליד מוסקבה ב-7-8 בנובמבר בשנה שעברה. מסביר כיצד להרחיב את הפונקציונליות של NGINX עם OpenResty ו-Lua.
שלום לכולם, שמי ולדימיר פרוטסוב, אני עובד ב-Parallels. אני אספר לך קצת על עצמי. אני מבלה שלושה רבעים מחיי בכתיבת קוד. הפכתי למתכנת עד היסוד במובן המילולי: לפעמים אני רואה קוד בחלומות שלי. רבע מהחיים הוא פיתוח תעשייתי, כתיבת קוד שנכנס ישר לייצור. קוד שחלקכם משתמשים בו אבל לא מבינים אותו.
אז אתה מבין כמה זה היה גרוע. כשהייתי קצת צעיר, באתי וקיבלתי את מסדי הנתונים האלה של שני טרה-בייט. זה עומס גבוה לכולם כאן עכשיו. הלכתי לכנסים ושאלתי: "חבר'ה, תגידו לי, יש לכם ביג דאטה, הכל מגניב? כמה בסיסים יש לך שם? הם ענו לי: "יש לנו 100 גיגה-בייט!" אמרתי: "מגניב, 100 גיגה-בייט!" וחשבתי לעצמי איך לשמור בזהירות על פני הפוקר שלי. אתה חושב, כן, החבר'ה מגניבים, ואז אתה חוזר ומתעסק עם מסדי הנתונים הרב-טרה-בתים האלה. וזה להיות זוטר. אתה יכול לדמיין איזו מכה זו?
אני יודע יותר מ-20 שפות תכנות. זה משהו שהייתי צריך להבין כשעבדתי. הם נותנים לך קוד ב-Erlang, C, C++, Lua, Python, Ruby, משהו אחר, ואתה צריך לחתוך הכל. באופן כללי, הייתי חייב. לא ניתן היה לחשב את המספר המדויק, אבל אי שם בסביבות ה-20 המספר אבד.
מכיוון שכל הנוכחים יודעים מה זה Parallels ומה אנחנו עושים, אני לא אדבר על כמה אנחנו מגניבים ומה אנחנו עושים. אני רק אגיד לך שיש לנו 13 משרדים ברחבי העולם, יותר מ-300 עובדים, פיתוח במוסקבה, טאלין ומלטה. אם תרצו, תוכלו לקחת אותו ולעבור למלטה אם קר בחורף ואתם צריכים לחמם את הגב.
ספציפית, המחלקה שלנו כותבת בפייתון 2. אנחנו בעסק ואין לנו זמן ליישם טכנולוגיות אופנתיות, אז אנחנו סובלים. אנחנו משתמשים בג'נגו כי יש בו הכל, ולקחנו את מה שהיה מיותר וזרקנו. גם MySQL, Redis ו-NGINX. יש לנו גם הרבה דברים מגניבים אחרים. יש לנו MongoDB, יש לנו ארנבות שמתרוצצות, יש לנו הכל - אבל זה לא שלי, ואני לא עושה את זה.
OpenResty
סיפרתי על עצמי. בואו להבין על מה אני הולך לדבר היום:
- מהי OpenResty ועם מה אוכלים אותה?
- למה להמציא גלגל נוסף כשיש לנו Python, NodeJS, PHP, Go ועוד דברים מגניבים שכולם מרוצים מהם?
- ועוד כמה דוגמאות מהחיים. הייתי צריך לחתוך את הדוח הרבה כי זה לקח לי 3,5 שעות, אז יהיו כמה דוגמאות.
OpenResty הוא NGINX. בזכותו יש לנו שרת אינטרנט מלא שכתוב היטב ועובד במהירות. אני חושב שרובנו משתמשים ב-NGINX בייצור. כולכם יודעים שהוא מהיר ומגניב. הם יצרו בו קלט/פלט סינכרוני מגניב, אז אנחנו לא צריכים לסובב שום דבר, בדיוק כמו שהם עשו ב-Python. Gevent זה מגניב, מעולה, אבל אם תכתוב קוד C ומשהו ישתבש, אז עם Gevent תשתגע לאפות אותו. הייתה לי החוויה: לקח יומיים שלמים להבין מה השתבש שם. אם מישהו לא היה חופר כמה שבועות, מוצא את הבעיה, כותב באינטרנט וגוגל לא היה מוצא אותה, אז היינו משתגעים לגמרי.
ל-NGINX כבר בוצע אחסון במטמון ותוכן סטטי. אתה לא צריך לדאוג איך לעשות את זה באופן אנושי, כדי שלא תאט איפשהו, כדי שלא תאבד מתארים איפשהו. Nginx מאוד נוח לפריסה, אתה לא צריך לחשוב מה לקחת - WSGI, PHP-FPM, Gunicorn, Unicorn. Nginx הותקן, נתון למנהלים, הם יודעים איך לעבוד איתו. Nginx מעבד בקשות בצורה מובנית. אני אדבר על זה קצת מאוחר יותר. בקיצור, יש לו שלב שבו הוא פשוט קיבל את הבקשה, מתי הוא עיבד אותה ומתי הוא הגיש את התוכן למשתמש.
Nginx מגניב, אבל יש בעיה אחת: היא לא מספיק גמישה, אפילו עם כל הפיצ'רים המגניבים שהחבר'ה דחסו לתוך התצורה, למרות מה שאפשר להגדיר. הכוח הזה לא מספיק. זו הסיבה שהחבר'ה מטאובאו, לפני זמן רב, כך נראה לפני שמונה שנים, בנו את לואה לתוכו. מה זה נותן?
- גודל. זה קטן. LuaJIT נותן כ-100-200 קילובייט של תקורה של זיכרון ותקורה מינימלית של ביצועים.
- מהירות. מתורגמן LuaJIT קרוב ל-C במצבים רבים, במצבים מסוימים הוא מפסיד ל-Java, באחרים הוא עולה עליו. במשך זמן מה זה נחשב למצב האמנות, מהדר ה-JIT המגניב ביותר. עכשיו יש מגניבים יותר, אבל הם מאוד כבדים, למשל, אותו V8. חלק מתורגמני JS ו-Java HotSpot מהירים יותר בנקודות מסוימות, אך במקומות מסוימים הם עדיין מפסידים.
- קל ללמוד. אם יש לך, נגיד, בסיס קוד של Perl, ואתה לא בוקינג, לא תמצא מתכנתי Perl. בגלל שהם לא קיימים, כולם נלקחו משם, וללמד אותם זה ארוך וקשה. אם אתה רוצה מתכנתים למשהו אחר, אולי תצטרך גם להכשיר אותם מחדש או למצוא אותם. במקרה של לואה, הכל פשוט. כל זוטר יכול ללמוד לואה בשלושה ימים. לקח לי בערך שעתיים להבין את זה. שעתיים אחר כך כבר כתבתי קוד בהפקה. כעבור כשבוע הוא הלך ישר להפקה ועזב.
כתוצאה מכך, זה נראה כך:

יש כאן הרבה. OpenResty אספה חבורה של מודולים, גם luash וגם מנועיים. ויש לך הכל מוכן - פרוס ועובד.
דוגמאות
די למילים, בואו נעבור לקוד. הנה קצת שלום עולם:

מה יש שם? זהו מיקום של Engins. אנחנו לא דואגים, אנחנו לא כותבים ניתוב משלנו, אנחנו לא לוקחים איזה מוכן - כבר יש לנו את זה ב-NGINX, אנחנו חיים חיים טובים ועצלנים.
content_by_lua_block הוא בלוק שאומר שאנו מגישים תוכן באמצעות סקריפט Lua. אנחנו לוקחים את המשתנה Engines remote_addr ולהכניס אותו string.format. זה אותו דבר כמו sprintf, רק בלואה, רק נכון. ואנחנו נותנים את זה ללקוח.
כתוצאה מכך, זה ייראה כך:

אבל בואו נחזור לעולם האמיתי. אף אחד לא פורס את Hello World לייצור. האפליקציה שלנו בדרך כלל עוברת למסד הנתונים או למקום אחר ורוב הזמן מחכה לתגובה.

הוא רק יושב ומחכה. זה לא מאוד טוב. כשמגיעים 100.000 משתמשים, זה מאוד קשה לנו. אז בואו נשתמש באפליקציה פשוטה כדוגמה. נחפש תמונות, למשל, של חתולים. אבל לא רק נחפש, נרחיב את מילות המפתח, ואם המשתמש חיפש "חתלתולים", נמצא חתולים, חתולים פרוותיים וכן הלאה. ראשית, עלינו לקבל את נתוני הבקשה ב-backend. זה נראה כמו זה:

שתי שורות מאפשרות לך לקלוט פרמטרים של 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. אנחנו צריכים למשוך מפתחים חזיתיים. לפעמים אנחנו לא רוצים לעשות את זה בהתחלה. ומומחי SEO יגידו שאם אנחנו מחפשים תמונות, אז זה לא משנה להם. ואם ניתן להם קצת תוכן, הם יגידו שמנועי החיפוש שלנו לא מוסיפים שום דבר לאינדקס.
מה לעשות בנידון? כמובן, ניתן למשתמש HTML. יצירה ביד היא לא comme il faut, אז אנחנו רוצים להשתמש בתבניות. בשביל זה יש ספרייה lua-resty-template.

בטח ראית את שלוש האותיות המפחידות OPM. OpenResty מגיע עם מנהל חבילות משלה, שדרכו אתה יכול להתקין חבורה של מודולים שונים, בפרט, lua-resty-template. זהו מנוע תבניות פשוט, בדומה לתבניות ג'נגו. שם ניתן לכתוב קוד ולבצע החלפת משתנים.
כתוצאה מכך, הכל ייראה בערך כך:

לקחנו את הנתונים ורינדרנו את התבנית, שוב בשתי שורות. המשתמש מרוצה, הוא קיבל חתולים. מכיוון שהרחבנו את הבקשה, הוא קיבל גם חותם פרווה לגורים. אי אפשר לדעת, אולי הוא חיפש בדיוק את זה, אבל לא הצליח לנסח את בקשתו נכון.
הכל מגניב, אבל אנחנו בפיתוח ועדיין לא רוצים להראות את זה למשתמשים. בוא נעשה את ההרשאה. לשם כך, הבה נבחן כיצד NGINX מטפל בבקשה במונחי OpenResty:
- שלב ראשון - גישה, כשהמשתמש בדיוק הגיע, והסתכלנו עליו לפי כותרות, לפי כתובת IP ולפי נתונים אחרים. אנחנו יכולים לחתוך את זה מיד אם אנחנו לא אוהבים את זה. זה יכול לשמש להרשאה, או אם נקבל הרבה בקשות, נוכל לנתק אותן בקלות בשלב זה.
- לְשַׁכְתֵב. אנו משכתבים כמה נתוני בקשה.
- תוכן. אנו מספקים את התוכן למשתמש.
- מסנן כותרות. אנו מחליפים את כותרות התגובה. אם השתמשנו
proxy_pass, נוכל לשכתב כמה כותרות לפני שניתן את זה למשתמש. - מסנן גוף. אנחנו יכולים לשנות את הגוף.
- היכנס - רישום. אתה יכול לכתוב יומנים ב- elasticsearch ללא שכבה נוספת.
ההרשאה שלנו תיראה בערך כך:

נוסיף את זה לזה location, שתיארנו קודם, ושם שם את הקוד הבא:

אנחנו מסתכלים לראות אם יש לנו אסימון עוגיות. אם לא, אז אנחנו מבקשים אישור. משתמשים ערמומיים ויכולים לנחש שהם צריכים להגדיר אסימון קובץ Cookie. לכן, נשים את זה גם ברדיס:

הקוד לעבודה עם Redis הוא פשוט מאוד ואינו שונה משפות אחרות. יחד עם זאת, כל הקלט/פלט, פה ושם, לא חוסם. אם אתה כותב קוד סינכרוני, זה עובד באופן אסינכרוני. כמעט כמו לתת, אבל עשה זאת היטב.

בוא נעשה את ההרשאה בעצמה:

אנו אומרים שעלינו לקרוא את גוף הבקשה. אנו מקבלים ארגומנטים של POST ובודקים שהכניסה והסיסמה נכונות. אם הם שגויים, אנו קוראים לך אישור. ואם נכון, אז כתוב את האסימון ברדיס:

אל תשכחו להגדיר את העוגיה, גם זה נעשה בשתי שורות:

הדוגמה פשוטה וספקולטיבית. כמובן, לא נעשה שירות שמראה לאנשים חתולים. אבל מי מכיר אותנו. אז בואו נעבור על מה ניתן לעשות בייצור.
- אחורי מינימליסטי. לפעמים אנחנו צריכים להוציא רק מעט נתונים ל-backend: איפשהו אנחנו צריכים להכניס תאריך, איפשהו אנחנו צריכים להציג רשימה, לומר כמה משתמשים נמצאים באתר כעת, לצרף מונה או סטטיסטיקה. משהו כל כך קטן. כמה חתיכות מינימליות ניתן להכין בקלות רבה. זה יעשה את זה מהיר, קל ונהדר.
- עיבוד מקדים של נתונים. לפעמים אנחנו רוצים להטמיע פרסום בדף שלנו, ואנו מקבלים את הפרסום הזה באמצעות בקשות API. זה מאוד קל לעשות כאן. אנחנו לא מעמיסים את ה-backend שלנו, שכבר יושב ועובד קשה. אתה יכול לאסוף אותו ולאסוף אותו כאן. אנחנו יכולים לרקום איזשהו JS או להיפך, לנתק אותו ולעבד משהו מראש לפני שנותנים אותו למשתמש.
- חזית למיקרו-שירות. זה גם מקרה טוב מאוד, יישמתי אותו. לפני כן עבדתי בחברת טנצור העוסקת בדיווח אלקטרוני ומספקת דיווחים לכמחצית מהישויות המשפטיות בארץ. יצרנו שירות, הרבה דברים נעשו שם באותו מנגנון: ניתוב, הרשאה ועוד.
OpenResty יכול לשמש כדבק עבור המיקרו-שירותים שלך, ומספק גישה אחת לכל דבר וממשק יחיד. מכיוון שניתן לכתוב מיקרו-שירותים בצורה כזו שיש לך כאן Node.js, PHP כאן, Python כאן, משהו של Erlang כאן, אנחנו מבינים שאנחנו לא רוצים לשכתב את אותו הקוד בכל מקום. לכן, ניתן לחבר את OpenResty לחזית. - סטטיסטיקה וניתוח. בדרך כלל NGINX נמצא בכניסה, וכל הבקשות עוברות דרכה. זה במקום הזה זה מאוד נוח לאסוף. אתה יכול מיד לחשב משהו ולהעלות אותו לאנשהו, למשל, Elasticsearch, Logstash, או פשוט לכתוב אותו ליומן ואז לשלוח אותו לאנשהו.
- מערכות מרובות משתמשים. לדוגמא, משחקים מקוונים טובים מאוד להכנה. היום בקייפטאון, אלכסנדר גלדיש ידבר על איך ליצור אבטיפוס מהיר למשחק מרובה משתתפים באמצעות OpenResty.
- סינון בקשות (WAF). כיום זה אופנתי ליצור כל מיני חומות אש של יישומי אינטרנט; ישנם שירותים רבים המספקים אותם. באמצעות OpenResty, אתה יכול ליצור לעצמך חומת אש של אפליקציות אינטרנט שתסנן בפשטות ובקלות בקשות בהתאם לדרישות שלך. אם יש לכם Python, אז אתם מבינים ש-PHP בהחלט לא יוזרק לתוכם, אלא אם כן, כמובן, תוציאו אותו בכל מקום מהקונסולה. אתה יודע שיש לך MySQL ו-Python. כנראה שהם ינסו לעשות איזשהו מעבר ספריות ולהחדיר משהו למסד הנתונים. לכן, אתה יכול לסנן שאילתות מוזרות במהירות ובזול ממש בחזית.
- קהילה. מכיוון ש-OpenResty בנויה על NGINX, יש לה בונוס - זה קהילת NGINX. הוא גדול מאוד, וחלק הגון מהשאלות שיהיו לך בהתחלה כבר נפתרו על ידי קהילת NGINX.
מפתחי Lua. אתמול דיברתי עם החבר'ה שהגיעו ליום ההדרכה של HighLoad++ ושמעתי שרק Tarantool נכתב בלואה. זה לא נכון, הרבה דברים כתובים בלואה. דוגמאות: OpenResty, שרת Prosody XMPP, מנוע משחק Love2D, Lua שנכתב ב-Warcraft ובמקומות אחרים. יש הרבה מפתחי Lua, יש להם קהילה גדולה ומגיבה. כל השאלות שלי לואה נפתרו תוך כמה שעות. כשאתה כותב לרשימת התפוצה, ממש תוך כמה דקות יש כבר שלל תגובות, שמתארות מה ואיך, מה מה. זה מעולה. למרבה הצער, קהילה רוחנית חביבה כזו לא נמצאת בכל מקום.
יש GitHub עבור OpenResty, שבו אתה יכול לפתוח בעיה אם משהו נשבר. יש רשימת תפוצה ב-Google Groups, שבה אתה יכול לדון בנושאים כלליים, יש רשימת תפוצה בסינית - אי אפשר לדעת, אולי אתה לא דובר אנגלית, אבל אתה יודע סינית.
תוצאות של
- אני מקווה שהצלחתי להעביר ש-OpenResty היא מסגרת נוחה מאוד המותאמת לאינטרנט.
- יש לו מחסום כניסה נמוך, מכיוון שהקוד דומה למה שאנחנו כותבים בו, השפה די פשוטה ומינימליסטית.
- זה מספק קלט/פלט אסינכרוני ללא התקשרויות חוזרות, לא יהיו לנו אטריות כמו שלפעמים אנחנו יכולים לכתוב ב-NodeJS.
- יש לו פריסה קלה, מכיוון שאנחנו צריכים רק NGINX עם המודול הדרוש והקוד שלנו, והכל עובד מיד.
- קהילה גדולה ומגיבה.
לא סיפרתי בפירוט איך עושים ניתוב, התברר שזה סיפור ארוך מאוד.
תודה לך!

מקור: www.habr.com
