Dit is een artikel uit de Python-wereld, maar het is nog steeds toepasbaar op het hele programmeerveld, hoewel multithreading ons in staat stelt verzoeken sneller te verwerken, maar er is ook een plafond; groene (micro-thread) threads zijn de oplossing.
Multithreaded softwareontwikkeling lost een groot aantal problemen op, vooral voor netwerkgerichte applicaties die veeleisende prestaties vereisen om snel op gebruikers te reageren. Helaas is multithreading niet genoeg om grootschalige opvang op te lossenGelijktijdigheidseksuele problemen.
Het aanpakken van deze problemen vereist het veranderen van programmeermodellen, met gebruik van asynchrone gebeurtenissen en callback-gebaseerde mechanismen. Bij Druva hebben we een python-gebaseerde bibliotheek ontwikkeld genaamd Dhaga om grootschalige oplossingen op te lossenGelijktijdigheid, terwijl het programmeermodel geen significante wijzigingen vereist.
Softwareontwikkelaars leven in éénGelijktijdigheidwereld. Threads zijn tegenwoordig eersteklas burgers, vooral tijdens de ontwikkeling, vooral wanneer je applicatie intensieve netwerkoperaties uitvoert, zoals het inSync-systeem (netwerkbeveiligingssynchronisatieproduct) zoals Druva. Multithreading helpt de vloei van programmeercode voor netwerkoperaties eenvoudig en ordelijk. Wanneer onze applicatie prestatieverbeteringen of -verbeteringen nodig heeft, kan dat ook worden verbeterdElasticiteit, kunnen we het aantal draden verhogen.
Maar als het gaat om duizenden schalenGelijktijdigheidVerzoeken, discussies zijn niet genoeg.
We ontdekten dat multithreading de volgende nadelen heeft: 1. De inSync-systeemclient moet een groot aantal bestanden back-uppen naar de server via netwerk-RPC-aanroepen. Een typische manier voor ontwikkelaars om het proces te versnellen is het gebruik van threads. De prestaties die multithreading oplevert verhogen echter de kosten van geheugen en CPU; Ontwikkelaars moeten een balans bewaren tussen snelheid en aantal threads.
2. Onze servers moeten omgaan tussen het inSync-systeem en duizenden klantenGelijktijdigheidVerbindingen en meldingen. Om verbindingen efficiënt af te handelen, gebruiken we threads om verzoeken af te handelen. Maar het groeiende aantal inSync-systeemklanten betekent ook dat we het aantal threads moeten blijven verhogen, wat veel servergeheugen en CPU verbruikt.
3. Onze webserver moet duizenden parallelle HTTP-verzoeken afhandelen. Het meeste werk ligt aan de netwerksockets die data ontvangen en verzenden en doorgeven aan de backend van het inSync-systeem. Dit zorgt ervoor dat de meeste threads wachten op netwerkoperaties. Het C10K-probleem veroorzaakt dat wanneer er duizenden synchrone verzoeken naar de webserver zijn, het genereren van een thread voor elk verzoek vrij niet-schaalbaar is (Scale).
Beperkingen van asynchrone frameworks Veel asynchrone frameworks, waaronder Twisted, Tornado Tornado en asyncore, kunnen ontwikkelaars helpen om af te stappen van de populaire manieren van threads. Deze frameworks vertrouwen op niet-blokkerende sockets en callback-mechanismen (vergelijkbaar met Node.js). Als we deze frameworks zo gebruiken, zullen de belangrijkste onderdelen van onze Druva-code moeten worden gerefactored. Dat is niet wat we willen doen. Het refactoren van code verhoogt de ontwikkelings- en testcycli, waardoor we niet aan onze schaalvereisten kunnen voldoen. Aangezien meerdere delen van het product enorm moeten zijn, zal ieder van ons ze moeten refactoren – vandaar de moeite om te verdubbelen of te verdrievoudigen.
Om te voorkomen dat we te veel code zouden veranderen, moesten we afstappen van het directe gebruik van het bestaande framework. Gelukkig vonden we wat nuttige hulpmiddelen.
Omdat we de uitvoering van code op de netwerk-I/O willen controleren, hebben we een manier nodig om een thread op te delen in micro-threads. We vindenGreenlets。 Het biedt een niet-impliciete microthread-scheduling, genaamd co-routine coroutine. Met andere woorden. Het is handig als je wilt controleren hoe je code draait. Je kunt microthreads bouwen voor aangepaste schema's omdat je kunt bepalen wanneer greenlets pauzes geven. Dit is perfect voor ons omdat het ons volledige controle geeft over de planning van onze code.
Tornado is een eenvoudig, niet-blokkerend webserverframework geschreven in Python, ontworpen om duizenden asynchrone verzoeken af te handelen. We gebruiken de kerncomponent, IOLoop IOStream. IOLoop is een niet-blokkerende socket I/O-gebeurtenislus; Het gebruikt epoll (op Linux) of wachtrijen (BSD en Mac OS X), anders selecteert (op Windows) als deze beschikbaar zijn. IOStream biedt niet-blokkerende sockets, zoals een handige verpakking voor lezen en schrijven. We delegeren alle socket-operaties aan Tornado en gebruiken vervolgens callbacks om codebewerkingen te laten uitvoeren (banq-opmerking: zeer vergelijkbaar met Node.js mechanisme).
Het is een goed begin, maar we hebben meer nodig. Als we bovenstaande module direct in onze code gebruiken, zal veel van onze RPC-code moeten veranderen, RPC via greenlets inplannen, ervoor zorgen dat greenlets niet blokkeren (als greenlets verstopt raken, raakt de hele thread en alle andere verstopt), en callback-functies van de tornado worden afgewerkt.
We hebben een abstractie nodig om greenlets te beheren en te ordenen zodat ze niet verstopt raken met externe aanroepen, en deze abstractie kan enorm schaalbaar zijn buiten threads. Deze abstractie is Dhaga, waarmee de applicatiecodestroom geprogrammeerd kan worden als een traditionele synchrone sequentie, maar de uitvoering asynchroon is.
Dhaga (van het Hindi, wat thread betekent) is een uitvoeringsframework voor een lichtgewicht thread dat we abstraheren. De Dhaga-klasse is afgeleid van greenlets en gebruikt stackswitching om meerdere codeflows in één besturingssysteemthread uit te voeren. Threads van één besturingssysteem voeren meerdere dhagases uit met behulp van collaboratieve planning. Telkens wanneer een dhaga wacht (voornamelijk wachtend op een terugkeer van een RPC-aanroep), geeft deze de controle over aan het ouderniveau (d.w.z. de uitvoeringscontext van de OS-niveau thread die het heeft gemaakt). Het ouderniveau plant dan een andere dhaga in om klaar te zijn om te starten. De RPC-aanroep wordt doorgegeven aan de tornado-webserver om de Socket asynchroon te schrijven, waarna een callback wordt geregistreerd wanneer deze terugkeert, en wanneer deze RPC terugkeert, wordt de wachtende dhaga toegevoegd aan de uitvoerbare wachtrij en vervolgens opgepikt door de ouderthread. (Banq-noot: vergelijkbaar met node.js principe)
We kunnen Dhaga gebruiken in plaats van threads voor operaties met hoge latentie, en we gebruiken 512 dhagas in één thread wanneer het aantal threads voorbij een redelijke limiet voor doorvoer toeneemt.
|