Ինչպես է OpenAI-ը մեծ մասշտաբով մատուցում ցածր հապաղումով ձայնային արհեստական բանականություն
Յի Ժանգի և Ուիլյամ ՄաքԴոնալդի՝ տեխնիկական անձնակազմի անդամներ
Ձայնային արհեստական բանականությունը բնական է թվում միայն այն դեպքում, երբ զրույցը ընթանում է խոսքի արագությամբ։ Երբ ցանցը խանգարում է, մարդիկ դա անմիջապես լսում են՝ որպես անհարմար դադարներ, կտրտված ընդհատումներ կամ խոսքին ուշացած միջամտում։ Դա կարևոր է ChatGPT‑ի ձայնային գործառույթի համար, Realtime API-ով լուծումներ ստեղծող ծրագրավորողների համար, ինտերակտիվ աշխատանքային հոսքերում աշխատող ագենտների համար և այն մոդելների համար, որոնք պետք է մշակեն աուդիո, մինչ օգտատերը դեռ խոսում է։
OpenAI-ի մասշտաբների դեպքում դա վերածվում է երեք կոնկրետ պահանջների՝
- Համաշխարհային ընդգրկում՝ շաբաթական ավելի քան 900 միլիոն ակտիվ օգտատերերի համար
- Կապի արագ հաստատում, որպեսզի օգտատերը կարողանա սկսել խոսել աշխատաշրջանը սկսվելուն պես
- Մեդիայի երկկողմանի փոխանցման ժամանակը ցածր և կայուն է՝ ցածր տատանումներով և փաթեթների կորստով, այնպես որ հերթափոխով խոսակցությունը սահուն է և արձագանքող։
OpenAI-ում իրական ժամանակի արհեստական բանականության փոխազդեցությունների համար պատասխանատու թիմը վերջերս վերանախագծեց մեր WebRTC ստեկի ճարտարապետությունը՝ լուծելու երեք սահմանափակում, որոնք մեծ մասշտաբում սկսել էին բախվել միմյանց․ յուրաքանչյուր սեսիայի համար մեկ պորտով մեդիայի տերմինացիան լավ չի համապատասխանում OpenAI-ի ենթակառուցվածքին, վիճակային ICE (Interactive Connectivity Establishment՝ ինտերակտիվ կապակցելիության հաստատում) և DTLS (Datagram Transport Layer Security՝ տվյալագրամների տրանսպորտային շերտի անվտանգություն) սեսիաներին անհրաժեշտ է կայուն պատկանելություն, իսկ գլոբալ երթուղավորումը պետք է առաջին անցման հապաղումը պահի ցածր մակարդակի վրա։ Այս գրառման մեջ մենք քայլ առ քայլ ներկայացնում ենք մեր կառուցած բաժանված ռելե + փոխանցիչ-ընդունիչ ճարտարապետությունը, որը թույլ է տալիս հաճախորդների համար պահպանել WebRTC-ի ստանդարտ վարքագիծը՝ միաժամանակ փոխելով, թե ինչպես են փաթեթները երթուղավորվում OpenAI-ի ենթակառուցվածքի ներսում։
WebRTC-ը բաց ստանդարտ է, որը նախատեսված է դիտարկիչների, բջջային հավելվածների և սերվերների միջև ցածր հապաղմամբ աուդիո, վիդեո և տվյալներ փոխանցելու համար։ Այն հաճախ կապում են peer-to-peer (P2P) զանգերի հետ, սակայն այն նաև գործնական հիմք է հաճախորդից սերվեր իրական ժամանակի համակարգերի համար, քանի որ ստանդարտացնում է ինտերակտիվ մեդիայի ամենաբարդ մասերը՝ ICE՝ կապի հաստատման համար, NAT (Network Address Translation)՝ NAT-ի միջով անցման (traversal) համար, DTLS և SRTP (Secure Real-time Transport Protocol)՝ կոդավորված փոխանցման համար, կոդեկների համաձայնեցում՝ աուդիոն սեղմելու և ապակոդավորելու համար, RTCP (Real-time Transport Control Protocol)՝ որակի վերահսկման համար, ինչպես նաև հաճախորդի կողմի հնարավորություններ՝ օրինակ էխոյի չեղարկում և jitter buffering։
Այդ ստանդարտացումը կարևոր է ԱԲ պրոդուկտների համար։ Առանց WebRTC-ի՝ յուրաքանչյուր հաճախորդի անհրաժեշտ կլիներ առանձին լուծում՝ կապակցումը NAT-ների միջով հաստատելու, մեդիան գաղտնագրելու, կոդեկները համաձայնեցնելու (փոխանցման և ապասեղմման համար ընտրված կոդավորիչ-ապակոդավորիչներ) և փոփոխվող ցանցային պայմաններին հարմարվելու համար։ WebRTC-ի միջոցով մենք կարող ենք հիմնվել հաղորդակարգերի փաթեթի վրա, որն արդեն ներդրված է բրաուզերներում և շարժական հարթակներում՝ կենտրոնանալով այն ենթակառուցվածքի վրա, որը իրական ժամանակի մեդիան կապում է մոդելների հետ։
Մենք նաև հիմնվում ենք WebRTC էկոհամակարգի վրա՝ ներառյալ կայացած բաց կոդով իրականացումները և ստանդարտ աշխատանքը, որը ապահովում է բրաուզերների, բջջային հավելվածների և սերվերների փոխգործունակությունը։ WebRTC-ի սկզբնական ճարտարապետներից մեկը՝ Ջասթին Ուբերտին, և Pion-ի ստեղծողն ու սպասարկողը՝ Շոն Դուբուան, իրենց հիմնարար աշխատանքով հնարավորություն տվեցին մեր նման թիմերին կառուցել փորձարկված մեդիա ենթակառուցվածքի վրա՝ փոխարենը զրոյից վերստեղծելու ցածր մակարդակի տրանսպորտի, գաղտնագրման և ծանրաբեռնվածության վերահսկման վարքագիծը։ Մեզ համար մեծ պատիվ է, որ Ջասթինն ու Շոնը այժմ մեր գործընկերներն են OpenAI-ում՝ աջակցելով WebRTC-ի և իրական ժամանակի ԱԲ-ի համատեղման գործընթացին։
Արհեստական բանականության համար ամենակարևոր հատկությունն այն է, որ աուդիոն ստացվում է շարունակական հոսքով։ Խոսքային ագենտը կարող է սկսել արտագրել, հիմնավորում անել, գործիքներ կանչել կամ խոսք ստեղծել՝ առանց ամբողջական վերբեռնմանը սպասելու։ Սա է տարբերությունը զրուցային համակարգի և «սեղմիր՝ խոսելու համար» ռեժիմի տպավորություն թողնող համակարգի միջև։
Երբ ընտրեցինք WebRTC-ը, հաջորդ հարցն այն էր, թե որտեղ ավարտել այն (որտեղ կընդունեինք և մեր կողմից կկառավարեինք WebRTC կապը, օրինակ՝ եզրին) և ինչպես այդ սեսիաները կապել ինֆերենսի բեքենդին։ Ավարտումը կարևոր է, քանի որ այն որոշում է, թե ինչպես ենք մենք կառավարում իրական ժամանակի սեսիայի վիճակը, մեդիայի փոխանցումը, երթուղավորումը, հապաղումը և խափանումների մեկուսացումը։
SFU-ն, կամ ընտրովի փոխանցման միավորը, մեդիա սերվեր է, որը յուրաքանչյուր մասնակցից ստանում է մեկ WebRTC հոսք և ընտրողաբար փոխանցում է հոսքերը մյուսներին։ Այս մոդելում SFU-ն վերջնակետ է հանդիսանում յուրաքանչյուր մասնակցի համար առանձին WebRTC կապի, իսկ ԱԻ-ն միանում է նիստին որպես ևս մեկ մասնակից։ Դա կարող է լավ ընտրություն լինել այն արտադրանքների համար, որոնք բնույթով բազմամասնակից են, ինչպիսիք են խմբային զանգերը, դասարանները կամ համագործակցային հանդիպումները։ Այն մեկ տեղում է պահում աուդիո կոդեկները, RTCP հաղորդագրությունները, տվյալների ալիքները, ձայնագրումը և յուրաքանչյուր հոսքի քաղաքականությունը։1
Նույնիսկ հաճախորդից դեպի ԱԲ արտադրանքներում SFU-ն հաճախ լռելյայն մեկնարկային կետ է, քանի որ այն թիմերին թույլ է տալիս կրկին օգտագործել մեկ ապացուցված համակարգ՝ ազդանշանավորման, մեդիայի երթուղավորման, ձայնագրման, դիտարկելիության և ապագա ընդլայնումների համար, օրինակ՝ մարդ օպերատորին փոխանցումը կամ ավելի շատ մասնակիցների ավելացումը։
Մեր ծանրաբեռնվածությունը տարբեր է։ Սեանսների մեծ մասը 1:1 են՝ մեկ օգտատեր խոսում է մեկ մոդելի հետ, կամ մեկ հավելված՝ մեկ իրական ժամանակի ագենտի հետ՝ յուրաքանչյուր հերթում հապաղման նկատմամբ զգայունությամբ։ Այդպիսի տրաֆիկի բնույթի համար մենք ընտրեցինք տրանսիվերի մոդել․ WebRTC եզրային ծառայությունը տերմինացնում է հաճախորդի կապը, ապա մեդիան և իրադարձությունները փոխակերպում է ավելի պարզ ներքին արձանագրությունների՝ մոդելի եզրահանգման, տեքստավորման, խոսքի գեներացման, գործիքների օգտագործման և օրկեստրացման համար։
Այս նախագծում հաղորդիչ-ընդունիչը միակ ծառայությունն է, որը կառավարում է WebRTC նիստի վիճակը՝ ներառյալ ICE կապակցելիության ստուգումները, DTLS ձեռքսեղմումը, SRTP կոդավորման բանալիները և նիստի կյանքի ցիկլը։ “Termination”-ն այստեղ նշանակում է, որ տրանսիվերը այն վերջնակետն է, որն ավարտին է հասցնում այդ ձեռքսեղմման գործընթացները և գաղտնագրում կամ վերծանում է մեդիան։ Այդ վիճակը մեկ տեղում պահելը ավելի հեշտացրեց նիստի պատկանելության մասին դատողություններ անելը, և դա թույլ տվեց backend ծառայություններին մասշտաբավորվել սովորական ծառայությունների պես՝ փոխարենը իրենք որպես WebRTC peer-ներ հանդես գալու։
Հաղորդիչ-ընդունիչի մոդելը ընտրելուց հետո մեր առաջին իրականացումը Pion-ի հիման վրա կառուցված մեկ Go ծառայություն էր, որը մշակել է ինչպես ազդանշանային փոխանակումը, այնպես էլ մեդիայի ավարտը։ Այն հիմք է հանդիսանում ChatGPT‑ի ձայնային գործառույթի, Realtime API-ի WebRTC վերջնակետի և մի շարք հետազոտական նախագծերի համար։
Գործառնական առումով՝ ընդունիչ-հաղորդիչ ծառայությունը կատարում է երկու գործառույթ՝
- Ազդանշանավորում՝ SDP-ի համաձայնեցում, կոդեկի ընտրություն, ICE մուտքի տվյալներ և սեսիայի կարգավորում
- Մեդիա․ ներքևահոսքային WebRTC միացումների ավարտում և եզրակացության ու օրկեստրացիայի համար հետնամասային ծառայությունների հետ վերևահոսքային միացումների պահպանում
Մենք ցանկանում էինք, որ ծառայությունը գործարկվի այնպես, ինչպես մեր ենթակառուցվածքի մնացած մասը՝ Kubernetes-ում, որտեղ աշխատանքային բեռնվածությունները կարող են մեծանալ ու փոքրանալ և տեղափոխվել հոսթերի միջև՝ պահանջարկի փոփոխմանը զուգընթաց։ Սակայն յուրաքանչյուր սեանսի համար մեկ պորտ նախատեսող WebRTC ավանդական մոդելը վատ է համապատասխանում այդ միջավայրին, քանի որ այն կախված է հանրային UDP պորտերի մեծ միջակայքերից, որոնք դժվար է հասանելի դարձնել, անվտանգ դարձնել և պահպանել, երբ pod-երն ավելացվում, հեռացվում կամ վերապլանավորվում են։2
Առաջին խնդիրը հենց «յուրաքանչյուր աշխատաշրջանի համար մեկ պորտ» մոդելն էր։ Բարձր միաժամանակայնության դեպքում դա ենթադրում է հասանելի դարձնել և կառավարել UDP պորտերի շատ լայն միջակայքեր։
- Ամպային բեռի բաշխիչներն ու Kubernetes ծառայությունները նախատեսված չեն յուրաքանչյուր ծառայության համար տասնյակ հազարավոր հանրային UDP պորտերի համար։ Յուրաքանչյուր լրացուցիչ միջակայք ավելացնում է գործառնական բարդություն բեռի բաշխիչի կազմաձևման, առողջության ստուգման, միջցանցային էկրանի քաղաքականության և ներդրման անվտանգության մեջ։3
- UDP պորտերի մեծ միջակայքները դժվար է ապահովել անվտանգությամբ, քանի որ դրանք ընդլայնում են արտաքին հասանելիության տիրույթը և ավելի են դժվարացնում ցանցային քաղաքականության ստուգումը։
- Դրանք նաև հարմար չեն ավտոմատ մասշտաբավորման համար։ Պոդերը մշտապես ավելացվում, հեռացվում և վերապլանավորվում են Kubernetes-ում։ Յուրաքանչյուր պոդից պահանջելը, որ այն պահուստավորի և ազդարարի պորտերի մեծ, կայուն միջակայք, այդ էլաստիկությունը դարձնում է փխրուն։4
Հենց այդ պատճառով WebRTC-ի շատ համակարգեր անցնում են մեկ UDP պորտի յուրաքանչյուր սերվերի համար՝ կիրառական մակարդակի դեմուլտիպլեքսավորմամբ այդ պորտի հետևում։5
Մեկ սերվերի համար մեկ պորտ նախատեսող նախագծերը լուծում են պորտերի քանակի խնդիրը, սակայն առաջացնում են երկրորդ խնդիր՝ սերվերների պարկի ողջ մասշտաբով յուրաքանչյուր նստաշրջանի պատկանելիությունը պահպանելը։
ICE-ն և DTLS-ը վիճակային արձանագրություններ են։ Այն գործընթացը, որը ստեղծել է սեանսը, պետք է շարունակի ստանալ այդ սեանսի փաթեթները՝ վավերացնելու կապունակության ստուգումները, ավարտելու DTLS ձեռքսեղմումը, վերծանելու SRTP-ն և մշակելու սեանսի հետագա փոփոխությունները, օրինակ՝ ICE-ի վերագործարկումները։ Եթե նույն սեսիայի փաթեթները հայտնվեն այլ պրոցեսում, կարգավորումը կարող է ձախողվել կամ մեդիան կարող է խափանվել։
Դա մեզ տվեց հստակ նպատակ՝ հանրային ցանցի համար բացել փոքր, ֆիքսված UDP մակերես՝ միաժամանակ յուրաքանչյուր փաթեթը ուղղորդելով այն տրանսիվերին, որին պատկանում է համապատասխան WebRTC սեսիան։
Մենք գնահատեցինք դրան հասնելու մի քանի եղանակներ, այդ թվում՝ TURN-ը (Traversal Using Relays around NAT՝ NAT-ի շուրջ ռելեների միջոցով անցում), որտեղ եզրային ռելեն ավարտում է հաճախորդների հատկացումները և փոխանցում տրաֆիկը նրանց անունից։2
Մոտեցում | Առավելություններ | Բացասական կողմեր |
Յուրաքանչյուր աշխատաշրջանի համար եզակի IP:պորտ (հայտնի է նաև որպես բնիկ ուղղակի UDP) | Մեդիայի ուղղակի ուղի՝ հաճախորդից սերվեր Տվյալների ուղում վերահասցեավորման շերտ չկա | Յուրաքանչյուր սեանսի համար պահանջվում է մեկ հանրային UDP պորտ Պորտերի մեծ տիրույթները դժվար է հասանելի դարձնել և ապահովել Անհամապատասխան Kubernetes-ի և ամպային բեռի բաշխիչների համար |
Եզակի IP:պորտ յուրաքանչյուր սերվերի համար | Շատ ավելի փոքր հանրային UDP մակերես՝ համեմատած յուրաքանչյուր սեանսի բացահայտման հետ Մեկ սերվերի համար մեկ ընդհանուր սոկետը կարող է դեմուլտիպլեքսավորել բազմաթիվ աշխատաշրջաններ | Մեկ հոսթի վրա անխափան է աշխատում, բայց ինքնուրույն՝ ոչ բեռը բաշխվող ընդհանուր ֆլիթի ամբողջ տարածքով Մեկ հոսթի վրա սեանսների դեմուլտիպլեքսավորումը օգնում է միայն այն բանից հետո, երբ փաթեթը հասնում է այդ հոսթին․ բեռնաբաշխված սերվերների խմբում առաջին փաթեթը դեռ կարող է հայտնվել սխալ ինստանսում, ուստի անհրաժեշտ է դետերմինիստական եղանակ՝ յուրաքանչյուր սեանս ուղղորդելու դեպի տվյալ սեանսը տնօրինող գործընթացը։ |
TURN վերահաղորդիչ (արձանագրությունը ավարտող) | Հաճախորդներին անհրաժեշտ է միայն կապ հաստատել TURN ռելեի հասցեի և պորտի հետ՝ Կարող է կենտրոնացնել քաղաքականությունը եզրային մակարդակում | TURN-ի հատկացումները ավելացնում են կարգավորման լրացուցիչ երկկողմանի ցանցային փուլեր։ TURN սերվերների միջև հատկացումները տեղափոխելն ու վերականգնելը դեռևս դժվար է։ |
Առանց վիճակի վերահասցեավորիչ + վիճակով տերմինատոր (OpenAI-ի ռելե + ընդունիչ-հաղորդիչ) | Փոքր հանրային UDP հետք Տրանսիվերը դեռևս տնօրինում է ամբողջական WebRTC համաժողովը | Ավելացնում է փոխանցման մեկ միջանկյալ հանգույց՝ նախքան մեդիան կհասնի տրանսիվերին, որը պատկանում է համակարգին Պահանջվում է հատուկ համակարգում ռելեի և ընդունիչ-հաղորդիչի միջև |
Մեր առաքած ճարտարապետությունը տարանջատում է փաթեթների երթուղավորումը արձանագրության ավարտից։ Ազդանշանային հաղորդակցությունը դեռ հասնում է հաղորդիչ-ընդունիչին՝ նստաշրջանի կարգավորման համար, իսկ մեդիան նախ մուտք է գործում ռելեի միջոցով։ Ռելեն թեթև UDP փոխանցման շերտ է՝ փոքր հանրային հետքով, իսկ հաղորդիչ-ընդունիչը դրա հետևում գտնվող վիճակային WebRTC վերջնակետն է։
Ռելեյը չի վերծանում մեդիա տվյալները, չի գործարկում ICE վիճակների մեքենաներ և չի մասնակցում կոդեկների համաձայնեցմանը։ Այն կարդում է փաթեթի մետատվյալների բավարար մասը՝ նպատակակետ ընտրելու համար, ապա փաթեթը փոխանցում է սեանսը տնօրինող ընդունիչ-հաղորդիչին։ Տրանսիվերը դեռևս տեսնում է WebRTC-ի սովորական հոսք և դեռևս տիրապետում է պրոտոկոլի ամբողջ վիճակին։ Կլիենտի տեսանկյունից WebRTC աշխատաշրջանի հետ կապված ոչինչ չի փոխվում։
Առաջին փաթեթի երթուղավորումը այս կարգավորման առանցքային քայլն է։ Փոխանցիչը պետք է հաճախորդից ստացված առաջին փաթեթը երթուղավորի հենց փաթեթի ուղու վրա՝ նախքան որևէ սեսիա գոյություն կունենա, այլ ոչ թե դադար առնի արտաքին որոնման ծառայության վրա։
WebRTC-ի յուրաքանչյուր սեանս արդեն ներառում է արձանագրությանը բնորոշ երթուղավորման կապիչ՝ ICE օգտանվան հատվածը կամ ufrag-ը՝ կարճ նույնացուցիչ, որը փոխանակվում է սեանսի կարգավորման ընթացքում և արտացոլվում է STUN կապակցելիության ստուգումներում։ Մենք գեներացնում ենք սերվերի կողմի ufrag-ը այնպես, որ այն պարունակի բավարար երթուղավորման մետատվյալներ, որպեսզի ռելեյը կարողանա եզրակացնել նպատակակետ կլաստերը և պատկանող տրանսիվերը։
Ազդանշանային փոխանակման ընթացքում տրանսիվերը հատկացնում է նիստի վիճակը և SDP պատասխանում վերադարձնում է համօգտագործվող ռելեի VIP հասցեն և UDP պորտը։ VIP-ը վիրտուալ IP հասցե է, որը ծառայում է որպես ռելեների պարկի դիմային հասցե. պորտի հետ միասին այն հաճախորդին տալիս է մեկ կայուն նշանակետ, օրինակ՝ `203.0.113.10:3478`, թեև դրա հետևում տեղակայված են ռելեի բազմաթիվ օրինակներ։ Հաճախորդի առաջին մեդիա-ուղու փաթեթը սովորաբար STUN (Session Traversal Utilities for NAT) կապակցման հարցում է, որը ICE-ն օգտագործում է՝ ստուգելու, որ փաթեթները կարող են հասնել հայտարարված հասցեին։
Relay-ը վերլուծում է առաջին STUN փաթեթի միայն այն մասը, որն անհրաժեշտ է սերվերի ufrag-ը կարդալու, երթուղավորման հուշումը վերծանելու և փաթեթը պատկանող տրանսիվերին փոխանցելու համար։ Յուրաքանչյուր տրանսիվեր լսում է ընդհանուր UDP սոկետի վրա, ինչը նշանակում է, որ օպերացիոն համակարգի մեկ վերջնակետ կապված է ներքին IP:port-ի հետ, ոչ թե յուրաքանչյուր նստաշրջանի համար առանձին սոկետ։ Այն բանից հետո, երբ ռելեն ստեղծում է աշխատաշրջան՝ հաճախորդի աղբյուրային IP:port-ից դեպի այդ տրանսիվերի նպատակակետը, հաջորդող DTLS, RTP և RTCP փաթեթներն անցնում են աշխատաշրջանի շրջանակում՝ առանց ufrag-ը կրկին վերծանելու։
Ռելեի աշխատաշրջանը միտումնավոր նվազագույն է՝ բաղկացած միայն հիշողության մեջ պահվող աշխատաշրջանից՝ փաթեթների փոխանցման մասին տեղեկացնելու համար, ինչպես նաև մշտադիտարկման համար անհրաժեշտ հաշվիչներից և ժամկետի լրացման ու մաքրման համար նախատեսված ժամանակաչափերից։ Այս դիզայնի ընտրությունը ապահովում է փաթեթների երթուղավորումը անմիջապես դրանց անցման ուղու վրա։ Եթե ռելեյը վերագործարկվում է և կորցնում աշխատաշրջանը, հաջորդ STUN փաթեթը վերակառուցում է աշխատաշրջանը՝ ufrag երթուղավորման հուշման հիման վրա։ Այն էլ ավելի հուսալի դարձնելու համար օգտագործվում է Redis քեշ՝ երթուղու հաստատումից հետո <հաճախորդի IP + պորտ, տրանսիվերի IP + պորտ> համապատասխանեցումը պահելու նպատակով, որպեսզի այն հնարավոր լինի վերականգնել շատ ավելի վաղ՝ նախքան հաջորդ STUN փաթեթի ժամանումը։
Երբ մենք հանրային UDP մակերեսը կրճատեցինք փոքր քանակի կայուն հասցեների և պորտերի, մենք կարողացանք նույն ռելեի օրինաչափությունը կիրառել ամբողջ աշխարհում։ Global Relay-ը մեր աշխարհագրորեն բաշխված ռելեային մուտքային կետերի հավաքակազմն է, որոնք բոլորը իրականացնում են փաթեթների փոխանցման նույն սկզբունքը։
Լայն աշխարհագրական մուտքային կետերը կրճատում են հաճախորդից դեպի OpenAI առաջին անցումը, քանի որ փաթեթը կարող է մուտք գործել մեր ցանց՝ օգտատիրոջը մոտ գտնվող ռելեում՝ թե՛ աշխարհագրական, թե՛ ցանցային տոպոլոգիայի տեսանկյունից, փոխարենը նախ հանրային ինտերնետով անցնելու դեպի հեռավոր տարածաշրջան։ Գործնական առումով դա նշանակում է ավելի ցածր ուշացում, ավելի քիչ ցնցումներ և ավելի քիչ կանխելի կորուստների պոռթկումներ՝ մինչև տրաֆիկը հասնի մեր մայրուղային ցանցին։6
Մենք օգտագործում ենք Cloudflare-ի աշխարհագրական և մոտակայության հիման վրա ուղղորդումը սիգնալացման համար, որպեսզի սկզբնական HTTP կամ WebSocket հարցումը հասնի մոտակա տրանսիվերի կլաստերին։ Հարցման համատեքստը սահմանում է աշխատաշրջանի տեղակայումը և այն, թե Global Relay-ի որ մուտքի կետն է ներկայացվում հաճախորդին։ SDP պատասխանը տրամադրում է Global Relay-ի հասցեն, մինչդեռ ufrag-ը պարունակում է բավարար տեղեկատվություն, որպեսզի Global Relay-ը մեդիան ուղղորդի դեպի նշանակված կլաստեր, իսկ ռելեն՝ դեպի նպատակակետ տրանսիվեր։
Միասին աշխարհագրական ուղղորդմամբ ազդանշանակումն ու Global Relay-ը կարգավորումն ու մեդիան տեղադրում են մոտակա մուտքային ուղու վրա՝ միաժամանակ սեսիան խարսխված պահելով մեկ հաղորդընդունիչի վրա։ Դա նվազեցնում է ազդանշանավորման և առաջին ICE կապակցելիության ստուգման երկկողմանի փոխանցման ժամանակը, ինչը ուղղակիորեն կրճատում է այն ժամանակը, որի ընթացքում օգտատերը սպասում է խոսքի մեկնարկին։
Մենք փոխանցման ծառայությունը գրել ենք Go ծրագրավորման լեզվով և միտումնավոր սահմանափակել ենք իրականացումը։ Linux-ում միջուկի ցանցային ստեկը մեքենայի ցանցային ինտերֆեյսից ստանում է UDP փաթեթներ և դրանք փոխանցում է սոկետին՝ օպերացիոն համակարգի վերջնակետին, որից գործընթացը կարդում է՝ կապակցվելով IP:Port-ին։ Relay-ն աշխատում է userspace-ում, ուստի սովորական Go պրոցեսը կարդում է փաթեթների վերնագրերը այդ սոկետից, թարմացնում է հոսքի վիճակի փոքր մասը և փոխանցում է փաթեթները՝ առանց WebRTC-ն տերմինացնելու։ Մեզ անհրաժեշտ չէր միջուկը շրջանցող որևէ շրջանակ, որը թույլ կտար օգտատիրոջ տարածքի գործընթացին ուղղակիորեն հարցումներ կատարել ցանցային հերթերին՝ փաթեթների ավելի բարձր հաճախականության հասնելու համար, բայց նաև կավելացներ գործառնական բարդությունը։
Նախագծման հիմնական ընտրությունները՝
- Պրոտոկոլի ավարտ չկա․ ռելեյը վերլուծում է միայն STUN վերնագրերը/ufrag-ը․ այն օգտագործում է քեշավորված վիճակը հետագա DTLS-ի, RTP-ի և RTCP-ի համար՝ փաթեթները անթափանց պահելով։
- Անցողիկ վիճակ. Այն պահպանում է փոքր, կարճ timeout-ով, օպերատիվ հիշողության մեջ գտնվող քարտեզ՝ հաճախորդի հասցեն համապատասխանեցնելու համար ընդունիչ-հաղորդիչի նպատակակետին՝ հոսքի վիճակի և դիտարկելիության ապահովման նպատակով։
- Հորիզոնական մասշտաբավորում. Մի քանի ռելեյային ինստանսներ զուգահեռաբար աշխատում են բեռի բաշխիչի հետևում։ Վիճակը WebRTC-ի կոշտ վիճակ չէ, ուստի վերագործարկումները հանգեցնում են թրաֆիկի նվազագույն կորուստների և հոսքի արագ վերականգնման։
Արդյունավետության միջոցառումներ՝
SO_REUSEPORT-ը Linux-ի սոկետի ընտրանք է, որը թույլ է տալիս մի քանի փոխանցման աշխատողների նույն մեքենայի վրա կապել նույն UDP պորտը։ Այնուհետև միջուկը մուտքային փաթեթները բաշխում է այդ աշխատողների միջև՝ խուսափելով մեկ ընթերցման ցիկլի խոչընդոտից։runtime.LockOSThread-ը յուրաքանչյուր UDP կարդացող goroutine ամրակցում է ՕՀ-ի որոշակի հոսքին։SO_REUSEPORT-ի հետ համակցված՝ այն սովորաբար պահում է նույն հոսքի փաթեթները (սկզբնաղբյուրի և նպատակակետի IP:Port-երը՝ գումարած արձանագրությունը) պրոցեսորի նույն միջուկում՝ բարելավելով քեշի տեղայնությունը և նվազեցնելով կոնտեքստի փոխարկումները։- Նախապես հատկացված բուֆերներն ու նվազագույն պատճենումը նվազեցնում են վերլուծման և հատկացման ծախսերը՝ խուսափելով Go-ում աղբի հավաքագրումից։
Այս իրականացումը սպասարկեց մեր գլոբալ իրական ժամանակի մեդիա տրաֆիկը՝ փոխանցիչների համեմատաբար փոքր ենթակառուցվածքով, ուստի մենք պահպանեցինք ավելի պարզ դիզայնը՝ միջուկը շրջանցելու ուղին ընտրելու փոխարեն։
Այս ճարտարապետությունը թույլ է տալիս մեզ գործարկել WebRTC մեդիան Kubernetes-ում՝ առանց հազարավոր UDP պորտեր արտաքին հասանելի դարձնելու։ Դա կարևոր է, քանի որ ավելի փոքր և հաստատուն UDP մակերեսը ավելի հեշտ է անվտանգացնել և բեռը հավասարակշռել, և այն թույլ է տալիս ենթակառուցվածքին մասշտաբավորվել՝ առանց հանրային պորտերի մեծ միջակայքներ ամրագրելու։ Kubernetes-ի կողմից ավելի լավ ենթակառուցվածքային աջակցությամբ և ավելի փոքր հարձակման մակերեսի շնորհիվ բարձրացված անվտանգությամբ՝ այս դիզայնը նաև պահպանում է ստանդարտ WebRTC վարքագիծը հաճախորդների համար և հաստատում, որ առանց SFU-ի դիզայնը ճիշտ կանխադրված ընտրություն էր մեր աշխատանքային ծանրաբեռնվածության համար։ Մեր սեսիաների մեծ մասը կետից կետ են, հապաղման նկատմամբ զգայուն և ավելի հեշտ են մասշտաբավորվում, երբ ինֆերենսի ծառայությունները ստիպված չեն գործել որպես WebRTC-ի հավասարազոր հանգույցներ։
Ավելի լայն դասը այն է, որ բարդություն ավելացնելու լավագույն տեղը թեթև ուղղորդման շերտն է, ոչ թե յուրաքանչյուր backend ծառայությունում, և ոչ էլ հատուկ client-ի վարքագծում։ Երթուղավորման մետատվյալները արձանագրությանը բնորոշ դաշտում կոդավորելը մեզ ապահովեց դետերմինիստական առաջին փաթեթի երթուղավորմամբ, փոքր հանրային UDP հետքով և բավարար ճկունությամբ՝ մուտքային հանգույցները աշխարհի տարբեր մասերում օգտատերերին մոտ տեղակայելու համար։
Մի քանի ընտրություն հատկապես կարևոր էին․
- Պահպանեք պրոտոկոլի իմաստաբանությունը եզրային մակարդակում։ Կլիենտները դեռ օգտագործում են ստանդարտ WebRTC արձանագրությունը, ինչը պահպանում է դիտարկիչների և շարժական սարքերի փոխգործունակությունը անփոփոխ։
- Պահեք նիստի հիմնական վիճակները մեկ վայրում։ Ընդունիչ-հաղորդիչը կառավարում է ICE-ը, DTLS-ը, SRTP-ը և նիստի կյանքի ցիկլը։ Ռելեն միայն փոխանցում է փաթեթները։
- Երթուղավորեք կարգավորման մեջ առկա տեղեկատվության հիման վրա։ ICE ufrag-ը մեզ տրամադրեց առաջին փաթեթի երթուղավորման կցման կետ՝ առանց կրիտիկական ուղու որոնման կախվածություն ավելացնելու։
- Հաճախ հանդիպող դեպքերի համար օպտիմալացրեք՝ նախքան միջուկը շրջանցելու մեթոդին դիմելը։ Նեղ շրջանակով Go-ի իրականացումը՝
SO_REUSEPORT-ի զգուշավոր կիրառմամբ, հոսքերի ամրակցմամբ և քիչ հատկացումներ պահանջող վերլուծմամբ, բավարար էր մեր աշխատանքային բեռի համար։
Իրական ժամանակի ձայնային AI-ն աշխատում է միայն այն դեպքում, երբ ենթակառուցվածքը հապաղումը աննկատելի է դարձնում։ Մեզ համար դա նշանակում էր փոխել WebRTC-ի մեր տեղակայման կառուցվածքը՝ առանց փոխելու այն, ինչ հաճախորդներն ակնկալում են հենց WebRTC-ից։
Հեղինակ
Հղումներ
2. GitHub - l7mp/stunner: WebRTC-ի համար նախատեսված Kubernetes մեդիա դարպաս(բացվում է նոր պատուհանում)
3. WebRTC պորտերը համառոտ [Օրինակներ] - BlogGeek.me(բացվում է նոր պատուհանում)
4. Տեղակայել Kubernetes-ում - LiveKit-ի փաստաթղթեր(բացվում է նոր պատուհանում)
6. Cloudflare Calls: միլիոնավոր կասկադային ծառեր՝ մինչև ամենաներքև(բացվում է նոր պատուհանում)


