Վեբ հավելվածներն այժմ օգտագործվում են ամենուր, և տրանսպորտային բոլոր արձանագրությունների մեջ HTTP-ն զբաղեցնում է առյուծի բաժինը։ Վեբ հավելվածների մշակման նրբությունները ուսումնասիրելիս մարդկանց մեծամասնությունը շատ քիչ ուշադրություն է դարձնում օպերացիոն համակարգին, որտեղ իրականում աշխատում են այս հավելվածները: Զարգացման (Dev) և գործառնությունների (Ops) տարանջատումը միայն վատթարացրեց իրավիճակը: Սակայն DevOps-ի մշակույթի աճի հետ մեկտեղ ծրագրավորողները դառնում են պատասխանատու իրենց հավելվածները ամպի մեջ գործարկելու համար, ուստի նրանց համար շատ օգտակար է լավ ծանոթանալ օպերացիոն համակարգի հետին պլանին: Սա հատկապես օգտակար է, եթե դուք փորձում եք համակարգ տեղադրել հազարավոր կամ տասնյակ հազարավոր միաժամանակյա միացումների համար:
Վեբ ծառայությունների սահմանափակումները շատ նման են այլ հավելվածների սահմանափակումներին: Անկախ նրանից, թե դա բեռի հավասարակշռողներ են, թե տվյալների բազայի սերվերներ, այս բոլոր հավելվածներն ունեն նմանատիպ խնդիրներ բարձր արդյունավետության միջավայրում: Հասկանալով այս հիմնարար սահմանափակումները և ընդհանուր առմամբ դրանք հաղթահարելը կօգնի ձեզ գնահատել ձեր վեբ հավելվածների կատարումն ու մասշտաբայնությունը:
Ես գրում եմ հոդվածների այս շարքը՝ պատասխանելով երիտասարդ ծրագրավորողների հարցերին, ովքեր ցանկանում են դառնալ լավ տեղեկացված համակարգերի ճարտարապետներ: Անհնար է հստակ հասկանալ Linux հավելվածների օպտիմիզացման տեխնիկան՝ առանց սուզվելու այն հիմունքների մեջ, թե ինչպես են դրանք աշխատում օպերացիոն համակարգի մակարդակում: Չնայած կան բազմաթիվ տեսակի հավելվածներ, այս շարքում ես ցանկանում եմ ուսումնասիրել վեբ վրա հիմնված հավելվածները, այլ ոչ թե աշխատասեղանի ծրագրերը, ինչպիսիք են բրաուզերը կամ տեքստային խմբագրիչը: Այս նյութը նախատեսված է մշակողների և ճարտարապետների համար, ովքեր ցանկանում են հասկանալ, թե ինչպես են աշխատում Linux կամ Unix ծրագրերը և ինչպես դրանք կառուցվածքավորել բարձր արդյունավետության համար:
Linux-ն է սերվերի սենյակ օպերացիոն համակարգ, և ամենից հաճախ ձեր հավելվածներն աշխատում են այս ՕՀ-ով: Չնայած ես ասում եմ «Linux», բայց շատ ժամանակ կարելի է հանգիստ ենթադրել, որ ես նկատի ունեմ բոլոր Unix-ի նման օպերացիոն համակարգերը ընդհանրապես: Այնուամենայնիվ, ես չեմ փորձարկել ուղեկցող կոդը այլ համակարգերում: Այսպիսով, եթե դուք հետաքրքրված եք FreeBSD-ով կամ OpenBSD-ով, ձեր արդյունքները կարող են տարբեր լինել: Երբ ես փորձում եմ Linux-ին հատուկ ինչ-որ բան, ես դա նշում եմ:
Թեև դուք կարող եք օգտագործել այս գիտելիքները զրոյից հավելված ստեղծելու համար, և այն կատարյալ օպտիմիզացված կլինի, ավելի լավ է դա չանել: Եթե դուք գրում եք նոր վեբ սերվեր C կամ C++ լեզվով ձեր կազմակերպության բիզնես հավելվածի համար, դա կարող է լինել ձեր աշխատանքի վերջին օրը: Այնուամենայնիվ, այս հավելվածների կառուցվածքի իմացությունը կօգնի գոյություն ունեցող ծրագրերի ընտրությանը: Դուք կկարողանաք համեմատել գործընթացի վրա հիմնված համակարգերը թելի վրա հիմնված, ինչպես նաև իրադարձությունների վրա հիմնված համակարգերի հետ: Դուք կհասկանաք և կգնահատեք, թե ինչու է Nginx-ն ավելի լավ, քան Apache httpd-ը, ինչու Tornado-ի վրա հիմնված Python հավելվածը կարող է ավելի շատ օգտվողների սպասարկել՝ համեմատած Django-ի վրա հիմնված Python հավելվածի հետ:
ZeroHTTPd: Ուսուցման գործիք
ZeroHTTPd վեբ սերվեր է, որը ես զրոյից գրել եմ C-ում՝ որպես ուսուցման գործիք: Այն չունի արտաքին կախվածություն, ներառյալ Redis-ի հասանելիությունը: Մենք վարում ենք մեր սեփական Redis ընթացակարգերը: Լրացուցիչ մանրամասների համար տե՛ս ստորև:
Թեև մենք կարող էինք երկար քննարկել տեսությունը, չկա ավելի լավ բան, քան կոդ գրելը, այն գործարկելը և սերվերի բոլոր ճարտարապետությունները միմյանց հետ համեմատելը: Սա ամենաակնհայտ մեթոդն է։ Հետևաբար, մենք կգրենք պարզ ZeroHTTPd վեբ սերվեր՝ օգտագործելով յուրաքանչյուր մոդել՝ գործընթացի վրա հիմնված, շղթայի վրա հիմնված և իրադարձությունների վրա հիմնված: Եկեք ստուգենք այս սերվերներից յուրաքանչյուրը և տեսնենք, թե ինչպես են նրանք կատարում միմյանց համեմատ: ZeroHTTPd-ն իրականացվում է մեկ C ֆայլում: Իրադարձությունների վրա հիմնված սերվերը ներառում է ուտաշ, հեշ աղյուսակի հիանալի իրականացում, որը գալիս է մեկ վերնագրի ֆայլում: Մնացած դեպքերում կախվածություններ չկան, որպեսզի չբարդացնեն նախագիծը։
Կոդում կան բազմաթիվ մեկնաբանություններ, որոնք կօգնեն ձեզ հասկանալ: Լինելով պարզ վեբ սերվեր մի քանի տող կոդի մեջ՝ ZeroHTTPd-ը նաև նվազագույն շրջանակ է վեբ զարգացման համար: Այն ունի սահմանափակ ֆունկցիոնալություն, բայց կարող է սպասարկել ստատիկ ֆայլեր և շատ պարզ «դինամիկ» էջեր։ Պետք է ասեմ, որ ZeroHTTPd-ը լավ է սովորելու, թե ինչպես ստեղծել բարձր արդյունավետությամբ Linux հավելվածներ: Մեծ հաշվով, վեբ ծառայությունների մեծ մասը սպասում է հարցումներին, ստուգում դրանք և մշակում դրանք: Սա հենց այն է, ինչ կանի ZeroHTTPd-ը: Սա սովորելու գործիք է, ոչ թե արտադրություն: Այն հիանալի չէ սխալների հետ վարվելու հարցում և դժվար թե պարծենա անվտանգության լավագույն փորձից (այո, ես օգտագործել եմ strcpy) կամ C լեզվի խելացի հնարքները։Բայց հուսով եմ, որ այն լավ կկատարի իր գործը։
ZeroHTTPd գլխավոր էջ: Այն կարող է արտադրել տարբեր տեսակի ֆայլեր, ներառյալ պատկերները
Հյուրերի գրքի հայտ
Ժամանակակից վեբ հավելվածները սովորաբար չեն սահմանափակվում միայն ստատիկ ֆայլերով: Նրանք ունեն բարդ փոխազդեցություններ տարբեր տվյալների բազաների, քեշերի և այլնի հետ: Այսպիսով, մենք կստեղծենք պարզ վեբ հավելված, որը կոչվում է «Հյուրերի գիրք», որտեղ այցելուները գրառումներ են թողնում իրենց անունների տակ: Հյուրերի գրքերի խանութների մուտքերը թողել են ավելի վաղ: Էջի ներքևում կա նաև այցելուների հաշվիչ:
Վեբ հավելված «Հյուրերի գիրք» ZeroHTTPd
Այցելուների հաշվիչը և հյուրերի գրքի գրառումները պահվում են Redis-ում: Redis-ի հետ հաղորդակցվելու համար իրականացվում են սեփական ընթացակարգեր, որոնք կախված չեն արտաքին գրադարանից: Ես homebrew-ի կոդերի տարածման մեծ երկրպագու չեմ, երբ կան հանրությանը հասանելի և լավ փորձարկված լուծումներ: Սակայն ZeroHTTPd-ի նպատակն է ուսումնասիրել Linux-ի աշխատանքը և մուտքը արտաքին ծառայություններ, մինչդեռ HTTP հարցումների սպասարկումը լուրջ ազդեցություն ունի կատարողականի վրա: Մենք պետք է լիովին վերահսկենք Redis-ի հետ հաղորդակցությունը մեր յուրաքանչյուր սերվերի ճարտարապետության մեջ: Որոշ ճարտարապետություններում մենք օգտագործում ենք արգելափակող զանգեր, մյուսներում՝ իրադարձությունների վրա հիմնված ընթացակարգեր: Արտաքին Redis հաճախորդի գրադարանի օգտագործումը չի ապահովի այս վերահսկողությունը: Բացի այդ, մեր փոքրիկ Redis հաճախորդը կատարում է միայն մի քանի գործառույթ (բանալին ստանալը, կարգավորումը և ավելացումը; զանգվածի ստացում և ավելացում): Բացի այդ, Redis արձանագրությունը չափազանց էլեգանտ է և պարզ: Դուք նույնիսկ կարիք չունեք այն հատուկ ուսուցանելու: Հենց այն փաստը, որ արձանագրությունն ամբողջ աշխատանքը կատարում է շուրջ հարյուր տող կոդով, ցույց է տալիս, թե որքան լավ է այն մտածված:
Հետևյալ նկարը ցույց է տալիս, թե ինչ է անում հավելվածը, երբ հաճախորդը (բրաուզերը) պահանջում է /guestbookURL.
Ինչպես է աշխատում հյուրերի գրքի հավելվածը
Երբ հյուրերի գրքի էջը պետք է թողարկվի, կա մեկ զանգ դեպի ֆայլային համակարգ՝ կաղապարը հիշողության մեջ կարդալու համար և երեք ցանցային զանգ դեպի Redis: Կաղապարի ֆայլը պարունակում է էջի HTML բովանդակության մեծ մասը վերևի սքրինշոթում: Կան նաև հատուկ տեղապահեր բովանդակության դինամիկ մասի համար՝ գրառումներ և այցելուների հաշվիչ։ Մենք դրանք ստանում ենք Redis-ից, տեղադրում ենք էջ և հաճախորդին տրամադրում լիարժեք ձևավորված բովանդակություն։ Երրորդ զանգը դեպի Redis կարելի է խուսափել, քանի որ Redis-ը վերադարձնում է նոր բանալի արժեքը, երբ ավելացվում է: Այնուամենայնիվ, մեր սերվերի համար, որն ունի ասինխրոն իրադարձությունների վրա հիմնված ճարտարապետություն, շատ ցանցային զանգեր լավ թեստ են ուսումնական նպատակների համար: Այսպիսով, մենք մերժում ենք այցելուների թվի Redis վերադարձի արժեքը և հարցում ենք անում առանձին զանգով:
Սերվերի ճարտարապետություն ZeroHTTPd
Մենք կառուցում ենք ZeroHTTPd-ի յոթ տարբերակ՝ նույն ֆունկցիոնալությամբ, բայց տարբեր ճարտարապետություններով.
Կրկնվող
Fork սերվեր (մեկ երեխայի գործընթաց յուրաքանչյուր հարցում)
Pre-fork սերվեր (գործընթացների նախնական պատառաքաղ)
Սերվեր կատարման թելերով (մեկ թեմա յուրաքանչյուր հարցում)
Սերվեր նախնական թելի ստեղծմամբ
Ճարտարապետության վրա հիմնված poll()
Ճարտարապետության վրա հիմնված epoll
Մենք չափում ենք յուրաքանչյուր ճարտարապետության կատարումը՝ սերվերը բեռնելով HTTP հարցումներով: Բայց բարձր զուգահեռ ճարտարապետությունները համեմատելիս հարցումների թիվը մեծանում է: Մենք երեք անգամ փորձարկում ենք և հաշվարկում միջինը:
Փորձարկման մեթոդաբանություն
ZeroHTTPd բեռնվածության փորձարկման կարգավորում
Կարևոր է, որ թեստերն իրականացնելիս բոլոր բաղադրիչները չաշխատեն նույն մեքենայի վրա: Այս դեպքում ՕՀ-ն կրում է լրացուցիչ ժամանակացույց, քանի որ բաղադրիչները մրցում են CPU-ի համար: Ընտրված սերվերի ճարտարապետություններից յուրաքանչյուրի օպերացիոն համակարգի վերադիր չափումը այս վարժության ամենակարևոր նպատակներից է: Ավելի շատ փոփոխականներ ավելացնելը կվնասի գործընթացին: Հետևաբար, վերը նշված նկարի կարգավորումը լավագույնս աշխատում է:
Ի՞նչ է անում այս սերվերներից յուրաքանչյուրը:
load.unixism.net. Այստեղ մենք վազում ենք ab, Apache Benchmark կոմունալ. Այն առաջացնում է բեռ, որն անհրաժեշտ է մեր սերվերի ճարտարապետությունը փորձարկելու համար:
nginx.unixism.net. Երբեմն մենք ցանկանում ենք գործարկել սերվերային ծրագրի մեկից ավելի օրինակ: Դա անելու համար Nginx սերվերը համապատասխան կարգավորումներով աշխատում է որպես բեռի հավասարակշռող, որը գալիս է ab մեր սերվերի գործընթացներին:
zerohttpd.unixism.net. Այստեղ մենք գործարկում ենք մեր սերվերի ծրագրերը յոթ տարբեր ճարտարապետության վրա՝ մեկ առ մեկ:
redis.unixism.net. Այս սերվերը գործարկում է Redis daemon-ը, որտեղ պահվում են հյուրերի գրքի մուտքերը և այցելուների հաշվիչները:
Բոլոր սերվերները աշխատում են նույն պրոցեսորի միջուկով: Գաղափարը յուրաքանչյուր ճարտարապետության առավելագույն կատարումը գնահատելն է: Քանի որ բոլոր սերվերային ծրագրերը փորձարկվում են նույն սարքաշարի վրա, սա համեմատության հիմք է: Իմ թեստային կարգավորումը բաղկացած է Digital Ocean-ից վարձակալված վիրտուալ սերվերներից:
Ի՞նչ ենք մենք չափում:
Դուք կարող եք չափել տարբեր ցուցանիշներ: Մենք գնահատում ենք յուրաքանչյուր ճարտարապետության աշխատանքը տվյալ կոնֆիգուրացիայի մեջ՝ սերվերները բեռնելով զուգահեռության տարբեր մակարդակների հարցումներով.
Թեստի արդյունքներ
Հետևյալ գծապատկերը ցույց է տալիս սերվերների աշխատանքը տարբեր ճարտարապետությունների վրա՝ զուգահեռության տարբեր մակարդակներում: y առանցքը վայրկյանում հարցումների քանակն է, x առանցքը՝ զուգահեռ կապեր։
Ստորև ներկայացված է աղյուսակ՝ արդյունքներով:
հարցումներ մեկ վայրկյանում
զուգահեռություն կրկնվող պատառաքաղ նախնական պատառաքաղ հոսք նախնական հեռարձակում քվեարկություն էպոլլ
20
7
112
2100
1800
2250
1900
2050
50
7
190
2200
1700
2200
2000
2000
100
7
245
2200
1700
2200
2150
2100
200
7
330
2300
1750
2300
2200
2100
300
-
380
2200
1800
2400
2250
2150
400
-
410
2200
1750
2600
2000
2000
500
-
440
2300
1850
2700
1900
2212
600
-
460
2400
1800
2500
1700
2519
700
-
460
2400
1600
2490
1550
2607
800
-
460
2400
1600
2540
1400
2553
900
-
460
2300
1600
2472
1200
2567
1000
-
475
2300
1700
2485
1150
2439
1500
-
490
2400
1550
2620
900
2479
2000
-
350
2400
1400
2396
550
2200
2500
-
280
2100
1300
2453
490
2262
3000
-
280
1900
1250
2502
լայն տարածում
2138
5000
-
լայն տարածում
1600
1100
2519
-
2235
8000
-
-
1200
լայն տարածում
2451
-
2100
10 000
-
-
լայն տարածում
-
2200
-
2200
11 000
-
-
-
-
2200
-
2122
12 000
-
-
-
-
970
-
1958
13 000
-
-
-
-
730
-
1897
14 000
-
-
-
-
590
-
1466
15 000
-
-
-
-
532
-
1281
Գրաֆիկից և աղյուսակից երևում է, որ 8000 միաժամանակյա հարցումներից ավելին մեզ մնացել է ընդամենը երկու խաղացող՝ pre-fork և epoll: Քանի որ բեռը մեծանում է, հարցումների վրա հիմնված սերվերն ավելի վատ է աշխատում, քան հոսքայինը: Թելերի նախաստեղծման ճարտարապետությունը արժանի մրցակից է epoll-ին, վկայում է այն մասին, թե որքան լավ է Linux միջուկը պլանավորում մեծ թվով թելեր:
ZeroHTTPd աղբյուրի կոդը
ZeroHTTPd աղբյուրի կոդը այստեղ. Յուրաքանչյուր ճարտարապետության համար կա առանձին գրացուցակ:
Բացի բոլոր ճարտարապետությունների համար յոթ գրացուցակներից, վերին մակարդակի գրացուցակում կան ևս երկուսը` հանրային և կաղապարներ: Առաջինը պարունակում է index.html ֆայլը և առաջին սքրինշոթի պատկերը: Դուք կարող եք այնտեղ տեղադրել այլ ֆայլեր և թղթապանակներ, և ZeroHTTPd-ը պետք է առանց որևէ խնդրի սպասարկի այդ ստատիկ ֆայլերը: Եթե զննարկիչի ուղին համընկնում է հանրային թղթապանակի ուղու հետ, ապա ZeroHTTPd-ը փնտրում է index.html ֆայլը այս գրացուցակում: Հյուրերի գրքի բովանդակությունը ստեղծվում է դինամիկ: Այն ունի միայն գլխավոր էջ, և դրա բովանդակությունը հիմնված է «templates/guestbook/index.html» ֆայլի վրա: ZeroHTTPd-ը հեշտությամբ ավելացնում է դինամիկ էջեր ընդլայնման համար: Գաղափարն այն է, որ օգտվողները կարող են ձևանմուշներ ավելացնել այս գրացուցակում և անհրաժեշտության դեպքում երկարացնել ZeroHTTPd-ը:
Բոլոր յոթ սերվերները կառուցելու համար գործարկեք make all վերին մակարդակի գրացուցակից - և բոլոր կառուցումները կհայտնվեն այս գրացուցակում: Գործարկվող ֆայլերը փնտրում են հանրային և ձևանմուշների դիրեկտորիաներ այն գրացուցակում, որտեղից դրանք գործարկվել են:
Linux API
Այս հոդվածաշարի տեղեկատվությունը հասկանալու համար ձեզ հարկավոր չէ լավ տիրապետել Linux API-ին: Այնուամենայնիվ, ես խորհուրդ եմ տալիս ավելին կարդալ այս թեմայի վերաբերյալ, ինտերնետում կան բազմաթիվ տեղեկատու ռեսուրսներ: Թեև մենք կանդրադառնանք Linux API-ների մի քանի կատեգորիաների, մեր ուշադրությունը հիմնականում կենտրոնանալու է գործընթացների, շղթաների, իրադարձությունների և ցանցային ստեկի վրա: Բացի Linux API-ի մասին գրքերից և հոդվածներից, ես նաև խորհուրդ եմ տալիս կարդալ մանա համակարգային զանգերի և օգտագործվող գրադարանային գործառույթների համար:
Կատարողականություն և մասշտաբայնություն
Մեկ նշում կատարողականության և մասշտաբայնության մասին: Նրանց միջեւ տեսականորեն որեւէ կապ չկա։ Դուք կարող եք ունենալ վեբ ծառայություն, որը շատ լավ է աշխատում՝ մի քանի միլիվայրկյան արձագանքման ժամանակով, բայց այն ընդհանրապես չի ծավալվում: Նմանապես, կարող է լինել վատ կատարող վեբ հավելված, որի պատասխանը տևում է մի քանի վայրկյան, բայց այն տատանվում է տասնյակ հազարավոր միաժամանակյա օգտատերերի համար: Այնուամենայնիվ, բարձր կատարողականության և մասշտաբայնության համադրությունը շատ հզոր համադրություն է: Բարձր արդյունավետությամբ հավելվածները սովորաբար օգտագործում են ռեսուրսները խնայողաբար և այդպիսով արդյունավետ կերպով սպասարկում են սերվերի ավելի շատ միաժամանակ օգտագործողներ՝ նվազեցնելով ծախսերը:
CPU և I/O առաջադրանքներ
Ի վերջո, հաշվարկների մեջ միշտ կան երկու հնարավոր տեսակի առաջադրանքներ՝ I/O և CPU-ի համար: Ինտերնետով հարցումներ ստանալը (ցանցային I/O), ֆայլերի սպասարկումը (ցանցային և սկավառակի I/O), տվյալների բազայի հետ հաղորդակցվելը (ցանց և սկավառակի I/O) բոլոր I/O գործողություններն են: Տվյալների բազայի որոշ հարցումներ կարող են լինել մի փոքր ինտենսիվ պրոցեսոր (տեսակավորում, միջինում մեկ միլիոն արդյունք և այլն): Վեբ հավելվածների մեծ մասը սահմանափակված է առավելագույն հնարավոր I/O-ով, և պրոցեսորը հազվադեպ է օգտագործվում ամբողջ հզորությամբ: Երբ տեսնում եք, որ որոշ I/O առաջադրանք օգտագործում է մեծ քանակությամբ պրոցեսոր, դա, ամենայն հավանականությամբ, կիրառման վատ ճարտարապետության նշան է: Սա կարող է նշանակել, որ պրոցեսորի ռեսուրսները վատնվում են գործընթացի կառավարման և համատեքստի փոխարկման վրա, և դա ամբողջովին օգտակար չէ: Եթե դուք ինչ-որ բան եք անում, ինչպիսին է պատկերի մշակումը, աուդիո ֆայլերի փոխակերպումը կամ մեքենայական ուսուցումը, ապա հավելվածը պահանջում է հզոր պրոցեսորի ռեսուրսներ: Բայց շատ ծրագրերի համար դա այդպես չէ: