Modernizarea software-ului moștenit: programare MUD folosind Erlang și CloudI
Publicat: 2022-03-11Ce este Modernizarea Legacy?
Codul moștenit este peste tot. Și pe măsură ce ritmul de proliferare a codului continuă să crească exponențial, tot mai mult cod este retrogradat la statutul moștenit. În multe organizații mari, întreținerea sistemelor vechi consumă mai mult de 90% din resursele sistemelor informaționale.
Necesitatea de a moderniza codul și sistemele vechi pentru a satisface cerințele actuale de performanță și procesare este larg răspândită. Această postare oferă un studiu de caz al utilizării limbajului de programare Erlang și arhitectura orientată pe servicii CloudI (SOA) bazată pe Erlang, pentru a adapta codul moștenit – în special, o colecție veche de zeci de ani de cod sursă C – la secolul XXI. .
Uciderea Dragonului Codului Sursă
Cu ani în urmă, eram un mare fan al jocurilor online multiplayer bazate pe text, cunoscute sub numele de Temnite Multi-User (MUD). Dar au fost mereu plini de probleme de performanță. Am decis să mă arunc înapoi într-o grămadă de zeci de ani de cod sursă C și să văd cum am putea moderniza acest cod moștenit și să împingem aceste jocuri online timpurii la limite. La un nivel înalt, acest proiect a fost un exemplu excelent de utilizare a Erlang pentru a adapta software-ul moștenit pentru a îndeplini cerințele secolului 21.
Un scurt rezumat:
- Scopul : Luați un vechi joc video MUD pentru 50 de jucători și împingeți codul sursă pentru a accepta mii și mii de conexiuni simultane.
- Problema : Legacy, cod sursă C cu un singur thread.
- Soluția : CloudI, un serviciu bazat pe Erlang care oferă toleranță la erori și scalabilitate.
Ce este un MUD bazat pe text?
Toate jocurile de rol online masiv multiplayer (MMORPG) – cum ar fi World of Warcraft și EverQuest – au dezvoltat funcții ale căror origini timpurii pot fi urmărite până la jocurile online multiplayer mai vechi, bazate pe text, cunoscute sub numele de Dungeons Multi-User (MUD).
Primul MUD a fost Essex MUD (sau MUD1) al lui Roy Trubshaw, care a fost dezvoltat inițial în 1978 folosind limbajul de asamblare MARO-10 pe un DEC PDP-10, dar a fost convertit la BCPL, un predecesor al limbajului de programare C (și rula până 1987). (După cum puteți vedea, aceste lucruri sunt mai vechi decât majoritatea programatorilor.)
MUD-urile au câștigat treptat popularitate la sfârșitul anilor 1980 și începutul anilor 1990, cu diferite baze de coduri MUD scrise în C. Baza de coduri DikuMUD, de exemplu, este cunoscută ca rădăcina unuia dintre cei mai mari arbori de cod sursă MUD derivat, cu cel puțin 51 de variante unice toate. bazat pe același cod sursă DikuMUD. (În acest interval de timp, de altfel, MUD-urile au devenit cunoscute alternativ sub numele de „Distrugătorul Multi-Licență” din cauza numărului de studenți care au eșuat din școală din cauza obsesiei lor pentru ei.)
Problema cu MUD-urile moștenite
Codul sursă istoric C MUD (inclusiv DikuMUD și variantele sale) este plin de probleme de performanță din cauza limitărilor existente la momentul creării.
Lipsa filetului
Pe atunci, nu exista o bibliotecă de threading ușor accesibilă. Mai mult, threading-ul ar fi făcut codul sursă mai dificil de întreținut și modificat. Drept urmare, aceste MUD-uri erau toate cu un singur fir.
În timpul unei singure „bifări” (o creștere a ceasului intern care urmărește progresul tuturor evenimentelor de joc), codul sursă MUD trebuie să proceseze fiecare eveniment de joc pentru fiecare priză conectată. Cu alte cuvinte: fiecare bucată de cod încetinește procesarea unei singure căpușe. Și dacă orice calcul forțează procesarea să se întindă mai mult decât un singur tick, MUD-ul rămâne în întârziere, impactând fiecare jucător conectat.
Cu acest decalaj, jocul devine imediat mai puțin captivant. Jucătorii privesc neputincioși când personajele lor mor, propriile lor comenzi rămânând neprocesate.
Vă prezentăm SillyMUD
În scopul acestui experiment de modernizare a aplicațiilor moștenite, am ales SillyMUD, un derivat istoric al DikuMUD care a influențat MMORPG-urile moderne și problemele de performanță pe care le împărtășesc. În anii 1990, am jucat un MUD care a fost derivat din baza de cod SillyMUD, așa că știam că codul sursă va fi un punct de plecare interesant și oarecum familiar.
Ce am moștenit?
Codul sursă SillyMUD este similar cu cel al altor MUD-uri C istorice, prin aceea că este limitat la aproximativ 50 de jucători concurenți (64, mai exact, pe baza codului sursă).
Cu toate acestea, am observat că codul sursă a fost modificat din motive de performanță (adică, pentru a împinge limitarea playerului concurent). Specific:
- Codului sursă lipsea o căutare a numelui de domeniu pe adresa IP a conexiunii, absentă din cauza latenței impuse de o căutare a numelui de domeniu (în mod normal, un MUD mai vechi dorește o căutare a numelui de domeniu pentru a facilita interzicerea utilizatorilor rău intenționați).
- Codul sursă a avut comanda „donate” dezactivată (un pic neobișnuit) din cauza posibilei creări de liste lungi de articole donate care au necesitat apoi traversări de liste cu procesare intensivă. Acestea, la rândul lor, au afectat performanța jocului pentru toți ceilalți jucători (cu un singur thread, vă amintiți?).
Vă prezentăm CloudI
CloudI a fost discutat anterior ca o soluție pentru dezvoltarea poliglotă datorită toleranței la erori și scalabilității pe care le oferă.
CloudI oferă o abstractizare a serviciului (pentru a oferi o arhitectură orientată pe servicii (SOA)) în Erlang, C/C++, Java, Python și Ruby, păstrând în același timp defecțiunile software izolate în cadrul CloudI. Toleranța la erori este furnizată prin implementarea Erlang de la CloudI, bazându-se pe caracteristicile Erlang tolerante la erori și pe implementarea modelului actorului. Această toleranță la erori este o caracteristică cheie a implementării Erlang de la CloudI, deoarece toate programele conțin erori.
CloudI oferă, de asemenea, un server de aplicații pentru a controla durata de viață a execuției serviciului și crearea proceselor de serviciu (fie ca procese ale sistemului de operare pentru limbaje de programare non-Erlang, fie ca procese Erlang pentru serviciile implementate în Erlang), astfel încât execuția serviciului să aibă loc fără a afecta starea externă. fiabilitate. Pentru mai multe, vedeți postarea mea anterioară.

Cum pot moderniza CloudI un MUD vechi bazat pe text?
Codul sursă istoric C MUD oferă o oportunitate interesantă pentru integrarea CloudI, având în vedere problemele sale de fiabilitate:
- Stabilitatea serverului de joc are un impact direct asupra atractivității oricărei mecanisme de joc.
- Concentrarea dezvoltării software pe remedierea erorilor de stabilitate a serverului limitează dimensiunea și domeniul de aplicare al jocului rezultat.
Odată cu integrarea CloudI, erorile de stabilitate a serverului pot fi încă remediate în mod normal, dar impactul lor este limitat, astfel încât funcționarea serverului de joc nu este întotdeauna afectată atunci când o eroare nedescoperită anterior determină defectarea unui sistem intern de joc. Acesta oferă un exemplu excelent de utilizare a Erlang pentru a impune toleranța la erori într-o bază de cod moștenită.
Ce modificări au fost necesare?
Baza de cod originală a fost scrisă pentru a fi atât cu un singur thread, cât și foarte dependentă de variabilele globale. Scopul meu a fost să păstrez funcționalitatea moștenită a codului sursă în timp ce o modernizez pentru utilizarea în prezent.
Cu CloudI, am reușit să păstrez codul sursă cu un singur thread, oferind în continuare scalabilitate a conexiunii socket.
Să revizuim modificările necesare:
Ieșire de consolă
Salvarea în tampon a ieșirii consolei SillyMUD (un afișaj terminal, adesea conectat la Telnet) era deja în vigoare, dar o anumită utilizare a descriptorilor de fișier direct a necesitat tamponare (astfel încât ieșirea consolei să devină răspunsul la o solicitare de serviciu CloudI).
Manevrarea prizei
Gestionarea socket-urilor în codul sursă original se baza pe un apel de funcție select()
pentru a detecta intrarea, erorile și șansa de ieșire, precum și pentru a întrerupe un joc de 250 de milisecunde înainte de a gestiona evenimentele de joc în așteptare.
Integrarea CloudI SillyMUD se bazează pe solicitările de serviciu primite pentru intrare în timp ce se întrerupe cu funcția cloudi_poll a API-ului C cloudi_poll
(pentru cele 250 de milisecunde înainte de a gestiona aceleași evenimente de joc în așteptare). Codul sursă SillyMUD a rulat cu ușurință în CloudI ca serviciu CloudI după ce a fost integrat cu API-ul C CloudI (deși CloudI oferă atât API-uri C, cât și C++, folosind API-ul C a facilitat mai bine integrarea cu codul sursă C al SillyMUD).
Abonamente
Integrarea CloudI se abonează la trei modele principale de nume de serviciu pentru a gestiona evenimentele de conectare, deconectare și joc. Aceste modele de nume provin de la apelarea C CloudI API abonare în codul sursă de integrare. În consecință, fie conexiunile WebSocket, fie conexiunile Telnet au destinații de nume de serviciu pentru trimiterea cererilor de servicii atunci când sunt stabilite conexiunile.
Suportul WebSocket și Telnet în CloudI este furnizat de serviciile interne CloudI ( cloudi_service_http_cowboy
pentru suport WebSocket și cloudi_service_tcp
pentru suport Telnet). Deoarece serviciile interne CloudI sunt scrise în Erlang, ele pot valorifica scalabilitatea extremă a lui Erlang, folosind în același timp abstracția serviciului CloudI care oferă funcțiile API CloudI.
Mergand inainte
Evitând manipularea socket-ului, a avut loc mai puțină procesare în cazul erorilor de socket sau în situații precum moartea link-ului (în care utilizatorii sunt deconectați de la server). Astfel, eliminarea gestionării socket-urilor de nivel scăzut a abordat problema principală de scalabilitate.
Dar problemele de scalabilitate rămân. De exemplu, MUD utilizează sistemul de fișiere ca bază de date locală atât pentru elementele de joc statice, cât și pentru cele dinamice (adică, jucătorii și progresul lor, împreună cu zonele lumii, obiectele și monștrii). Refactorizarea codului moștenit al MUD pentru a se baza în schimb pe un serviciu CloudI pentru o bază de date ar oferi mai multă toleranță la erori. Dacă am folosi o bază de date mai degrabă decât un sistem de fișiere, mai multe procese de serviciu SillyMUD CloudI ar putea fi utilizate concomitent ca servere de joc separate, păstrând utilizatorii izolați de erorile de rulare și reducând timpul de nefuncționare.
Cât de mult s-a îmbunătățit MUD-ul?
Au existat trei domenii principale de îmbunătățire:
- Toleranță la erori. Odată cu integrarea modernizată a serviciului SillyMUD CloudI, izolarea erorilor de socket și a latenței din codul sursă SillyMUD oferă un grad de toleranță la erori.
- Scalabilitatea conexiunii. Cu utilizarea serviciilor interne CloudI, limitarea utilizatorilor concurenți SillyMUD poate trece cu ușurință de la 64 (istoric) la 16.384 de utilizatori (fără probleme de latență!) .
- Eficiență și performanță. Întrucât gestionarea conexiunii se face în CloudI în loc de codul sursă SillyMUD cu un singur thread, eficiența codului sursă de joc SillyMUD este îmbunătățită în mod natural și poate face față unei sarcini mai mari.
Așadar, cu o simplă integrare CloudI, numărul de conexiuni a crescut cu trei ordine de mărime, oferind în același timp toleranță la erori și creșterea eficienței aceluiași joc moștenit.
Imaginea de ansamblu
Erlang a oferit un timp de funcționare de 99,9999999% (mai puțin de 31,536 milisecunde de oprire pe an) pentru sistemele de producție. Cu CloudI, aducem aceeași fiabilitate altor limbaje și sisteme de programare.
Dincolo de a demonstra viabilitatea acestei abordări pentru îmbunătățirea codului sursă a serverului de jocuri moștenit (SillyMUD a fost modificat ultima dată cu peste 20 de ani în urmă, în 1993!), acest proiect demonstrează la un nivel mai larg modul în care Erlang și CloudI pot fi valorificate pentru a moderniza aplicațiile moștenite și pentru a furniza erori. -toleranță, performanță îmbunătățită și disponibilitate ridicată în general. Aceste rezultate au un potențial promițător pentru adaptarea codului moștenit la secolul 21, fără a necesita o revizuire majoră a software-ului.