Karanasan sa pagbuo ng serbisyo ng Refund Tool na may asynchronous na API sa Kafka

Ano ang maaaring puwersahin ang isang malaking kumpanya tulad ng Lamoda, na may isang streamlined na proseso at dose-dosenang mga interconnected na serbisyo, upang makabuluhang baguhin ang diskarte nito? Ang pagganyak ay maaaring ganap na naiiba: mula sa pambatasan hanggang sa pagnanais na mag-eksperimento na likas sa lahat ng mga programmer.

Ngunit hindi ito nangangahulugan na hindi ka makakaasa sa mga karagdagang benepisyo. Sasabihin sa iyo ni Sergey Zaika kung ano ang eksaktong maaari mong mapanalunan kung ipapatupad mo ang API na hinimok ng mga kaganapan sa Kafka (fewald). Tiyak na magkakaroon din ng pag-uusap tungkol sa mga malalaking shot at mga kagiliw-giliw na pagtuklas - hindi magagawa ng eksperimento kung wala sila.

Karanasan sa pagbuo ng serbisyo ng Refund Tool na may asynchronous na API sa Kafka

Disclaimer: Ang artikulong ito ay batay sa mga materyales mula sa isang meetup na ginawa ni Sergey noong Nobyembre 2018 sa HighLoad++. Ang live na karanasan ni Lamoda sa pakikipagtulungan sa Kafka ay nakaakit ng mga tagapakinig nang hindi bababa sa iba pang mga ulat sa iskedyul. Sa tingin namin ito ay isang mahusay na halimbawa ng katotohanan na maaari at dapat kang laging makahanap ng mga taong katulad ng pag-iisip, at ang mga organizer ng HighLoad++ ay patuloy na susubukan na lumikha ng isang kapaligiran na kaaya-aya para dito.

Tungkol sa proseso

Ang Lamoda ay isang malaking platform ng e-commerce na may sariling contact center, serbisyo sa paghahatid (at maraming mga kaakibat), isang photo studio, isang malaking bodega, at lahat ng ito ay tumatakbo sa sarili nitong software. Mayroong dose-dosenang mga paraan ng pagbabayad, mga kasosyo sa b2b na maaaring gumamit ng ilan o lahat ng mga serbisyong ito at gustong malaman ang napapanahong impormasyon sa kanilang mga produkto. Bilang karagdagan, ang Lamoda ay nagpapatakbo sa tatlong bansa bukod sa Russian Federation at lahat ay medyo naiiba doon. Sa kabuuan, malamang na mayroong higit sa isang daang mga paraan upang i-configure ang isang bagong order, na dapat iproseso sa sarili nitong paraan. Gumagana ang lahat ng ito sa tulong ng dose-dosenang mga serbisyo na kung minsan ay nakikipag-usap sa mga hindi halatang paraan. Mayroon ding sentral na sistema na ang pangunahing responsibilidad ay ang mga katayuan ng order. BOB ang tawag namin sa kanya, katrabaho ko siya.

Refund Tool na may API na hinimok ng mga kaganapan

Ang salitang hinihimok ng mga kaganapan ay medyo hackneyed; nang kaunti pa ay tutukuyin natin nang mas detalyado kung ano ang ibig sabihin nito. Magsisimula ako sa konteksto kung saan nagpasya kaming subukan ang mga event-driven na API approach sa Kafka.

Karanasan sa pagbuo ng serbisyo ng Refund Tool na may asynchronous na API sa Kafka

Sa anumang tindahan, bukod pa sa mga order na binabayaran ng mga customer, may mga pagkakataon na ang tindahan ay kinakailangan na magbalik ng pera dahil ang produkto ay hindi nababagay sa customer. Ito ay medyo maikling proseso: nililinaw namin ang impormasyon, kung kinakailangan, at inililipat ang pera.

Ngunit ang pagbabalik ay naging mas kumplikado dahil sa mga pagbabago sa batas, at kinailangan naming magpatupad ng hiwalay na microservice para dito.

Karanasan sa pagbuo ng serbisyo ng Refund Tool na may asynchronous na API sa Kafka

Ang aming motibasyon:

  1. Batas FZ-54 - sa madaling salita, ang batas ay nangangailangan ng pag-uulat sa tanggapan ng buwis tungkol sa bawat transaksyon sa pananalapi, maging ito ay isang pagbabalik o isang resibo, sa loob ng medyo maikling SLA ng ilang minuto. Kami, bilang isang kumpanya ng e-commerce, ay nagsasagawa ng maraming operasyon. Sa teknikal, nangangahulugan ito ng bagong responsibilidad (at samakatuwid ay isang bagong serbisyo) at mga pagpapabuti sa lahat ng kasangkot na sistema.
  2. Nahati si BOB ay isang panloob na proyekto ng kumpanya upang mapawi ang BOB mula sa isang malaking bilang ng mga hindi pangunahing responsibilidad at bawasan ang pangkalahatang pagiging kumplikado nito.

Karanasan sa pagbuo ng serbisyo ng Refund Tool na may asynchronous na API sa Kafka

Ipinapakita ng diagram na ito ang mga pangunahing sistema ng Lamoda. Ngayon karamihan sa kanila ay higit pa isang konstelasyon ng 5-10 microservice sa paligid ng isang lumiliit na monolith. Ang mga ito ay dahan-dahang lumalaki, ngunit sinusubukan naming gawing mas maliit ang mga ito, dahil ang pag-deploy ng fragment na napili sa gitna ay nakakatakot - hindi namin maaaring payagan itong mahulog. Napipilitan kaming ireserba ang lahat ng mga palitan (mga arrow) at isaalang-alang ang katotohanan na ang alinman sa mga ito ay maaaring lumabas na hindi magagamit.

Ang BOB ay mayroon ding napakaraming palitan: mga sistema ng pagbabayad, mga sistema ng paghahatid, mga sistema ng abiso, atbp.

Ang teknikal na BOB ay:

  • ~150k linya ng code + ~100k linya ng pagsubok;
  • php7.2 + Zend 1 at Symfony Components 3;
  • >100 API & ~50 papalabas na pagsasama;
  • 4 na bansa na may sariling lohika sa negosyo.

Ang pag-deploy ng BOB ay magastos at masakit, ang dami ng code at mga problema na nilulutas nito ay ganoong walang sinuman ang maaaring ilagay ang lahat ng ito sa kanilang mga ulo. Sa pangkalahatan, maraming dahilan para gawing simple ito.

Proseso ng Pagbabalik

Sa una, dalawang sistema ang kasangkot sa proseso: BOB at Pagbabayad. Ngayon dalawa pa ang lalabas:

  • Serbisyo sa Fiscalization, na aasikasuhin ang mga problema sa fiscalization at komunikasyon sa mga panlabas na serbisyo.
  • Refund Tool, na naglalaman lamang ng mga bagong palitan upang hindi mapalaki ang BOB.

Ngayon ang proseso ay ganito ang hitsura:

Karanasan sa pagbuo ng serbisyo ng Refund Tool na may asynchronous na API sa Kafka

  1. Nakatanggap ang BOB ng kahilingan para sa refund.
  2. Pinag-uusapan ni BOB ang Refund Tool na ito.
  3. Ang Refund Tool ay nagsasabi sa Pagbabayad: "Ibalik ang pera."
  4. Ibinabalik ng pagbabayad ang pera.
  5. Ang Refund Tool at BOB ay nagsi-synchronize ng mga status sa isa't isa, dahil sa ngayon kailangan nila ito. Hindi pa kami handang ganap na lumipat sa Refund Tool, dahil may UI ang BOB, mga ulat para sa accounting, at sa pangkalahatan ay maraming data na hindi madaling mailipat. Kailangan mong umupo sa dalawang upuan.
  6. Ang kahilingan para sa fiscalization ay mawawala.

Bilang resulta, gumawa kami ng uri ng event bus sa Kafka - event-bus, kung saan nagsimula ang lahat. Hurray, mayroon na tayong isang punto ng kabiguan (sarcasm).

Karanasan sa pagbuo ng serbisyo ng Refund Tool na may asynchronous na API sa Kafka

Ang mga kalamangan at kahinaan ay medyo halata. Gumawa kami ng bus, ibig sabihin, nakadepende na rito ang lahat ng serbisyo. Pinapasimple nito ang disenyo, ngunit nagpapakilala ng isang punto ng pagkabigo sa system. Babagsak ang Kafka, hihinto ang proseso.

Ano ang event-driven na API

Ang isang magandang sagot sa tanong na ito ay nasa ulat ni Martin Fowler (GOTO 2017) "Ang Maraming Kahulugan ng Arkitekturang Nababatay sa Kaganapan".

Maikling kung ano ang ginawa namin:

  1. I-wrap up ang lahat ng asynchronous na palitan sa pamamagitan ng imbakan ng mga kaganapan. Sa halip na ipaalam sa bawat interesadong consumer ang tungkol sa pagbabago ng status sa network, nagsusulat kami ng kaganapan tungkol sa pagbabago ng status sa isang sentralisadong storage, at binabasa ng mga consumer na interesado sa paksa ang lahat ng lumalabas doon.
  2. Ang kaganapan sa kasong ito ay isang abiso (mga notification) na may nagbago sa isang lugar. Halimbawa, nagbago ang katayuan ng order. Ang isang consumer na interesado sa ilang data na kasama ng pagbabago ng status na hindi kasama sa notification ay maaaring malaman mismo ang status nito.
  3. Ang maximum na opsyon ay ganap na pag-sourcing ng kaganapan, paglipat ng estado, kung saan ang kaganapan ay naglalaman ng lahat ng impormasyong kinakailangan para sa pagpoproseso: kung saan ito nanggaling at kung anong status ito napunta, kung paano eksaktong nagbago ang data, atbp. Ang tanging tanong ay ang pagiging posible at ang dami ng impormasyon na maaari mong iimbak.

Bilang bahagi ng paglulunsad ng Refund Tool, ginamit namin ang ikatlong opsyon. Ang pinasimpleng pagpoproseso ng event na ito dahil hindi na kailangang kumuha ng detalyadong impormasyon, at inalis nito ang senaryo kung saan ang bawat bagong kaganapan ay bumubuo ng isang pagsabog ng paglilinaw sa pagkuha ng mga kahilingan mula sa mga consumer.

Serbisyo ng Refund Tool hindi load, kaya ang Kafka ay may higit na lasa ng panulat kaysa sa isang pangangailangan. Hindi ko iniisip na kung ang serbisyo ng refund ay naging isang proyektong may mataas na karga, magiging masaya ang negosyo.

Async exchange AS IS

Para sa mga asynchronous na palitan, karaniwang ginagamit ng departamento ng PHP ang RabbitMQ. Kinokolekta namin ang data para sa kahilingan, inilagay ito sa isang pila, at binasa ito ng consumer ng parehong serbisyo at ipinadala ito (o hindi ipinadala). Para sa API mismo, aktibong ginagamit ng Lamoda ang Swagger. Nagdidisenyo kami ng API, inilalarawan ito sa Swagger, at bumubuo ng client at server code. Gumagamit din kami ng bahagyang pinahusay na JSON RPC 2.0.

Sa ilang lugar ay ginagamit ang mga ESB bus, ang ilan ay nakatira sa activeMQ, ngunit, sa pangkalahatan, RabbitMQ - pamantayan.

Async exchange TO BE

Kapag nagdidisenyo ng palitan sa pamamagitan ng mga kaganapan-bus, maaaring masubaybayan ang isang pagkakatulad. Pareho naming inilalarawan ang pagpapalitan ng data sa hinaharap sa pamamagitan ng mga paglalarawan ng istruktura ng kaganapan. Ang yaml format, kinailangan naming gawin ang pagbuo ng code sa aming sarili, ang generator ay lumilikha ng mga DTO ayon sa detalye at nagtuturo sa mga kliyente at server na makipagtulungan sa kanila. Ang henerasyon ay napupunta sa dalawang wika - golang at php. Nakakatulong ito na panatilihing pare-pareho ang mga aklatan. Ang generator ay nakasulat sa golang, kaya naman nakuha ang pangalang gogi.

Ang pag-sourcing ng kaganapan sa Kafka ay isang karaniwang bagay. Mayroong isang solusyon mula sa pangunahing bersyon ng enterprise ng Kafka Confluent, mayroon nakadi, isang solusyon mula sa aming mga kapatid sa domain na Zalando. Ang aming pagganyak na magsimula sa vanilla Kafka - Nangangahulugan ito na iwanan ang solusyon nang libre hanggang sa wakas ay magpasya kami kung gagamitin namin ito sa lahat ng dako, at mag-iwan din ng puwang sa aming sarili para sa maniobra at mga pagpapabuti: gusto namin ng suporta para sa aming JSON RPC 2.0, mga generator para sa dalawang wika at tingnan natin kung ano pa.

Nakakabaliw na kahit na sa isang masayang kaso, kapag may halos katulad na negosyo, ang Zalando, na gumawa ng halos katulad na solusyon, hindi namin ito magagamit nang epektibo.

Ang pattern ng arkitektura sa paglulunsad ay ang mga sumusunod: direkta kaming nagbabasa mula sa Kafka, ngunit sumulat lamang sa pamamagitan ng mga kaganapan-bus. Mayroong maraming handa para sa pagbabasa sa Kafka: mga broker, balancer, at ito ay higit pa o hindi gaanong handa para sa pahalang na pag-scale, nais kong panatilihin ito. Nais naming kumpletuhin ang pag-record sa pamamagitan ng isang Gateway aka Events-bus, at narito kung bakit.

Mga kaganapan-bus

O isang bus ng kaganapan. Isa lang itong stateless http gateway, na tumatagal sa ilang mahahalagang tungkulin:

  • Paggawa ng Pagpapatunay β€” sinusuri namin na ang mga kaganapan ay nakakatugon sa aming mga pagtutukoy.
  • Sistema ng master ng kaganapan, iyon ay, ito ang pangunahing at tanging sistema sa kumpanya na sumasagot sa tanong kung aling mga kaganapan kung saan ang mga istruktura ay itinuturing na wasto. Ang pagpapatunay ay nagsasangkot lamang ng mga uri ng data at mga enum upang mahigpit na tukuyin ang nilalaman.
  • Pag-andar ng hash para sa sharding - ang istraktura ng mensahe ng Kafka ay key-value at gamit ang hash ng key ay kinakalkula kung saan ito ilalagay.

Bakit

Nagtatrabaho kami sa isang malaking kumpanya na may streamlined na proseso. Bakit baguhin ang anumang bagay? Isa itong eksperimento, at inaasahan naming aanihin ang ilang mga benepisyo.

1:n+1 na palitan (isa sa marami)

Pinapadali ng Kafka na ikonekta ang mga bagong consumer sa API.

Sabihin nating mayroon kang isang direktoryo na kailangan mong panatilihing napapanahon sa ilang mga system nang sabay-sabay (at sa ilang mga bago). Noong nakaraan, nag-imbento kami ng isang bundle na nagpatupad ng set-API, at ang master system ay ipinaalam sa mga address ng consumer. Ngayon ang master system ay nagpapadala ng mga update sa paksa, at lahat na interesado ay nagbabasa nito. May lumitaw na bagong system - nilagdaan namin ito para sa paksa. Oo, bundle din, ngunit mas simple.

Sa kaso ng refund-tool, na isang piraso ng BOB, maginhawa para sa amin na panatilihing naka-synchronize ang mga ito sa pamamagitan ng Kafka. Sinasabi ng pagbabayad na ibinalik ang pera: BOB, nalaman ito ng RT, binago ang kanilang mga katayuan, nalaman ito ng Fiscalization Service at nagbigay ng tseke.

Karanasan sa pagbuo ng serbisyo ng Refund Tool na may asynchronous na API sa Kafka

Mayroon kaming mga plano na lumikha ng pinag-isang Serbisyo sa Mga Notification na mag-aabiso sa kliyente tungkol sa mga balita tungkol sa kanyang order/pagbabalik. Ngayon ang responsibilidad na ito ay kumalat sa pagitan ng mga system. Sapat na para sa amin na turuan ang Serbisyo ng Mga Notification na kumuha ng nauugnay na impormasyon mula sa Kafka at tumugon dito (at huwag paganahin ang mga notification na ito sa ibang mga system). Walang bagong direktang palitan ang kakailanganin.

Hinihimok ng data

Nagiging transparent ang impormasyon sa pagitan ng mga system - kahit anong "madugong negosyo" ang mayroon ka at gaano man kalaki ang iyong backlog. Ang Lamoda ay may departamento ng Data Analytics na nangongolekta ng data mula sa mga system at inilalagay ito sa isang muling magagamit na anyo, para sa negosyo at para sa mga matalinong system. Binibigyang-daan ka ng Kafka na mabilis na bigyan sila ng maraming data at panatilihing napapanahon ang daloy ng impormasyong iyon.

Log ng pagtitiklop

Ang mga mensahe ay hindi nawawala pagkatapos basahin, tulad ng sa RabbitMQ. Kapag ang isang kaganapan ay naglalaman ng sapat na impormasyon para sa pagproseso, mayroon kaming kasaysayan ng mga kamakailang pagbabago sa bagay, at, kung ninanais, ang kakayahang ilapat ang mga pagbabagong ito.

Ang panahon ng pag-iimbak ng replication log ay nakasalalay sa intensity ng pagsulat sa paksang ito; pinapayagan ka ng Kafka na flexible na magtakda ng mga limitasyon sa oras ng pag-iimbak at dami ng data. Para sa masinsinang paksa, mahalagang magkaroon ng panahon ang lahat ng mga mamimili na basahin ang impormasyon bago ito mawala, kahit na sa kaso ng panandaliang kawalan ng kakayahang magamit. Karaniwang posible na mag-imbak ng data para sa mga yunit ng araw, na sapat na para sa suporta.

Karanasan sa pagbuo ng serbisyo ng Refund Tool na may asynchronous na API sa Kafka

Susunod, isang maliit na muling pagsasalaysay ng dokumentasyon, para sa mga hindi pamilyar sa Kafka (ang larawan ay mula rin sa dokumentasyon)

Ang AMQP ay may mga pila: nagsusulat kami ng mga mensahe sa isang pila para sa mamimili. Karaniwan, ang isang queue ay pinoproseso ng isang system na may parehong lohika ng negosyo. Kung kailangan mong abisuhan ang ilang mga system, maaari mong turuan ang application na sumulat sa ilang mga pila o i-configure ang exchange gamit ang mekanismo ng fanout, na nag-clone sa kanila mismo.

Ang Kafka ay may katulad na abstraction paksa, kung saan nagsusulat ka ng mga mensahe, ngunit hindi sila nawawala pagkatapos basahin. Bilang default, kapag kumonekta ka sa Kafka, matatanggap mo ang lahat ng mensahe at may opsyong i-save kung saan ka tumigil. Ibig sabihin, nagbasa ka nang sunud-sunod, maaaring hindi mo markahan ang mensahe bilang nabasa, ngunit i-save ang id kung saan maaari mong ipagpatuloy ang pagbabasa. Ang Id na iyong pinagkasunduan ay tinatawag na offset, at ang mekanismo ay commit offset.

Alinsunod dito, maaaring ipatupad ang iba't ibang lohika. Halimbawa, mayroon kaming BOB sa 4 na pagkakataon para sa iba't ibang bansa - Ang Lamoda ay nasa Russia, Kazakhstan, Ukraine, Belarus. Dahil hiwalay ang mga ito, mayroon silang bahagyang magkakaibang mga config at sarili nilang logic sa negosyo. Ipinapahiwatig namin sa mensahe kung aling bansa ang tinutukoy nito. Ang bawat BOB consumer sa bawat bansa ay nagbabasa gamit ang ibang groupId, at kung ang mensahe ay hindi naaangkop sa kanila, nilalaktawan nila ito, i.e. agad na nag-offset ng +1. Kung ang parehong paksa ay binabasa ng aming Serbisyo sa Pagbabayad, ginagawa ito sa isang hiwalay na grupo, at samakatuwid ang mga offset ay hindi nagsalubong.

Mga kinakailangan sa kaganapan:

  • Pagkumpleto ng data. Nais kong magkaroon ng sapat na data ang kaganapan upang ito ay maproseso.

  • Integridad Ibinibigay namin sa Events-bus ang pag-verify na pare-pareho ang kaganapan at mapoproseso nito ito.
  • Mahalaga ang order. Sa kaso ng pagbabalik, napipilitan tayong magtrabaho kasama ang kasaysayan. Sa mga notification, ang order ay hindi mahalaga, kung ang mga ito ay homogenous na notification, ang email ay magiging pareho anuman ang order na unang dumating. Sa kaso ng refund, mayroong malinaw na proseso; kung babaguhin namin ang order, lalabas ang mga pagbubukod, hindi gagawin o ipoproseso ang refund - mapupunta kami sa ibang katayuan.
  • Hindi pagbabago. Mayroon kaming tindahan, at ngayon ay gumagawa kami ng mga kaganapan sa halip na isang API. Kailangan namin ng paraan upang mabilis at murang magpadala ng impormasyon tungkol sa mga bagong kaganapan at pagbabago sa mga umiiral na sa aming mga serbisyo. Ito ay nakakamit sa pamamagitan ng isang karaniwang detalye sa isang hiwalay na git repository at mga generator ng code. Samakatuwid, ang mga kliyente at server sa iba't ibang mga serbisyo ay pinagsama-sama.

Kafka sa Lamoda

Mayroon kaming tatlong Kafka installation:

  1. Mga tala;
  2. R&D;
  3. Mga kaganapan-bus.

Ngayon ay pinag-uusapan lamang natin ang tungkol sa huling punto. Sa mga kaganapan-bus, wala kaming napakalaking pag-install - 3 broker (server) at 27 paksa lamang. Bilang isang tuntunin, ang isang paksa ay isang proseso. Ngunit ito ay isang banayad na punto, at tatalakayin natin ito ngayon.

Karanasan sa pagbuo ng serbisyo ng Refund Tool na may asynchronous na API sa Kafka

Sa itaas ay ang rps graph. Ang proseso ng mga refund ay minarkahan ng turquoise na linya (oo, ang nasa X axis), at ang pink na linya ay ang proseso ng pag-update ng nilalaman.

Ang Lamoda catalog ay naglalaman ng milyun-milyong produkto, at ang data ay ina-update sa lahat ng oras. Ang ilang mga koleksyon ay lumalabas sa uso, ang mga bago ay inilabas upang palitan ang mga ito, at ang mga bagong modelo ay patuloy na lumalabas sa katalogo. Sinusubukan naming hulaan kung ano ang magiging interesante sa aming mga customer bukas, kaya patuloy kaming bumibili ng mga bagong bagay, kunan ng larawan ang mga ito at ina-update ang display case.

Ang mga pink peak ay mga update sa produkto, iyon ay, mga pagbabago sa mga produkto. Makikita na ang mga lalaki ay nagpa-picture, nagpa-picture, at saka ulit! β€” nag-load ng isang pakete ng mga kaganapan.

Mga kaso ng paggamit ng Lamoda Events

Ginagamit namin ang itinayong arkitektura para sa mga sumusunod na operasyon:

  • Ibalik ang pagsubaybay sa katayuan: call-to-action at pagsubaybay sa katayuan mula sa lahat ng kasangkot na system. Pagbabayad, status, fiscalization, notification. Dito namin sinubukan ang diskarte, gumawa ng mga tool, nakolekta ang lahat ng mga bug, nagsulat ng dokumentasyon at sinabi sa aming mga kasamahan kung paano gamitin ito.
  • Pag-update ng mga card ng produkto: pagsasaayos, meta-data, mga katangian. Isang sistema ang nagbabasa (na nagpapakita), at marami ang nagsusulat.
  • Email, push at sms: nakolekta na ang order, dumating na ang order, tinanggap ang pagbabalik, atbp., marami sila.
  • Stock, pag-renew ng bodega β€” quantitative update of items, numero lang: pagdating sa bodega, pagbalik. Kinakailangan na ang lahat ng mga sistemang nauugnay sa pagrereserba ng mga kalakal ay gumana gamit ang pinakabagong data. Sa kasalukuyan, ang sistema ng pag-update ng stock ay medyo kumplikado; Pasimplehin ito ng Kafka.
  • Data ng Pagsusuri (R&D department), ML tools, analytics, statistics. Gusto naming maging transparent ang impormasyon - Ang Kafka ay angkop para dito.

Ngayon ang mas kawili-wiling bahagi tungkol sa malalaking bumps at kawili-wiling mga pagtuklas na naganap sa nakalipas na anim na buwan.

Mga problema sa disenyo

Sabihin nating gusto naming gumawa ng bagong bagay - halimbawa, ilipat ang buong proseso ng paghahatid sa Kafka. Ngayon bahagi ng proseso ay ipinatupad sa Order Processing sa BOB. Mayroong isang modelo ng katayuan sa likod ng paglipat ng isang order sa serbisyo ng paghahatid, paggalaw sa isang intermediate na bodega, at iba pa. Mayroong isang buong monolith, kahit na dalawa, kasama ang isang grupo ng mga API na nakatuon sa paghahatid. Marami pa silang alam tungkol sa paghahatid.

Ang mga ito ay tila magkatulad na mga lugar, ngunit ang Pagproseso ng Order sa BOB at ang Sistema ng Pagpapadala ay may magkaibang katayuan. Halimbawa, ang ilang mga serbisyo ng courier ay hindi nagpapadala ng mga intermediate na katayuan, ngunit ang mga pangwakas lamang: "naihatid" o "nawala". Ang iba, sa kabaligtaran, ay nag-uulat nang detalyado tungkol sa paggalaw ng mga kalakal. Ang bawat isa ay may sariling mga panuntunan sa pagpapatunay: para sa ilan, ang email ay wasto, na nangangahulugang ito ay ipoproseso; para sa iba hindi valid, pero ipoproseso pa rin ang order dahil may phone number para sa contact, at may magsasabi na hindi mapoproseso ang naturang order.

Stream ng data

Sa kaso ng Kafka, ang tanong ng pag-aayos ng daloy ng data ay lumitaw. Ang gawaing ito ay nagsasangkot ng pagpili ng isang diskarte batay sa ilang mga punto; suriin natin ang lahat ng ito.

Sa isang paksa o sa magkaibang paksa?

Mayroon kaming detalye ng kaganapan. Sa BOB isinusulat namin na ang ganoon at ganoong order ay kailangang maihatid, at ipahiwatig: ang numero ng order, komposisyon nito, ilang SKU at bar code, atbp. Kapag dumating ang mga kalakal sa bodega, ang paghahatid ay makakatanggap ng mga katayuan, mga timestamp at lahat ng kailangan. Ngunit pagkatapos ay gusto naming makatanggap ng mga update sa data na ito sa BOB. Mayroon kaming baligtad na proseso ng pagtanggap ng data mula sa paghahatid. Ito ba ay ang parehong kaganapan? O ito ba ay isang hiwalay na palitan na nararapat sa sarili nitong paksa?

Malamang, sila ay magkapareho, at ang tukso na gumawa ng isang paksa ay hindi walang batayan, dahil ang isang hiwalay na paksa ay nangangahulugang magkahiwalay na mga mamimili, magkahiwalay na mga config, isang hiwalay na henerasyon ng lahat ng ito. Ngunit hindi isang katotohanan.

Bagong field o bagong kaganapan?

Ngunit kung gagamitin mo ang parehong mga kaganapan, pagkatapos ay lumitaw ang isa pang problema. Halimbawa, hindi lahat ng delivery system ay makakabuo ng uri ng DTO na maaaring buuin ng BOB. Ipinapadala namin sa kanila ang id, ngunit hindi nila ito sine-save dahil hindi nila ito kailangan, at mula sa punto ng view ng pagsisimula ng proseso ng event-bus, kinakailangan ang field na ito.

Kung magpapakilala kami ng panuntunan para sa event-bus na kinakailangan ang field na ito, mapipilitan kaming magtakda ng mga karagdagang panuntunan sa pagpapatunay sa BOB o sa starter ng event. Ang pagpapatunay ay nagsisimulang kumalat sa buong serbisyo - ito ay hindi masyadong maginhawa.

Ang isa pang problema ay ang tukso sa incremental development. Sinasabi sa amin na may kailangang idagdag sa kaganapan, at marahil, kung iisipin natin, dapat itong maging isang hiwalay na kaganapan. Ngunit sa aming pamamaraan, ang isang hiwalay na kaganapan ay isang hiwalay na paksa. Ang isang hiwalay na paksa ay ang buong proseso na inilarawan ko sa itaas. Natutukso ang developer na magdagdag lang ng isa pang field sa JSON schema at muling buuin ito.

Sa kaso ng mga refund, nakarating kami sa kaganapan ng mga kaganapan sa kalahating taon. Nagkaroon kami ng isang meta-event na tinatawag na pag-update ng refund, na mayroong field ng uri na naglalarawan kung ano talaga ang update na ito. Dahil dito, nagkaroon kami ng "kahanga-hangang" switch na may mga validator na nagsabi sa amin kung paano i-validate ang kaganapang ito sa ganitong uri.

Pag-bersyon ng kaganapan

Upang mapatunayan ang mga mensahe sa Kafka maaari mong gamitin euro, ngunit ito ay kinakailangan upang agad na ilagay ito at gamitin ang Confluent. Sa aming kaso, kailangan naming maging maingat sa bersyon. Hindi palaging posible na muling basahin ang mga mensahe mula sa replication log dahil ang modelo ay "umalis". Karaniwan, ito ay lumiliko upang bumuo ng mga bersyon upang ang modelo ay pabalik na katugma: halimbawa, gumawa ng isang field na pansamantalang opsyonal. Kung ang mga pagkakaiba ay masyadong malakas, magsisimula kaming magsulat sa isang bagong paksa, at ilipat ang mga kliyente kapag natapos na nilang basahin ang luma.

Garantisadong pagkakasunud-sunod ng pagbabasa ng mga partisyon

Ang mga paksa sa loob ng Kafka ay nahahati sa mga partisyon. Hindi ito masyadong mahalaga habang nagdidisenyo kami ng mga entity at palitan, ngunit mahalaga ito kapag nagpapasya kung paano ito ubusin at sukatin.

Sa karaniwang kaso, sumulat ka ng isang paksa sa Kafka. Bilang default, isang partition ang ginagamit, at lahat ng mensahe sa paksang ito ay mapupunta dito. At dahil dito, binabasa ng mamimili ang mga mensaheng ito nang sunud-sunod. Sabihin nating ngayon kailangan nating palawakin ang sistema upang ang mga mensahe ay mabasa ng dalawang magkaibang mga mamimili. Kung, halimbawa, nagpapadala ka ng SMS, maaari mong sabihin sa Kafka na gumawa ng karagdagang partisyon, at magsisimulang hatiin ng Kafka ang mga mensahe sa dalawang bahagi - kalahati dito, kalahati dito.

Paano sila hinati ni Kafka? Ang bawat mensahe ay may katawan (kung saan iniimbak namin ang JSON) at isang susi. Maaari kang mag-attach ng hash function sa key na ito, na tutukuyin kung saang partition papasok ang mensahe.

Sa aming kaso sa mga refund, ito ay mahalaga, kung kukuha kami ng dalawang partisyon, pagkatapos ay may pagkakataon na ang isang parallel na mamimili ay magpoproseso ng pangalawang kaganapan bago ang una at magkakaroon ng problema. Tinitiyak ng hash function na ang mga mensahe na may parehong key ay mapupunta sa parehong partition.

Mga kaganapan kumpara sa mga utos

Ito ay isa pang problema na aming naranasan. Ang kaganapan ay isang partikular na kaganapan: sinasabi namin na may nangyari sa isang lugar (something_happened), halimbawa, nakansela ang isang item o may naganap na refund. Kung may nakikinig sa mga kaganapang ito, ayon sa "nakansela ang item," gagawin ang entity ng refund, at isusulat ang "naganap na refund" sa isang lugar sa mga setup.

Ngunit kadalasan, kapag nagdidisenyo ka ng mga kaganapan, hindi mo nais na isulat ang mga ito nang walang kabuluhan - umaasa ka sa katotohanang may magbabasa nito. May mataas na tukso na magsulat ng hindi something_happened (item_canceled, refund_refunded), ngunit something_should_be_done. Halimbawa, handa nang ibalik ang item.

Sa isang banda, iminumungkahi nito kung paano gagamitin ang kaganapan. Sa kabilang banda, ito ay parang isang normal na pangalan ng kaganapan. Bukod dito, hindi malayo dito ang do_something command. Ngunit wala kang garantiya na may nagbabasa ng kaganapang ito; at kung nabasa mo ito, pagkatapos ay matagumpay mong nabasa; at kung matagumpay mong nabasa ito, may ginawa ka, at matagumpay ang isang bagay. Sa sandaling ang isang kaganapan ay maging isang bagay, kailangan ang feedback, at iyon ay isang problema.

Karanasan sa pagbuo ng serbisyo ng Refund Tool na may asynchronous na API sa Kafka

Sa asynchronous exchange sa RabbitMQ, kapag nabasa mo ang mensahe, pumunta sa http, mayroon kang tugon - hindi bababa sa natanggap ang mensahe. Kapag sumulat ka sa Kafka, mayroong isang mensahe na isinulat mo sa Kafka, ngunit wala kang alam tungkol sa kung paano ito naproseso.

Samakatuwid, sa aming kaso, kinailangan naming magpakilala ng kaganapan sa pagtugon at mag-set up ng pagsubaybay upang kung napakaraming mga kaganapan ang ipinadala, pagkatapos ng ganoon at ganoong oras ay dapat dumating ang parehong bilang ng mga kaganapan sa pagtugon. Kung hindi ito mangyayari, kung gayon parang may mali. Halimbawa, kung ipinadala namin ang kaganapang "item_ready_to_refund", inaasahan namin na gagawa ng refund, ibabalik ang pera sa kliyente, at ipapadala sa amin ang event na "money_refund". Ngunit hindi ito tiyak, kaya kailangan ang pagsubaybay.

Nuances

Mayroong isang medyo halatang problema: kung magbasa ka mula sa isang paksa nang sunud-sunod, at mayroon kang ilang masamang mensahe, babagsak ang mamimili, at hindi ka na lalayo pa. Kailangan mo itigil ang lahat ng mga mamimili, mag-commit offset pa upang magpatuloy sa pagbabasa.

Alam namin ang tungkol dito, umaasa kami dito, ngunit nangyari ito. At nangyari ito dahil ang kaganapan ay wasto mula sa punto ng view ng mga kaganapan-bus, ang kaganapan ay wasto mula sa punto ng view ng validator ng aplikasyon, ngunit ito ay hindi wasto mula sa punto ng view ng PostgreSQL, dahil sa aming isang sistema MySQL na may UNSIGNED INT, at sa bagong nakasulat na sistema ay may PostgreSQL lamang sa INT. Ang kanyang sukat ay medyo mas maliit, at ang Id ay hindi kasya. Namatay si Symfony na may pagbubukod. Siyempre, nakuha namin ang pagbubukod dahil umasa kami dito, at gagawin namin ang offset na ito, ngunit bago iyon gusto naming dagdagan ang counter ng problema, dahil hindi matagumpay na naproseso ang mensahe. Ang mga counter sa proyektong ito ay nasa database din, at isinara na ng Symfony ang komunikasyon sa database, at ang pangalawang pagbubukod ay pinatay ang buong proseso nang walang pagkakataon na gumawa ng offset.

Ang serbisyo ay humiga nang ilang oras - sa kabutihang-palad, kasama si Kafka hindi ito masama, dahil nananatili ang mga mensahe. Kapag naibalik ang trabaho, maaari mong tapusin ang pagbabasa ng mga ito. Ito ay komportable.

Ang Kafka ay may kakayahang magtakda ng arbitrary na offset sa pamamagitan ng tooling. Ngunit upang gawin ito, kailangan mong ihinto ang lahat ng mga mamimili - sa aming kaso, maghanda ng isang hiwalay na paglabas kung saan walang mga mamimili, muling pag-deploy. Pagkatapos sa Kafka maaari mong ilipat ang offset sa pamamagitan ng tooling, at mapupunta ang mensahe.

Isa pang nuance - replication log vs rdkafka.so - ay nauugnay sa mga detalye ng aming proyekto. Gumagamit kami ng PHP, at sa PHP, bilang panuntunan, lahat ng mga aklatan ay nakikipag-ugnayan sa Kafka sa pamamagitan ng rdkafka.so repository, at pagkatapos ay mayroong ilang uri ng wrapper. Marahil ito ang ating mga personal na paghihirap, ngunit lumabas na ang simpleng pagbabasa ng isang piraso ng nabasa na natin ay hindi ganoon kadali. Sa pangkalahatan, may mga problema sa software.

Pagbabalik sa mga detalye ng pagtatrabaho sa mga partisyon, nakasulat ito mismo sa dokumentasyon mga mamimili >= mga partisyon ng paksa. Ngunit nalaman ko ang tungkol dito nang mas huli kaysa sa gusto ko. Kung gusto mong mag-scale at magkaroon ng dalawang consumer, kailangan mo ng hindi bababa sa dalawang partition. Iyon ay, kung mayroon kang isang partition kung saan 20 libong mga mensahe ang naipon, at gumawa ka ng bago, ang bilang ng mga mensahe ay hindi mapantayan sa lalong madaling panahon. Samakatuwid, upang magkaroon ng dalawang parallel na mga mamimili, kailangan mong harapin ang mga partisyon.

Pagsubaybay

Sa palagay ko ang paraan ng pagsubaybay natin ay magiging mas malinaw kung ano ang mga problema sa umiiral na diskarte.

Halimbawa, kinakalkula namin kung gaano karaming mga produkto sa database ang kamakailang nagbago ng kanilang katayuan, at, nang naaayon, dapat na nangyari ang mga kaganapan batay sa mga pagbabagong ito, at ipinapadala namin ang numerong ito sa aming sistema ng pagsubaybay. Pagkatapos ay mula sa Kafka nakuha namin ang pangalawang numero, kung gaano karaming mga kaganapan ang aktwal na naitala. Malinaw, ang pagkakaiba sa pagitan ng dalawang numerong ito ay dapat palaging zero.

Karanasan sa pagbuo ng serbisyo ng Refund Tool na may asynchronous na API sa Kafka

Bilang karagdagan, kailangan mong subaybayan kung paano gumagana ang producer, kung ang mga kaganapan-bus ay nakatanggap ng mga mensahe, at kung ano ang ginagawa ng mamimili. Halimbawa, sa mga chart sa ibaba, gumagana nang maayos ang Refund Tool, ngunit malinaw na may ilang problema ang BOB (mga asul na taluktok).

Karanasan sa pagbuo ng serbisyo ng Refund Tool na may asynchronous na API sa Kafka

Nabanggit ko na ang consumer-group lag. Sa madaling salita, ito ang bilang ng mga hindi pa nababasang mensahe. Sa pangkalahatan, mabilis na gumagana ang aming mga consumer, kaya ang lag ay karaniwang 0, ngunit kung minsan ay maaaring magkaroon ng panandaliang peak. Magagawa ito ng Kafka sa labas ng kahon, ngunit kailangan mong magtakda ng isang tiyak na agwat.

May project lunggana magbibigay sa iyo ng karagdagang impormasyon sa Kafka. Ginagamit lang nito ang consumer-group API para ibigay ang status kung paano gumagana ang grupong ito. Bilang karagdagan sa OK at Nabigo, mayroong isang babala, at maaari mong malaman na ang iyong mga mamimili ay hindi makayanan ang bilis ng produksyon - wala silang oras upang i-proofread kung ano ang nakasulat. Ang sistema ay medyo matalino at madaling gamitin.

Karanasan sa pagbuo ng serbisyo ng Refund Tool na may asynchronous na API sa Kafka

Ito ang hitsura ng tugon ng API. Narito ang grupong bob-live-fifa, partition refund.update.v1, status OK, lag 0 - ang huling huling offset ng ganito at ganoon.

Karanasan sa pagbuo ng serbisyo ng Refund Tool na may asynchronous na API sa Kafka

Pagsubaybay updated_at SLA (stuck) nabanggit ko na. Halimbawa, ang produkto ay nagbago sa katayuan na handa na itong ibalik. Nag-install kami ng Cron, na nagsasabing kung sa loob ng 5 minuto ang bagay na ito ay hindi napunta sa pag-refund (nagbabalik kami ng pera sa pamamagitan ng mga sistema ng pagbabayad nang napakabilis), kung gayon ang isang bagay ay tiyak na nagkamali, at ito ay tiyak na isang kaso para sa suporta. Samakatuwid, kinuha lang namin ang Cron, na nagbabasa ng mga ganoong bagay, at kung mas malaki sila sa 0, pagkatapos ay nagpapadala ito ng alerto.

Upang ibuod, ang paggamit ng mga kaganapan ay maginhawa kapag:

  • impormasyon ay kailangan ng ilang mga sistema;
  • ang resulta ng pagproseso ay hindi mahalaga;
  • may kaunting mga kaganapan o maliliit na kaganapan.

Mukhang ang artikulo ay may isang napaka-tiyak na paksa - asynchronous na API sa Kafka, ngunit kaugnay nito nais kong magrekomenda ng maraming bagay nang sabay-sabay.
Una, susunod HighLoad++ kailangan nating maghintay hanggang Nobyembre; sa Abril magkakaroon ng bersyon ng St. Petersburg, at sa Hunyo ay pag-uusapan natin ang tungkol sa mataas na pagkarga sa Novosibirsk.
Pangalawa, ang may-akda ng ulat, si Sergei Zaika, ay miyembro ng Komite ng Programa ng aming bagong kumperensya sa pamamahala ng kaalaman KnowledgeConf. Ang kumperensya ay isang araw, magaganap sa Abril 26, ngunit ang programa nito ay napakatindi.
At ito ay sa Mayo PHP Russia ΠΈ RIT++ (kasama ang DevOpsConf) - maaari mo ring imungkahi ang iyong paksa doon, pag-usapan ang iyong karanasan at magreklamo tungkol sa iyong mga stuffed cone.

Pinagmulan: www.habr.com

Magdagdag ng komento