Test public: o soluție pentru confidențialitate și scalabilitate pe Ethereum

Blockchain este o tehnologie inovatoare care promite să îmbunătățească multe domenii ale vieții umane. Transferă procese și produse reale în spațiul digital, asigură viteza și fiabilitatea tranzacțiilor financiare, reduce costurile acestora și, de asemenea, vă permite să creați aplicații DAPP moderne folosind contracte inteligente în rețele descentralizate.

Având în vedere numeroasele beneficii și diversele aplicații ale blockchain-ului, poate părea surprinzător că această tehnologie promițătoare nu și-a făcut încă loc în fiecare industrie. Problema este că blockchain-urile moderne descentralizate le lipsește scalabilitatea. Ethereum procesează aproximativ 20 de tranzacții pe secundă, ceea ce nu este suficient pentru a satisface nevoile afacerilor dinamice de astăzi. În același timp, companiile care folosesc tehnologia blockchain ezită să abandoneze Ethereum datorită gradului său ridicat de protecție împotriva hackingului și a defecțiunilor rețelei.

Pentru a asigura descentralizarea, securitatea și scalabilitatea în blockchain, rezolvând astfel Trilema Scalabilitatii, echipa de dezvoltare Ocazie a creat Plasma Cash, un lanț subsidiar format dintr-un contract inteligent și o rețea privată bazată pe Node.js, care își transferă periodic starea la lanțul rădăcină (Ethereum).

Test public: o soluție pentru confidențialitate și scalabilitate pe Ethereum

Procese cheie în Plasma Cash

1. Utilizatorul numește funcția smart contract `deposit`, trecând în ea suma de ETH pe care dorește să o depună în jetonul Plasma Cash. Funcția de contract inteligent creează un token și generează un eveniment despre acesta.

2. Nodurile Plasma Cash abonate la evenimentele smart contract primesc un eveniment despre crearea unui depozit și adaugă o tranzacție despre crearea unui token în pool.

3. Periodic, nodurile speciale Plasma Cash preiau toate tranzacțiile din pool (până la 1 milion) și formează un bloc din ele, calculează arborele Merkle și, în consecință, hash-ul. Acest bloc este trimis la alte noduri pentru verificare. Nodurile verifică dacă hash-ul Merkle este valid și dacă tranzacțiile sunt valide (de exemplu, dacă expeditorul jetonului este proprietarul acestuia). După verificarea blocului, nodul apelează funcția `submitBlock` a contractului inteligent, care salvează numărul blocului și hashul Merkle în lanțul de margine. Contractul inteligent generează un eveniment care indică adăugarea cu succes a unui bloc. Tranzacțiile sunt eliminate din pool.

4. Nodurile care primesc evenimentul de trimitere a blocului încep să aplice tranzacțiile care au fost adăugate la bloc.

5. La un moment dat, proprietarul (sau non-proprietarul) jetonului dorește să îl retragă din Plasma Cash. Pentru a face acest lucru, apelează funcția `startExit`, trecând în ea informații despre ultimele 2 tranzacții de pe token, care confirmă că el este proprietarul token-ului. Contractul inteligent, folosind hash-ul Merkle, verifică prezența tranzacțiilor în blocuri și trimite jetonul pentru retragere, care va avea loc în două săptămâni.

6. Dacă operațiunea de retragere a jetonului a avut loc cu încălcări (jetonul a fost cheltuit după începerea procedurii de retragere sau jetonul era deja al altcuiva înainte de retragere), proprietarul jetonului poate respinge retragerea în termen de două săptămâni.

Test public: o soluție pentru confidențialitate și scalabilitate pe Ethereum

Confidențialitatea se realizează în două moduri

1. Lanțul rădăcină nu știe nimic despre tranzacțiile care sunt generate și transmise în cadrul lanțului copil. Informațiile despre cine a depus și a retras ETH din Plasma Cash rămân publice.

2. Lanțul copil permite tranzacții anonime folosind zk-SNARK-uri.

Stiva de tehnologie

  • NodeJS
  • Redis
  • Etherium
  • soild

Testarea

În timpul dezvoltării Plasma Cash, am testat viteza sistemului și am obținut următoarele rezultate:

  • până la 35 de tranzacții pe secundă sunt adăugate la pool;
  • într-un bloc pot fi stocate până la 1 de tranzacții.

Testele au fost efectuate pe următoarele 3 servere:

1. Intel Core i7-6700 Quad-Core Skylake incl. SSD NVMe – 512 GB, 64 GB RAM DDR4
Au fost ridicate 3 noduri de validare Plasma Cash.

2. AMD Ryzen 7 1700X Octa-Core „Summit Ridge” (Zen), SSD SATA – 500 GB, 64 GB RAM DDR4
Nodul Ropsten testnet ETH a fost ridicat.
Au fost ridicate 3 noduri de validare Plasma Cash.

3. Intel Core i9-9900K Octa-Core incl. SSD NVMe – 1 TB, 64 GB RAM DDR4
1 nod de trimitere Plasma Cash a fost ridicat.
Au fost ridicate 3 noduri de validare Plasma Cash.
A fost lansat un test pentru a adăuga tranzacții în rețeaua Plasma Cash.

Total: 10 noduri Plasma Cash într-o rețea privată.

Testul 1

Există o limită de 1 milion de tranzacții pe bloc. Prin urmare, 1 milion de tranzacții se încadrează în 2 blocuri (întrucât sistemul reușește să ia parte din tranzacții și să trimită în timp ce acestea sunt trimise).


Stare inițială: ultimul bloc #7; 1 milion de tranzacții și jetoane sunt stocate în baza de date.

00:00 — începerea scriptului de generare a tranzacției
01:37 - 1 milion de tranzacții au fost create și a început trimiterea către nod
01:46 — nodul de trimitere a luat 240 de tranzacții din grupul și blocul de formulare #8. De asemenea, vedem că 320 de tranzacții sunt adăugate la pool în 10 secunde
01:58 — blocul #8 este semnat și trimis pentru validare
02:03 — blocul #8 este validat și funcția `submitBlock` a contractului inteligent este apelată cu hash-ul Merkle și numărul de bloc
02:10 — scriptul demonstrativ a terminat de funcționat, care a trimis 1 milion de tranzacții în 32 de secunde
02:33 - nodurile au început să primească informații că blocul #8 a fost adăugat la lanțul rădăcină și au început să efectueze 240k tranzacții
02:40 - 240 tranzacții au fost eliminate din grup, care sunt deja în blocul #8
02:56 — nodul de trimitere a preluat cele 760 de tranzacții rămase din grup și a început să calculeze hash-ul Merkle și blocul de semnare #9
03:20 - toate nodurile conțin 1 milion de tranzacții de 240 și jetoane
03:35 — blocul #9 este semnat și trimis pentru validare către alte noduri
03:41 - a apărut o eroare de rețea
04:40 — în așteptarea validării blocului #9 a expirat
04:54 — nodul de trimitere a preluat cele 760 de tranzacții rămase din grup și a început să calculeze hash-ul Merkle și blocul de semnare #9
05:32 — blocul #9 este semnat și trimis pentru validare către alte noduri
05:53 — blocul #9 este validat și trimis la lanțul rădăcină
06:17 - nodurile au început să primească informații că blocul #9 a fost adăugat la lanțul rădăcină și au început să efectueze 760k tranzacții
06:47 — grupul a șters tranzacțiile care se află în blocul #9
09:06 - toate nodurile conțin 2 milioane de tranzacții și jetoane

Testul 2

Există o limită de 350k per bloc. Ca rezultat, avem 3 blocuri.


Stare inițială: ultimul bloc #9; 2 milioane de tranzacții și jetoane sunt stocate în baza de date

00:00 — scriptul de generare a tranzacțiilor a fost deja lansat
00:44 - 1 milion de tranzacții au fost create și a început trimiterea către nod
00:56 — nodul de trimitere a luat 320 de tranzacții din grupul și blocul de formulare #10. De asemenea, vedem că 320 de tranzacții sunt adăugate la pool în 10 secunde
01:12 — blocul #10 este semnat și trimis la alte noduri pentru validare
01:18 — scriptul demonstrativ a terminat de funcționat, care a trimis 1 milion de tranzacții în 34 de secunde
01:20 — blocul #10 este validat și trimis la lanțul rădăcină
01:51 - toate nodurile au primit informații de la lanțul rădăcină care a fost adăugat blocul #10 și au început să aplice tranzacții de 320
02:01 - pool-ul a fost compensat pentru 320 tranzacții care au fost adăugate la blocul #10
02:15 — nodul de trimitere a luat 350 de tranzacții din grupul și blocul de formulare #11
02:34 — blocul #11 este semnat și trimis la alte noduri pentru validare
02:51 — blocul #11 este validat și trimis la lanțul rădăcină
02:55 — ultimul nod a finalizat tranzacții din blocul #10
10:59 — tranzacția cu trimiterea blocului #9 a durat foarte mult timp în lanțul rădăcină, dar a fost finalizată și toate nodurile au primit informații despre aceasta și au început să efectueze 350k tranzacții
11:05 - pool-ul a fost compensat pentru 320 tranzacții care au fost adăugate la blocul #11
12:10 - toate nodurile conțin 1 milion de tranzacții și jetoane de 670
12:17 — nodul de trimitere a luat 330 de tranzacții din grupul și blocul de formulare #12
12:32 — blocul #12 este semnat și trimis la alte noduri pentru validare
12:39 — blocul #12 este validat și trimis la lanțul rădăcină
13:44 - toate nodurile au primit informații de la lanțul rădăcină care a fost adăugat blocul #12 și au început să aplice tranzacții de 330
14:50 - toate nodurile conțin 2 milioane de tranzacții și jetoane

Testul 3

În primul și al doilea server, un nod de validare a fost înlocuit cu un nod de trimitere.


Stare inițială: ultimul bloc #84; 0 tranzacții și jetoane salvate în baza de date

00:00 — Au fost lansate 3 scripturi care generează și trimit 1 milion de tranzacții fiecare
01:38 — 1 milion de tranzacții au fost create și a început trimiterea către nodul #3
01:50 — nodul de trimitere #3 a preluat 330 de tranzacții din grupul și blocul de formulare #85 (f21). De asemenea, vedem că 350 de tranzacții sunt adăugate la pool în 10 secunde
01:53 — 1 milion de tranzacții au fost create și a început trimiterea către nodul #1
01:50 — nodul de trimitere #3 a preluat 330 de tranzacții din grupul și blocul de formulare #85 (f21). De asemenea, vedem că 350 de tranzacții sunt adăugate la pool în 10 secunde
02:01 — nodul de trimitere #1 a luat 250 de tranzacții din grupul și blocul de formulare #85 (65e)
02:06 — blocul #85 (f21) este semnat și trimis la alte noduri pentru validare
02:08 — scriptul demonstrativ al serverului #3, care a trimis 1 milion de tranzacții în 30 de secunde, a terminat de funcționat
02:14 — blocul #85 (f21) este validat și trimis la lanțul rădăcină
02:19 — blocul #85 (65e) este semnat și trimis la alte noduri pentru validare
02:22 — 1 milion de tranzacții au fost create și a început trimiterea către nodul #2
02:27 — blocul #85 (65e) validat și trimis la lanțul rădăcină
02:29 — nodul de trimitere #2 a preluat 111855 tranzacții din blocul de blocuri și formulare #85 (256).
02:36 — blocul #85 (256) este semnat și trimis la alte noduri pentru validare
02:36 — scriptul demonstrativ al serverului #1, care a trimis 1 milion de tranzacții în 42.5 de secunde, a terminat de funcționat
02:38 — blocul #85 (256) este validat și trimis la lanțul rădăcină
03:08 — scriptul serverului #2 a terminat de funcționat, care a trimis 1 milion de tranzacții în 47 de secunde
03:38 - toate nodurile au primit informații din lanțul rădăcină care au fost adăugate blocurile #85 (f21), #86(65e), #87(256) și au început să aplice tranzacții de 330k, 250k, 111855
03:49 - pool-ul a fost compensat la 330k, 250k, 111855 tranzacții care au fost adăugate la blocurile #85 (f21), #86(65e), #87(256)
03:59 — nodul de trimitere #1 a luat 888145 de tranzacții din grup și blocul de formulare #88 (214), nodul de trimitere #2 a luat 750 de tranzacții din grupul și blocul de formulare #88 (50a), nodul de trimitere #3 a luat 670 de tranzacții de la piscina și formează blocul #88 (d3b)
04:44 — blocul #88 (d3b) este semnat și trimis la alte noduri pentru validare
04:58 — blocul #88 (214) este semnat și trimis la alte noduri pentru validare
05:11 — blocul #88 (50a) este semnat și trimis la alte noduri pentru validare
05:11 — blocul #85 (d3b) este validat și trimis la lanțul rădăcină
05:36 — blocul #85 (214) este validat și trimis la lanțul rădăcină
05:43 - toate nodurile au primit informații din lanțul rădăcină care blocurile #88 (d3b), #89(214) au fost adăugate și încep să aplice tranzacții de 670k, 750k
06:50 — din cauza unei erori de comunicare, blocul #85 (50a) nu a fost validat
06:55 — nodul de trimitere nr. 2 a preluat 888145 tranzacții din grupul și blocul de formulare #90 (50a)
08:14 — blocul #90 (50a) este semnat și trimis la alte noduri pentru validare
09:04 — blocul #90 (50a) este validat și trimis la lanțul rădăcină
11:23 - toate nodurile au primit informații de la lanțul rădăcină care a fost adăugat blocul #90 (50a) și au început să aplice 888145 tranzacții. În același timp, serverul #3 a aplicat deja tranzacții din blocurile #88 (d3b), #89(214)
12:11 - toate piscinele sunt goale
13:41 — toate nodurile serverului #3 conțin 3 milioane de tranzacții și jetoane
14:35 — toate nodurile serverului #1 conțin 3 milioane de tranzacții și jetoane
19:24 — toate nodurile serverului #2 conțin 3 milioane de tranzacții și jetoane

Obstacole

În timpul dezvoltării Plasma Cash, am întâlnit următoarele probleme, pe care le-am rezolvat treptat și le rezolvăm:

1. Conflict în interacțiunea diferitelor funcții ale sistemului. De exemplu, funcția de adăugare a tranzacțiilor la pool a blocat munca de trimitere și validare a blocurilor și invers, ceea ce a dus la o scădere a vitezei.

2. Nu a fost imediat clar cum să trimiteți un număr mare de tranzacții minimizând în același timp costurile de transfer de date.

3. Nu era clar cum și unde să stocăm datele pentru a obține rezultate înalte.

4. Nu era clar cum se organizează o rețea între noduri, deoarece dimensiunea unui bloc cu 1 milion de tranzacții ocupă aproximativ 100 MB.

5. Lucrul în modul cu un singur fir întrerupe conexiunea dintre noduri atunci când au loc calcule lungi (de exemplu, construirea unui arbore Merkle și calcularea hash-ului acestuia).

Cum ne-am descurcat cu toate acestea?

Prima versiune a nodului Plasma Cash a fost un fel de combină care putea face totul în același timp: să accepte tranzacții, să trimită și să valideze blocuri și să ofere un API pentru accesarea datelor. Deoarece NodeJS este nativ cu un singur thread, funcția grea de calcul a arborelui Merkle a blocat funcția de adăugare a tranzacției. Am văzut două opțiuni pentru a rezolva această problemă:

1. Lansați mai multe procese NodeJS, fiecare dintre ele îndeplinește funcții specifice.

2. Utilizați worker_threads și mutați execuția unei părți a codului în fire.

Drept urmare, am folosit ambele opțiuni în același timp: am împărțit logic un nod în 3 părți care pot funcționa separat, dar în același timp sincron

1. Nod de trimitere, care acceptă tranzacții în pool și creează blocuri.

2. Un nod de validare care verifică validitatea nodurilor.

3. Nod API - oferă un API pentru accesarea datelor.

În acest caz, vă puteți conecta la fiecare nod printr-un socket Unix folosind cli.

Am mutat operațiuni grele, cum ar fi calcularea arborelui Merkle, într-un fir separat.

Astfel, am realizat funcționarea normală a tuturor funcțiilor Plasma Cash simultan și fără defecțiuni.

Odată ce sistemul a fost funcțional, am început să testăm viteza și, din păcate, am primit rezultate nesatisfăcătoare: 5 de tranzacții pe secundă și până la 000 de tranzacții pe bloc. A trebuit să-mi dau seama ce a fost implementat incorect.

Pentru început, am început să testăm mecanismul de comunicare cu Plasma Cash pentru a afla capacitatea maximă a sistemului. Am scris mai devreme că nodul Plasma Cash oferă o interfață socket Unix. Inițial a fost bazat pe text. Obiectele json au fost trimise folosind `JSON.parse()` și `JSON.stringify()`.

```json
{
  "action": "sendTransaction",
  "payload":{
    "prevHash": "0x8a88cc4217745fd0b4eb161f6923235da10593be66b841d47da86b9cd95d93e0",
    "prevBlock": 41,
    "tokenId": "57570139642005649136210751546585740989890521125187435281313126554130572876445",
    "newOwner": "0x200eabe5b26e547446ae5821622892291632d4f4",
    "type": "pay",
    "data": "",
    "signature": "0xd1107d0c6df15e01e168e631a386363c72206cb75b233f8f3cf883134854967e1cd9b3306cc5c0ce58f0a7397ae9b2487501b56695fe3a3c90ec0f61c7ea4a721c"
  }
}
```

Am măsurat viteza de transfer a unor astfel de obiecte și am găsit ~ 130k pe secundă. Am încercat să înlocuim funcțiile standard pentru lucrul cu json, dar performanța nu s-a îmbunătățit. Motorul V8 trebuie să fie bine optimizat pentru aceste operațiuni.

Am lucrat cu tranzacții, jetoane și blocuri prin clase. La crearea unor astfel de clase, performanța a scăzut de 2 ori, ceea ce indică faptul că OOP nu este potrivit pentru noi. A trebuit să rescriu totul într-o abordare pur funcțională.

Înregistrare în baza de date

Inițial, Redis a fost aleasă pentru stocarea datelor ca fiind una dintre cele mai productive soluții care ne satisface cerințele: stocare cheie-valoare, lucru cu tabele hash, seturi. Am lansat redis-benchmark și am obținut ~80 operațiuni pe secundă într-un singur mod de pipeline.

Pentru performanță ridicată, am reglat Redis mai fin:

  • A fost stabilită o conexiune socket Unix.
  • Am dezactivat salvarea stării pe disc (pentru fiabilitate, puteți configura o replică și puteți salva pe disc într-un Redis separat).

În Redis, un pool este un tabel hash, deoarece trebuie să putem prelua toate tranzacțiile într-o singură interogare și să ștergem tranzacțiile una câte una. Am încercat să folosim o listă obișnuită, dar este mai lent la descărcarea întregii liste.

Când se utilizează NodeJS standard, bibliotecile Redis au atins o performanță de 18 de tranzacții pe secundă. Viteza a scăzut de 9 ori.

Deoarece benchmark-ul ne-a arătat că posibilitățile erau clar de 5 ori mai mari, am început să optimizăm. Am schimbat biblioteca în ioredis și am obținut o performanță de 25k pe secundă. Am adăugat tranzacțiile una câte una folosind comanda `hset`. Deci am generat o mulțime de interogări în Redis. A apărut ideea de a combina tranzacțiile în loturi și de a le trimite cu o singură comandă `hmset`. Rezultatul este 32k pe secundă.

Din mai multe motive, pe care le vom descrie mai jos, lucrăm cu date folosind `Buffer` și, după cum se dovedește, dacă le convertiți în text (`buffer.toString('hex')`) înainte de a scrie, puteți obține suplimentar performanţă. Astfel, viteza a fost crescută la 35k pe secundă. Momentan, am decis să suspendăm optimizarea ulterioară.

A trebuit să trecem la un protocol binar deoarece:

1. Sistemul calculează adesea hashuri, semnături etc., iar pentru aceasta are nevoie de date în `Buffer.

2. Când sunt trimise între servicii, datele binare cântăresc mai puțin decât textul. De exemplu, la trimiterea unui bloc cu 1 milion de tranzacții, datele din text pot ocupa mai mult de 300 de megaocteți.

3. Transformarea constantă a datelor afectează performanța.

Prin urmare, am luat ca bază propriul nostru protocol binar pentru stocarea și transmiterea datelor, dezvoltat pe baza minunatei biblioteci `binary-data`.

Ca rezultat, am obținut următoarele structuri de date:

-Tranzacţie

  ```json
  {
    prevHash: BD.types.buffer(20),
    prevBlock: BD.types.uint24le,
    tokenId: BD.types.string(null),
    type: BD.types.uint8,
    newOwner: BD.types.buffer(20),
    dataLength: BD.types.uint24le,
    data: BD.types.buffer(({current}) => current.dataLength),
    signature: BD.types.buffer(65),
    hash: BD.types.buffer(32),
    blockNumber: BD.types.uint24le,
    timestamp: BD.types.uint48le,
  }
  ```

— Jeton

  ```json
  {
    id: BD.types.string(null),
    owner: BD.types.buffer(20),
    block: BD.types.uint24le,
    amount: BD.types.string(null),
  }
  ```

-Bloc

  ```json
  {
    number: BD.types.uint24le,
    merkleRootHash: BD.types.buffer(32),
    signature: BD.types.buffer(65),
    countTx: BD.types.uint24le,
    transactions: BD.types.array(Transaction.Protocol, ({current}) => current.countTx),
    timestamp: BD.types.uint48le,
  }
  ```

Cu comenzile obișnuite `BD.encode(block, Protocol).slice();` și `BD.decode(buffer, Protocol)` convertim datele în `Buffer` pentru salvarea în Redis sau redirecționarea către alt nod și preluarea date înapoi.

Avem, de asemenea, 2 protocoale binare pentru transferul de date între servicii:

— Protocol pentru interacțiunea cu Plasma Node prin soclu Unix

  ```json
  {
    type: BD.types.uint8,
    messageId: BD.types.uint24le,
    error: BD.types.uint8,
    length: BD.types.uint24le,
    payload: BD.types.buffer(({node}) => node.length)
  }
  ```

în cazul în care:

  • "tip" — acțiunea care trebuie efectuată, de exemplu, 1 — sendTransaction, 2 — getTransaction;
  • `sarcină utilă` — date care trebuie transmise funcției corespunzătoare;
  • `MessageId` — ID mesaj pentru ca răspunsul să poată fi identificat.

— Protocol pentru interacțiunea între noduri

  ```json
  {
    code: BD.types.uint8,
    versionProtocol: BD.types.uint24le,
    seq: BD.types.uint8,
    countChunk: BD.types.uint24le,
    chunkNumber: BD.types.uint24le,
    length: BD.types.uint24le,
    payload: BD.types.buffer(({node}) => node.length)
  }
  ```

în cazul în care:

  • `cod` — codul mesajului, de exemplu 6 — PREPARE_NEW_BLOCK, 7 — BLOCK_VALID, 8 — BLOCK_COMMIT;
  • `versionProtocol` — versiunea de protocol, deoarece nodurile cu versiuni diferite pot fi ridicate în rețea și pot funcționa diferit;
  • `seq` — identificatorul mesajului;
  • `countChunk` и `număr bucată` necesar pentru împărțirea mesajelor mari;
  • `lungime` и `sarcină utilă` lungimea și datele în sine.

Deoarece am pretastat datele, sistemul final este mult mai rapid decât biblioteca `rlp` a lui Ethereum. Din păcate, încă nu am reușit să-l refuzăm, deoarece este necesar să finalizăm smart contractul, ceea ce ne propunem să facem în viitor.

Dacă am reuși să atingem viteza 35 000 tranzacții pe secundă, trebuie și să le procesăm în timpul optim. Deoarece timpul aproximativ de formare a blocului durează 30 de secunde, trebuie să includem în bloc 1 000 000 tranzacții, ceea ce înseamnă trimiterea mai multor 100 MB de date.

Inițial, am folosit biblioteca `ethereumjs-devp2p` pentru a comunica între noduri, dar nu a putut gestiona atât de multe date. Ca rezultat, am folosit biblioteca `ws` și am configurat trimiterea datelor binare prin websocket. Desigur, am întâmpinat și probleme la trimiterea de pachete mari de date, dar le-am împărțit în bucăți și acum aceste probleme au dispărut.

De asemenea, formând un arbore Merkle și calculând hash-ul 1 000 000 tranzacţiile necesită aproximativ 10 secunde de calcul continuu. În acest timp, conexiunea cu toate nodurile reușește să se rupă. S-a decis mutarea acestui calcul într-un fir separat.

Concluzii:

De fapt, descoperirile noastre nu sunt noi, dar din anumite motive mulți experți uită de ele atunci când dezvoltă.

  • Utilizarea programării funcționale în loc de programare orientată pe obiecte îmbunătățește productivitatea.
  • Monolitul este mai rău decât o arhitectură de servicii pentru un sistem NodeJS productiv.
  • Utilizarea „worker_threads” pentru calcule grele îmbunătățește capacitatea de răspuns a sistemului, mai ales atunci când se ocupă cu operațiuni de i/o.
  • socket-ul unix este mai stabil și mai rapid decât cererile http.
  • Dacă trebuie să transferați rapid date mari prin rețea, este mai bine să utilizați websocket-uri și să trimiteți date binare, împărțite în bucăți, care pot fi redirecționate dacă nu ajung și apoi combinate într-un singur mesaj.

Vă invităm să vizitați GitHub proiect: https://github.com/opporty-com/Plasma-Cash/tree/new-version

Articolul a fost co-scris de Alexandru Nashivan, dezvoltator senior Clever Solution Inc.

Sursa: www.habr.com

Adauga un comentariu