Hacker News

Մենք միացրեցինք մեկ թելային C++-ը բազմաթելային Rust-ի հետ

Մենք միացրեցինք մեկ թելային C++-ը բազմաթելային Rust-ի հետ Interface-ի այս համապարփակ վերլուծությունը առաջարկում է դրա հիմնական բաղադրիչների և ավելի լայն հետևանքների մանրամասն ուսումնասիրություն: Ուշադրության հիմնական ոլորտները Քննարկումը կենտրոնացած է. Հիմնական մեխանիկական...

1 min read Via antithesis.com

Mewayz Team

Editorial Team

Hacker News
Ահա ամբողջական SEO բլոգի գրառումը.

Մենք ինտերֆեյս ենք արել մեկ թելային C++-ով բազմաթելային ժանգով

Միաթելային C++ կոդի ինտերֆեյսը բազմաթելային Rust-ի հետ ոչ միայն հնարավոր է, այլ դա հին համակարգերը արդիականացնելու ամենապրակտիկ եղանակներից մեկն է առանց լրիվ վերաշարադրման: Mewayz-ում մենք հաղթահարեցինք հենց այս մարտահրավերը, երբ մեր 207 մոդուլից բաղկացած բիզնես ՕՀ-ն ընդլայնեցինք 138,000 օգտատերերի սպասարկելու համար, և արդյունքները հիմնովին փոխեցին մեր պատկերացումները համակարգերի փոխգործունակության մասին:

Ինչու՞ պետք է ինտերֆեյս մեկ թելային C++-ով բազմաթելային ժանգով:

Արտադրական համակարգերի մեծ մասը կրում է տարիներ շարունակ փորձարկված C++ կոդ: Rust-ում ամեն ինչ վերաշարադրելը գրավիչ է թվում թղթի վրա, բայց այն ներկայացնում է հսկայական ռիսկ և ամիսներ ինժեներական ժամանակ: Պրագմատիկ մոտեցումը աստիճանական ընդունումն է՝ փաթաթելով գոյություն ունեցող C++ տրամաբանությունը՝ միաժամանակ բեռնաթափելով ծանրաբեռնվածությունը Rust-ի սեփականության մոդելում:

Մեր դեպքում, հիմնական բիզնեսի տրամաբանական մոդուլները տարիներ շարունակ հուսալիորեն աշխատում էին մեկ թելերով C++-ում: Նրանք իրականացնում էին առաջադրանքների հաջորդական մշակում, փաստաթղթերի ստեղծում և ֆինանսական հաշվարկներ: Բայց քանի որ մեր օգտատերերի բազան ավելացավ 100 հազարից, մեզ անհրաժեշտ էր տվյալների զուգահեռ մշակում, API-ի միաժամանակյա մշակում և ընդհանուր վիճակի անվտանգ կառավարում: Rust-ի Send և Sync հատկանիշները մեզ տվել են հավաքագրման ժամանակի համաժամանակյա երաշխիքներ, որ C++-ը պարզապես չէր կարող առաջարկել առանց լայնածավալ ձեռքով աուդիտի:

Հիմնական մոտիվացիան ռիսկի նվազեցումն է: Դուք պահպանում եք այն, ինչ աշխատում է, և ավելացնում եք ինչ կշեռքներ՝ առանց մոլախաղերի ձեր ամբողջ կոդերի բազան միգրացիայի վրա, որը կարող է երբեք չավարտվել:

Ինչպե՞ս է իրականում աշխատում FFI սահմանը:

Օտարերկրյա ֆունկցիայի միջերեսը (FFI) C++-ի և Rust-ի միջև գործում է C-ի հետ համատեղելի ֆունկցիայի ստորագրությունների միջոցով: Rust-ի արտաքին «C» բլոկները բացահայտում են գործառույթներ, որոնք C++-ը կարող է ուղղակիորեն զանգահարել և հակառակը: Կարևորագույն մարտահրավերն ի հայտ է գալիս, երբ Rust-ի բազմաշերտ գործարկման ժամանակը պետք է ապահով կերպով կանչի մեկ շարան C++ կոդը:

Մենք դա լուծեցինք՝ օգտագործելով հատուկ ճարտարապետություն՝

  • Թելերով սահմանափակված C++ կատարող. Բոլոր C++ զանգերը փոխանցվում են մեկ հատուկ շղթայի միջոցով՝ օգտագործելով հաղորդագրություն փոխանցող ալիք՝ ապահովելով, որ մեկ շղթայական անփոփոխությունը երբեք չի խախտվի:
  • Rust async bridge layer. Tokio-ի առաջադրանքները աշխատանքն ուղարկում են C++ կատարողին և սպասում արդյունքներին oneshot ալիքներով՝ պահպանելով Rust-ի կողմը լիովին ասինխրոն:
  • Անթափանց ցուցիչի կառավարում. C++ օբյեկտները փաթաթված են Rust կառուցվածքներով, որոնք իրականացնում են Drop որոշիչ մաքրման համար՝ կանխելով հիշողության արտահոսքը լեզվի սահմաններից մեկում:
  • Սերիալիզացիա սահմանի վրա. Տվյալների բարդ կառուցվածքները սերիականացված են FlatBuffers-ով FFI շերտում՝ խուսափելով կառուցվածքի փխրուն դասավորության համապատասխանությունից և հնարավորություն տալով յուրաքանչյուր կողմի անկախ էվոլյուցիան:
  • Խուճապի մեկուսացում. Rust-ի catch_unwind պարուրում է FFI մուտքի յուրաքանչյուր կետ այնպես, որ խուճապը երբեք չի հատում լեզվի սահմանը, ինչը կլինի անորոշ վարքագիծ:

Այս օրինաչափությունը մեզ տվեց բազմաթելային Rust-ի թողունակությունը՝ ապացուցված C++ տրամաբանության հուսալիությամբ՝ առանց նախնական բիզնես կանոնների ոչ մի տող վերաշարադրելու:

Որո՞նք են ամենամեծ որոգայթները, որոնցից պետք է խուսափել:

Ամենավտանգավոր սխալը ենթադրելն է, որ C++ կոդը անվտանգ է, երբ դա այդպես չէ: Համաշխարհային վիճակը, ստատիկ փոփոխականները և գրադարանի չվերադարձնող զանգերը կառաջացնեն տվյալների մրցավազք, որոնք Rust-ի կոմպիլյատորը չի կարող հայտնաբերել FFI-ի սահմանից այն կողմ: Rust-ի անվտանգության երաշխիքները կանգ են առնում անապահով բլոկում. ներսում ամեն ինչ ձեր պատասխանատվությունն է:

Հիմնական պատկերացում. Rust-ը երաշխավորում է հիշողության անվտանգությունը իր սեփական ծածկագրի շրջանակներում, սակայն այն պահին, երբ դուք անցնում եք FFI սահմանը դեպի C++, դուք ժառանգում եք շղթաների անվտանգության բոլոր խնդիրները, որոնք ունի C++-ը: Այդ սահմանի շուրջ ճարտարապետությունն ավելի կարևոր է, քան դրա երկու կողմի ծածկագիրը:

💡 DID YOU KNOW?

Mewayz replaces 8+ business tools in one platform

CRM · Invoicing · HR · Projects · Booking · eCommerce · POS · Analytics. Free forever plan available.

Start Free →

Մյուս տարածված որոգայթը ողջ կյանքի ընթացքում կառավարումն է: C++ օբյեկտները չեն մասնակցում Rust-ի փոխառության ստուգմանը: Եթե ​​Rust-ը թողնում է հղումը, մինչդեռ C++-ը դեռևս ցուցիչ է պահում, դուք ստանում եք առանց օգտագործման սխալներ, որոնք դաժանորեն դժվար է ախտորոշել: Մենք լուծեցինք դա՝ կիրառելով խիստ սեփականության իմաստաբանություն. C++ օբյեկտները միշտ պատկանում են հենց մեկ Rust փաթաթանին, և ընդհանուր մուտքն անցնում է Rust-ի կողմից Arc-ի վրա հիմնված հղումների հաշվարկով:

Կատարման առումով, FFI-ի չափից դուրս զանգերը ստեղծում են համատեքստի փոխարկումից և սերիականացումից: Մենք խմբաքանակային գործառնություններ ենք անում, որտեղ հնարավոր է, ուղարկելով աշխատանքային տարրերի հերթ C++ կատարողին, այլ ոչ թե անհատական միջլեզու զանգեր կատարելու:

Ինչպե՞ս կատարվեց այս մոտեցումը արտադրության մեջ:

Հիբրիդային ճարտարապետությունը մեր հարթակում տեղակայելուց հետո մենք չափեցինք կոնկրետ բարելավումները: Հարցման թողունակությունն ավելացել է 3,4 անգամ այն ​​մոդուլների համար, որոնք նախկինում խցանված էին C++ հաջորդական մշակման ժամանակ: Tail-ի հետաձգումը (p99) նվազել է 61%-ով, քանի որ Rust-ի համաժամանակյա գործարկման ժամանակը կարող էր միաժամանակ մշակել անկախ հարցումները, մինչդեռ C++-ը կատարում էր հաշվողական ծանր առաջադրանքներ իր հատուկ շղթայի վրա:

Ավելի կարևոր է, որ արտադրության առաջին վեց ամիսների ընթացքում մենք ունեցել ենք զրոյական համաժամանակյա սխալներ: Թելերի սահմանափակման օրինաչափությունը կառուցվածքային առումով անհնար էր դարձնում C++ կոդի կանչը մի քանի թելերից, մինչդեռ Rust-ի տիպային համակարգը կանխում էր տվյալների մրցավազքը սահմանի իր կողմում: Սա զգալի բարելավում էր մեր նախորդ մոտեցման համեմատ՝ փորձելով կապել C++-ին մուտեքսներով, որոնք մեկ եռամսյակում երեք ռասայական միջադեպեր էին առաջացրել:

Ինժեներական թիմը նաև հաղորդել է ավելի արագ կրկնվող ցիկլեր: Rust-ում կարող են ստեղծվել նոր հնարավորություններ՝ ամբողջական համաժամանակյա աջակցությամբ, մինչդեռ գոյություն ունեցող C++ մոդուլները շարունակում էին աշխատել առանց փոփոխության: Այս աճող ռազմավարությունը նշանակում էր, որ մենք երբեք չենք ունեցել բարձր ռիսկային «մեծ պայթյուն» միգրացիա, պարզապես կայուն, չափելի բարելավում:

Հաճախակի տրվող հարցեր

Կարո՞ղ է Rust-ը առանց փոփոխության զանգահարել մեկ շղթա ունեցող C++ գրադարաններ:

Այո, բայց դուք պետք է համոզվեք, որ այդ գրադարան բոլոր զանգերը կատարվում են մեկ շղթայից: Ստանդարտ օրինաչափությունն այն է, որ ստեղծվի հատուկ կատարողի շարանը, որը սերիականացնում է բոլոր C++ զանգերը ալիքի միջոցով: Rust-ի async առաջադրանքները հարցումներ են ներկայացնում և սպասում պատասխանների՝ առանց արգելափակելու բազմաշերտ գործարկման ժամանակը: C++ կոդը ինքնին փոփոխություններ չի պահանջում. անվտանգության սահմանափակումն ամբողջությամբ կիրառվում է Rust-ի կողմից:

Արդյո՞ք FFI-ի վերադիր ծախսերը բավականաչափ կարևոր են հավելվածի աշխատանքի վրա ազդելու համար:

Անհատական FFI զանգերն ունեն նվազագույն ծախսեր՝ սովորաբար 10 նանվայրկյանից ցածր՝ գործառույթի պարզ զանգի համար: Այնուամենայնիվ, տվյալների բարդ կառուցվածքների սերիականացումը և թելերի համաժամացումը սահմանին գումարվում են, եթե դուք հազարավոր նուրբ զանգեր եք կատարում: Գործողությունների փաթեթավորումը և զրոյական կրկնօրինակման սերիականացման ձևաչափերի օգտագործումը, ինչպիսիք են FlatBuffers-ը կամ Cap'n Proto-ն, նույնիսկ մասշտաբով աննշան են մնում:

Արդյո՞ք մենք պետք է վերագրենք մեր C++ կոդերի բազան Rust-ում` ինտերֆեյսի փոխարեն:

Թիմերի մեծամասնության համար աստիճանական ինտերֆեյսն ավելի անվտանգ և արագ ճանապարհ է: Ամբողջական վերաշարադրումը ներկայացնում է ինժեներական ռիսկի ամիսներ՝ առանց օգտագործողի առջև դրված արժեքի մինչև ավարտը: Ինտերֆեյսը թույլ է տալիս անմիջապես առաքել բարելավումները, վավերացնել Rust մոտեցումը արտադրության մեջ և մոդուլները մեկ առ մեկ տեղափոխել՝ հիմնվելով այն վայրի վրա, որտեղ համաժամանակությունն առավելագույն ազդեցություն է թողնում: Վերագրեք միայն այն մոդուլները, որտեղ FFI սահմանի պահպանման արժեքը գերազանցում է վերաշարադրման արժեքը:


Mewayz-ում մենք կառուցում ենք ենթակառուցվածքներ, որոնք մասշտաբային են՝ և՛ տեխնիկապես, և՛ գործառնական: Մեր 207 մոդուլից բաղկացած բիզնես ՕՀ-ն օգնում է 138,000 թիմերի ավելի խելացի աշխատանքային հոսքեր իրականացնել՝ սկսած $19/ամսական արժեքից: Անկախ նրանից, թե դուք կառավարում եք նախագծերը, ավտոմատացնում եք գործառնությունները կամ մեծացնում ձեր բիզնեսը, Mewayz-ը հարմարվում է ձեր աշխատանքի ձևին: Սկսեք ձեր անվճար փորձաշրջանը app.mewayz.com կայքում և տեսեք, թե ինչ կարող է անել ժամանակակից բիզնես ՕՀ-ն ձեր թիմի համար: