Îmbunătățirea throughput-ului unui Producer ✈

  • 3 minute de citit
  • 4 imagini

Disponibil și în English

Pe această pagină

Salut 👋,

În acest articol îți voi da câteva sfaturi despre cum poți îmbunătăți throughput-ul unui producător de mesaje.

Trebuia să scriu o aplicație în Golang care consuma mesaje din Apache Kafka și le trimitea într-un sink prin HTTP, în format JSON / HTTP Protocol Buffers.

Ca să văd dacă ideea mea funcționează, am început cu o abordare naivă: interogam Kafka pentru mesaje și apoi trimiteam fiecare mesaj în sink, unul câte unul. Asta a funcționat, dar era lent.

Ca să înțelegem mai bine sistemul, un coleg a configurat Grafana și un dashboard pentru monitorizare, folosind metricile Prometheus furnizate de Sink. Asta ne-a permis să testăm diferite versiuni ale producătorului și să-i observăm comportamentul.

Hai să explorăm ce putem face ca să îmbunătățim throughput-ul.

Batching al cererilor 📪

O îmbunătățire foarte importantă este batching-ul cererilor.

În loc să trimiți un singur mesaj pe cerere HTTP, încearcă să trimiți mai multe, dacă sink-ul permite.

După cum se vede în imagine, această idee simplă a ridicat throughput-ul de la 100 mesaje/sec la ~4000 mesaje/sec.

Batching-ul e delicat: dacă batch-urile sunt mari, receiver-ul poate fi copleșit, sau producătorul poate avea dificultăți să le construiască. Dacă batch-urile conțin câteva elemente, s-ar putea să nu vezi nicio îmbunătățire. Încearcă să alegi un număr pentru batch care nu e prea mare și nici prea mic.

Biblioteci JSON rapide ⏩

Dacă folosești HTTP și JSON, e o idee bună să înlocuiești biblioteca standard JSON.

Există multe biblioteci JSON open-source care oferă performanțe mult mai bune decât bibliotecile JSON standard incluse în limbaj.

Vezi:

Îmbunătățirile vor fi vizibile.

Partiționare 🖇

Există mai multe strategii de partiționare pe care le poți implementa. Depinde de stack-ul tehnologic.

Kafka îți permite să asociezi un consumator cu o partiție: dacă ai 3 partiții într-un topic, poți rula 3 instanțe de consumator în paralel din acel topic, în același consumer group. Aceasta se numește replicare. Eu nu am folosit-o, pentru că Sink-ul nu o permite: rulează o singură instanță a Producer-ului la un moment dat.

Dacă ai mai multe topicuri din care vrei să consumi, poți face partiționarea pe numele topicului sau pe un tipar de nume de topic, abonându-te la mai multe topicuri deodată folosind regex. Poți avea 3 consumatori care consumă din highspeed.* și 3 consumatori care consumă din other.*, dacă fiecare topic are 3 partiții.

Notă: Build-ul standard al librdkafka nu suportă expresii regex cu negative lookahead. Dacă ai nevoie de ele, va trebui să compilezi biblioteca din surse. Vezi issues/2769. E ușor de făcut, iar clientul confluent-kafka-go suportă build-uri personalizate ale librdkafka.

Expresiile cu negative lookahead îți permit să ignori anumite tipare. Vezi acest exemplu pentru o înțelegere mai bună: regex101.com/r/jZ9AEz/1

Protocol Buffers 🔷

În final, am observat o îmbunătățire uriașă de performanță când am înlocuit corpul JSON al cererii cu date codificate în Protocol Buffers și comprimate cu snappy.

Dacă Sink-ul tău suportă primirea de protocol buffers, e o idee bună să încerci să le trimiți în loc de JSON.

Mențiune de onoare: JSON comprimat cu GZIP 📚

Sink-ul suporta primirea de JSON comprimat cu GZIP, dar în cazul meu nu am observat îmbunătățiri notabile de performanță.

Am comparat utilizarea RAM și CPU a Producer-ului, numărul de octeți trimiși prin rețea și throughput-ul mesajelor. Au existat unele îmbunătățiri în unele zone , dar am decis să nu implementez compresia GZIP.

Totul ține de compromisuri și nevoi.

Concluzie

După cum ai văzut, există mai multe lucruri pe care le poți face pentru producătorii tăi ca să fie mai eficienți.

  • Batching al cererilor
  • Biblioteci JSON rapide
  • Partiționare
  • Protocol Buffers
  • Compresie

Sper că ți-a plăcut acest articol și că ai învățat ceva! Dacă ai idei, spune-mi în comentarii.

Mulțumesc pentru lectură! 😀