Scalarea aplicației dvs. web: pași de bază

Publicat: 2022-09-13

Nu este suficient să creezi aplicații pentru afacerea ta, trebuie să le optimizezi. O modalitate eficientă este scalarea. În acest articol, veți afla despre optimizarea codului, optimizarea arhitecturii și cum să construiți aplicații web scalabile în general.

Optimizarea

Gearheart sugerează să vă puneți următoarele întrebări:

  • interogările bazei de date sunt optime (analiza EXPLAIN, utilizarea indecșilor)?
  • datele sunt stocate corect (SQL vs NoSQL)?
  • este folosită cache?
  • fără solicitări inutile către FS sau baza de date?
  • sunt algoritmii de procesare a datelor optimi?
  • sunt setările de mediu optime: Apache/Nginx, MySQL/PostgreSQL, PHP/Python?

Fiecare dintre aceste întrebări ar putea fi acoperită într-un articol separat, astfel încât o analiză detaliată a acestora în cadrul acestui articol este în mod evident excesivă. Este important să înțelegeți că, înainte de a începe să scalați o aplicație, este foarte de dorit să optimizați activitatea acesteia cât mai mult posibil - de fapt, este posibil ca atunci să nu fie necesară nicio scalare.

Scalare

Să presupunem că ați optimizat deja aplicația, dar încă nu este capabilă să gestioneze încărcarea. În acest caz, soluția evidentă este distribuirea aplicației pe mai multe gazde pentru a crește performanța generală a aplicației prin creșterea resurselor disponibile. Această abordare se numește oficial „scalarea” aplicației. Mai exact, scalabilitatea este capacitatea unui sistem de a-și crește performanța prin creșterea cantității de resurse disponibile.

Există două tipuri de scalabilitate: verticală și orizontală. Scalabilitatea verticală presupune creșterea performanței aplicației prin adăugarea de resurse (CPU, memorie, disc) într-un singur nod (gazdă). Scalare orizontală este tipică pentru aplicațiile distribuite și implică creșterea performanței aplicației prin adăugarea unui alt nod.

Este clar că cel mai simplu mod este o simplă actualizare hardware (procesor, memorie, disc) – adică scalarea verticală. În plus, această abordare nu necesită modificări ale aplicației. Cu toate acestea, scalarea verticală își atinge rapid limita, după care dezvoltatorul și administratorul nu au de ales decât să treacă la scalarea orizontală a aplicației.

Arhitectura aplicației

Majoritatea aplicațiilor web sunt distribuite a priori, deoarece arhitectura lor poate fi împărțită în cel puțin trei straturi: web-server, logica de afaceri (aplicație), date (bază de date, statică).

Fiecare dintre aceste straturi poate fi scalat. Deci, dacă sistemul dvs. are o aplicație și o bază de date care rezidă pe aceeași gazdă, primul pas ar trebui să fie cu siguranță să le separați pe gazde diferite.

Blocajul

Trecând la scalarea sistemului, primul lucru de făcut este să determinați care dintre straturi este „gâtul de sticlă”, adică mai lent decât restul sistemului. Pentru început, puteți folosi utilitare banale precum top (htop) pentru a evalua consumul CPU/memorie și df, iostat pentru a evalua consumul de disc. Cu toate acestea, este de dorit să oferiți o gazdă separată cu o emulare a încărcăturii de luptă (folosind AB sau JMeter), pe care să puteți profila aplicația folosind utilitare precum xdebug, oprofile și așa mai departe. Puteți utiliza utilitare precum pgFouine pentru a identifica interogări restrânse ale bazei de date (desigur, este mai bine să o faceți pe baza jurnalelor de pe serverul de luptă).

De obicei depinde de arhitectura aplicației, dar în general cei mai probabili candidați pentru un blocaj sunt baza de date și codul. Dacă aplicația dvs. gestionează o mulțime de date de utilizator, blocajul este probabil să fie stocarea statică.

Scalare baze de date

După cum am menționat mai sus, blocajul în aplicațiile moderne este adesea baza de date. Problemele cu acesta sunt de obicei împărțite în două clase: performanță și necesitatea de a stoca o cantitate mare de date.

Puteți reduce încărcarea bazei de date împărțind-o în mai multe gazde. Există o dificultate acută de sincronizare între ele, care poate fi rezolvată prin implementarea schemei master/slave cu replicare sincronă sau asincronă. Pentru PostgreSQL, puteți utiliza Slony-I pentru replicarea sincronă și PgPool-II sau WAL (9.0) pentru replicarea asincronă. Pentru a rezolva problema împărțirii cererilor de citire și scriere, precum și pentru a echilibra încărcarea dintre slave, puteți configura un nivel special de acces la baza de date (PgPool-II).

Preocuparea stocării unor cantități mari de date în cazul bazelor de date relaționale poate fi rezolvată prin partiționare („partiționare” în PostgreSQL) sau prin implementarea bazei de date pe o bază de date distribuită precum Hadoop DFS.

Puteți citi despre ambele soluții în cartea excelentă despre configurarea PostgreSQL.

1.Totuși, pentru stocarea unor cantități mari de date, cea mai bună soluție este shardingul, care este un avantaj inerent al majorității bazelor de date NoSQL (de exemplu, MongoDB).

2. Mai mult, bazele de date NoSQL în general funcționează mai repede decât frații lor SQL din cauza lipsei de supraîncărcare pentru analiza/optimizarea interogării, verificarea integrității structurii datelor etc. Subiectul comparării bazelor de date relaționale și NoSQL este, de asemenea, destul de extins și merită un articol separat.

3. Separat, merită remarcată experiența Facebook, care folosește MySQL fără selecții JOIN. Această strategie le permite să scaleze mult mai ușor baza de date, transferând în același timp încărcarea din baza de date în cod, care, așa cum va fi descris mai jos, se scalează mai ușor decât baza de date.

Scalare de cod

  • Complexitățile de scalare a codului depind de câte resurse partajate au nevoie gazdele dvs. pentru a rula aplicația dvs. Vor fi doar sesiuni sau va trebui să partajați cache-urile și fișierele? Oricum, primul lucru de făcut este să rulați copii ale aplicației pe mai multe gazde cu același mediu.
  • Apoi, trebuie să configurați echilibrarea încărcării/cererilor între aceste gazde. Puteți face acest lucru atât pe TCP (HAProxy), HTTP (nginx) sau DNS.
  • Următorul pas, a menționat Gearheart, este de a face fișierele statice, memoria cache și sesiunile de aplicații web disponibile pe fiecare gazdă. Pentru sesiuni, puteți utiliza un server care funcționează în rețea (de exemplu, Memcached). Ca server cache, este logic să utilizați același Memcached, dar pe o gazdă diferită, desigur.
  • Fișierele statice pot fi montate dintr-un spațiu de stocare de fișiere partajat prin NFS/CIFS sau folosind FS distribuit (HDFS, GlusterFS, Ceph).

De asemenea, este posibilă stocarea fișierelor într-o bază de date (de exemplu, Mongo GridFS), rezolvând astfel problema disponibilității și scalabilității (ținând cont că pentru baza de date NoSQL problema de scalabilitate se rezolvă prin sharding).

Separat, de remarcat, problema implementării pe mai multe gazde. Cum să vă asigurați că utilizatorul făcând clic pe „Actualizare” nu vede diferite versiuni ale aplicației? Cea mai simplă soluție, după părerea mea, ar fi să excludem din config load balancer (server web) gazdele care nu sunt actualizate și să le pornești secvenţial pe măsură ce se fac actualizări. De asemenea, puteți lega utilizatorii de anumite gazde prin cookie sau IP. Dacă actualizarea necesită modificări semnificative ale bazei de date, cel mai simplu mod este închiderea temporară a proiectului.