{"id":56186,"date":"2020-02-06T00:00:00","date_gmt":"2020-02-05T21:00:00","guid":{"rendered":"https:\/\/prohoster.info\/blog\/blog_prohoster\/povtornaya-obrabotka-sobytij-poluchennyh-iz-kafka"},"modified":"2020-02-18T14:04:24","modified_gmt":"2020-02-18T11:04:24","slug":"povtornaya-obrabotka-sobytij-poluchennyh-iz-kafka","status":"publish","type":"post","link":"https:\/\/prohoster.info\/et\/blog\/administrirovanie\/povtornaya-obrabotka-sobytij-poluchennyh-iz-kafka","title":{"rendered":"S\u00fcndmuste uuesti t\u00f6\u00f6tlemine, mis on saadud Kafka'st","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><img decoding=\"async\" alt=\"S\u00fcndmuste uuesti t\u00f6\u00f6tlemine, mis on saadud Kafka&#039;st\" src=\"\/wp-content\/uploads\/2020\/02\/d04319691af5c5de40ccfe4a2268558c.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Tere, Habr.<\/p>\n<p><\/p>\n<p>Hiljuti jagasin ma <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/tinkoff\/blog\/481784\/\">oma kogemust<\/a><\/noindex> selle kohta, milliseid parameetreid me tiimis k\u00f5ige sagedamini kasutame Kafka Producentide ja Tarbijate jaoks, et saavutada garanteeritud kohaletoimetamine. Selles artiklis tahan r\u00e4\u00e4kida, kuidas me korraldasime s\u00fcndmuse uuesti t\u00f6\u00f6tlemise, mis saadi Kafka'st, v\u00e4lise s\u00fcsteemi ajutise k\u00e4ttesaamatuse t\u00f5ttu.<\/p>\n<p><\/p>\n<p>Kaasaegsed rakendused t\u00f6\u00f6tavad v\u00e4ga keerulises keskkonnas. \u00c4rilogika, mis on m\u00e4hitud kaasaegsesse tehnoloogilisse kihti, t\u00f6\u00f6tab Docker'i pildis, mida haldab koordineerija nagu Kubernetes v\u00f5i OpenShift, ning suhtleb teiste rakenduste v\u00f5i ettev\u00f5tte lahendustega l\u00e4bi f\u00fc\u00fcsiliste ja virtuaalsete marsruuterite ahela. Sellises keskkonnas v\u00f5ib alati midagi katki minna, seet\u00f5ttu on s\u00fcndmuste uuesti t\u00f6\u00f6tlemine juhul, kui m\u00f5ni v\u00e4line s\u00fcsteem on k\u00e4ttesaamatu, meie \u00e4ri protsesside oluline osa.<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2 id=\"kak-bylo-do-kafka\">Kuidas oli enne Kafka<\/h2>\n<p><\/p>\n<p>Varem kasutasime projektis IBM MQ as\u00fcnkroonseks s\u00f5numite edastamiseks. Teenuse t\u00f6\u00f6 k\u00e4igus tekkinud vea korral v\u00f5is saadud s\u00f5num paigutada sisemise surnud s\u00f5numite j\u00e4rjekorda (DLQ) edasiseks k\u00e4sitlemiseks. DLQ loodi siseneva j\u00e4rjekorra k\u00f5rvale, s\u00f5numi edasiviimine toimus IBM MQ sees. <\/p>\n<p><\/p>\n<p>Kui viga oli ajutine ja me suudame selle tuvastada (n\u00e4iteks ResourceAccessException HTTP-k\u00f5ne k\u00e4igus v\u00f5i MongoTimeoutException MongoDb p\u00e4ringus), siis rakendati korduste strateegiat. S\u00f5ltumata rakenduse loogika harudest, paigutati algne s\u00f5num kas s\u00fcsteemij\u00e4rjekorda edasil\u00fckatud saatmiseks v\u00f5i eraldi rakendusse, mis kunagi ammu loodi s\u00f5numite uuesti saatmiseks. Samal ajal kirjutatakse s\u00f5numi p\u00e4isesse saadetiste korduse number, mis on seotud viivituse intervalliga v\u00f5i rakenduse taseme strateegia l\u00f5puga. Kui oleme j\u00f5udnud strateegia l\u00f5ppu, kuid v\u00e4line s\u00fcsteem on endiselt k\u00e4ttesaamatu, siis paigutatakse s\u00f5num DLQ-sse k\u00e4sitlemiseks.<\/p>\n<p><\/p>\n<h2 id=\"poisk-resheniya\">Lahenduse otsing<\/h2>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/www.google.com\/search?q=kafka+retry+message\">Otsides internetist<\/a><\/noindex>, v\u00f5ib leida j\u00e4rgmist <noindex><a rel=\"nofollow\" href=\"https:\/\/blog.pragmatists.com\/retrying-consumer-architecture-in-the-apache-kafka-939ac4cb851a\">otsuse<\/a><\/noindex>. L\u00fchidalt \u00f6eldes pakutakse v\u00e4lja iga teema jaoks luua topikud igaks viivituse intervalliks ja rakendada rakenduse poolel Tarbijad, kes loevad s\u00f5numeid vajaliku viivitusega. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"S\u00fcndmuste uuesti t\u00f6\u00f6tlemine, mis on saadud Kafka&#039;st\" src=\"\/wp-content\/uploads\/2020\/02\/718c4c3f71ccf95b5a6f60e1b2c685d2.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Hoolimata paljusid positiivsetest \u00fclevaadetest, n\u00e4ib see mulle mitte just k\u00f5ige parem. Esiteks seet\u00f5ttu, et arendajal tuleb peale \u00e4rin\u00f5uete rakendamise kulutada palju aega ka kirjeldatud mehhanismi rakendamisele.<\/p>\n<p><\/p>\n<p>Lisaks, kui Kafka-klastris on aktiveeritud juurdep\u00e4\u00e4sude haldamine, peab kulutama aega teemade loomisele ja nendele vajalike juurdep\u00e4\u00e4sude tagamisele. Sellele lisandub vajadus leida iga retry-teema jaoks \u00f5ige retention.ms parameeter, et s\u00f5numid j\u00f5uaksid uuesti saata ja ei kaoks. Juhtimise ja juurdep\u00e4\u00e4sutaotluse rakendamine tuleb korrata iga olemasoleva v\u00f5i uue teenuse jaoks.<\/p>\n<p><\/p>\n<p>Vaatame n\u00fc\u00fcd, milliseid mehhanisme s\u00f5numite uuesti t\u00f6\u00f6tlemiseks pakub meile Spring tervikuna ja spring-kafka eelk\u00f5ige. Spring-kafka-l on \u00fclekantav s\u00f5ltuvus spring-retry'st, mis pakub abstraheerimisv\u00f5imalusi erinevate BackOffPolicy haldamiseks. See on \u00fcsna paindlik t\u00f6\u00f6riist, kuid selle oluline puudus on see, et s\u00f5numid uuesti saatmiseks salvestatakse rakenduse m\u00e4llu. See t\u00e4hendab, et rakenduse taask\u00e4ivitamine seoses uuendamise v\u00f5i t\u00f6\u00f6ea jooksul tekkinud veaga toob kaasa k\u00f5ikide uuesti t\u00f6\u00f6tlemist ootavate s\u00f5numite kadumise. Kuna see punkt on meie s\u00fcsteemi jaoks kriitiline, ei kaalunud me seda edasi.<\/p>\n<p><\/p>\n<p>Spring-kafka ise pakub mitmeid ContainerAwareErrorHandler'i rakendusi, n\u00e4iteks <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/spring-projects\/spring-kafka\/blob\/master\/spring-kafka\/src\/main\/java\/org\/springframework\/kafka\/listener\/SeekToCurrentErrorHandler.java\">SeekToCurrentErrorHandler<\/a><\/noindex>, millega saab, mitte nihutades offsetit vea tekkimisel, s\u00f5numi hiljem t\u00f6\u00f6delda. Alates versioonist spring-kafka 2.3 on v\u00f5imalik m\u00e4\u00e4rata BackOffPolicy.<\/p>\n<p><\/p>\n<p>See l\u00e4henemine v\u00f5imaldab uuesti t\u00f6\u00f6deldavatel s\u00f5numitel ellu j\u00e4\u00e4da rakenduse taask\u00e4ivitamise, kuid DLQ mehhanism on endiselt puudulik. Just selle variandi valisime 2019. aasta alguses, optimistlikult pidades, et DLQ-d ei tule vaja (meil vedas ja t\u00f5epoolest ei olnud seda mitu kuud rakenduse sellise uuesti t\u00f6\u00f6tlemise s\u00fcsteemiga). Ajutised vead p\u00f5hjustasid SeekToCurrentErrorHandler'i aktiveerimist. \u00dclej\u00e4\u00e4nud vead kirjutatakse logisse, p\u00f5hjustavad offseti nihkumist ja t\u00f6\u00f6tlemine j\u00e4tkub j\u00e4rgmise s\u00f5numiga.<\/p>\n<p><\/p>\n<h2 id=\"itogovoe-reshenie\">L\u00f5plik lahendus<\/h2>\n<p><\/p>\n<p>SeekToCurrentErrorHandler'ile p\u00f5hinev rakendus viitis meid arendada oma mehhanismi s\u00f5numite edastamiseks uuesti.<\/p>\n<p><\/p>\n<p>Esiteks soovisime kasutada juba olemasolevat teadmist ja laiendada seda rakenduse loogika p\u00f5hjal. Lineaarse loogikaga rakenduse jaoks oleks optimaalne peatada uute s\u00f5numite lugemine l\u00fchikeseks ajaks, mis on m\u00e4\u00e4ratud uuesti k\u00f5nede strateegia raames. \u00dclej\u00e4\u00e4nud rakenduste puhul soovisime omada \u00fchte punkti, mis tagaks uuesti k\u00f5nede strateegia elluviimise. Lisaks peaks see \u00fchtne punkt omama DLQ funktsionaalsust m\u00f5lema l\u00e4henemise jaoks.<\/p>\n<p><\/p>\n<p>Uuesti k\u00f5nede strateegia peaks olema salvestatud rakenduses, mis vastutab j\u00e4rgmise intervalli saamise eest ajutiste vigade korral.<\/p>\n<p><\/p>\n<h3 id=\"ostanovka-consumera-dlya-prilozheniya-s-lineynoy-logikoy\">Consumer'i peatamine lineaarse loogikaga rakenduse jaoks<\/h3>\n<p><\/p>\n<p>Spring-kafka kasutamise korral v\u00f5ib Consumer'i peatamise kood v\u00e4lja n\u00e4ha nii:<\/p>\n<p><\/p>\n<pre><code class=\"java\">public void pauseListenerContainer(MessageListenerContainer listenerContainer, \n                                   Instant retryAt) {\n        if (nonNull(retryAt) &amp;&amp; listenerContainer.isRunning()) {\n            listenerContainer.stop();\n            taskScheduler.schedule(() -&gt; listenerContainer.start(), retryAt);\n            return;\n        }\n        \/\/ DLQ-le\n    }<\/code><\/pre>\n<p><\/p>\n<p>N\u00e4ites on retryAt aeg, millal tuleks uuesti k\u00e4ivitada MessageListenerContainer, kui see veel t\u00f6\u00f6tab. Uuesti k\u00e4ivitamine toimub eraldi l\u00f5imes, mis on k\u00e4ivitatud TaskScheduler'is, mille rakenduse pakub samuti spring. <\/p>\n<p><\/p>\n<p>L\u00f6ime retryAt v\u00e4\u00e4rtuse j\u00e4rgmise meetodi abil:<\/p>\n<p><\/p>\n<ol>\n<li>Otsitakse uuesti k\u00f5nede arvu v\u00e4\u00e4rtust.<\/li>\n<li>Vastavalt uuesti k\u00f5nede arvu v\u00e4\u00e4rtusele otsitakse praegune viivituse intervall uuesti k\u00f5nede strateegias. Strateegia kuulutatakse v\u00e4lja rakenduses, mille salvestamiseks valisime JSON formaadi.<\/li>\n<li>JSON-massis leitud intervall sisaldab sekundeid, mille jooksul tuleb t\u00f6\u00f6tlemine uuesti korrata. See sekundite arv liidetakse praegusele ajale, moodustades retryAt v\u00e4\u00e4rtuse.<\/li>\n<li>Kui intervalli ei leita, siis retryAt v\u00e4\u00e4rtus on null ja s\u00f5num saadetakse DLQ-sse k\u00e4sitsi t\u00f6\u00f6tlemiseks.<\/li>\n<\/ol>\n<p><\/p>\n<p>Selle l\u00e4henemise puhul on vajalik s\u00e4ilitada igas s\u00f5numis, mis on praegu t\u00f6\u00f6tlemisel, korduvate katsete arv, n\u00e4iteks rakenduse m\u00e4lu. Katsete arvu s\u00e4ilitamine m\u00e4lus ei ole kriitiline, kuna lineaarse loogikaga rakendus ei pruugi kogu t\u00f6\u00f6tlemist korraldada. Erinevalt spring-retry'st ei too rakenduse taask\u00e4ivitamine kaasa k\u00f5igi s\u00f5numite kadumist uuesti t\u00f6\u00f6tlemiseks, vaid lihtsalt strateegia taask\u00e4ivitamise. <\/p>\n<p><\/p>\n<p>See l\u00e4henemine aitab v\u00e4hendada koormust v\u00e4lisele s\u00fcsteemile, mis v\u00f5ib olla v\u00e4ga suure koormuse t\u00f5ttu k\u00e4ttesaamatu. Teisis\u00f5nu, lisaks uuesti t\u00f6\u00f6tlemisele oleme saavutanud mustri elluviimise. <noindex><a rel=\"nofollow\" href=\"https:\/\/microservices.io\/patterns\/reliability\/circuit-breaker.html\">circuit breaker<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p>Meie puhul on veapiirang vaid 1, ja et minimeerida s\u00fcsteemi seiskumist ajutiste v\u00f5rgu katkestuste t\u00f5ttu, kasutame v\u00e4ga granulaarses korduste strateegias l\u00fchikesi viivitusintervalle. See v\u00f5ib mitte sobida k\u00f5igile ettev\u00f5tte grupi rakendustele, seet\u00f5ttu tuleb veapiirangu ja intervalli suuruse suhet valida, tuginedes s\u00fcsteemi omadustele.<\/p>\n<p><\/p>\n<h3 id=\"otdelnoe-prilozhenie-dlya-obrabotki-soobscheniy-ot-prilozheniy-s-nedeterminirovannoy-logikoy\">Eraldi rakendus s\u00f5numite t\u00f6\u00f6tlemiseks rakendustelt, millel on m\u00e4\u00e4ramatud loogikad.<\/h3>\n<p><\/p>\n<p>Siin on n\u00e4ide koodist, mis saadab s\u00f5numi sellesse rakendusse (Retryer), mis t\u00e4idab uuesti saatmise DESTINATION teema juurde, kui RETRY_AT aeg on k\u00e4es:<\/p>\n<p><\/p>\n<pre><code class=\"java\">\npublic  void retry(ConsumerRecord record, String retryToTopic, \n                         Instant retryAt, String counter, String groupId, Exception e) {\n        Headers headers = ofNullable(record.headers()).orElse(new RecordHeaders());\n        List<Header> arrayOfHeaders = \n            new ArrayList(Arrays.asList(headers.toArray()));\n        updateHeader(arrayOfHeaders, GROUP_ID, groupId::getBytes);\n        updateHeader(arrayOfHeaders, DESTINATION, retryToTopic::getBytes);\n        updateHeader(arrayOfHeaders, ORIGINAL_PARTITION, \n                     () -&gt; Integer.toString(record.partition()).getBytes());\n        if (nonNull(retryAt)) {\n            updateHeader(arrayOfHeaders, COUNTER, counter::getBytes);\n            updateHeader(arrayOfHeaders, SEND_TO, \"retry\"::getBytes);\n            updateHeader(arrayOfHeaders, RETRY_AT, retryAt.toString()::getBytes);\n        } else {\n            updateHeader(arrayOfHeaders, REASON, \n                         ExceptionUtils.getStackTrace(e)::getBytes);\n            updateHeader(arrayOfHeaders, SEND_TO, \"backout\"::getBytes);\n        }\n        ProducerRecord messageToSend =\n            new ProducerRecord(retryTopic, null, null, record.key(), record.value(), arrayOfHeaders);\n        kafkaTemplate.send(messageToSend);\n    }<\/code><\/pre>\n<p><\/p>\n<p>N\u00e4idatud n\u00e4itest on n\u00e4ha, et palju teavet edastatakse pealkirjades. RETRY_AT v\u00e4\u00e4rtus on samuti sama, mis Consumer\u2019i peatamise kordusmehhanismi puhul. Lisaks DESTINATIONile ja RETRY_AT-ile edastame:<\/p>\n<p><\/p>\n<ul>\n<li>GRUP_ID, mille kaudu r\u00fchmitame s\u00f5numid k\u00e4sitsi anal\u00fc\u00fcsimiseks ja otsingu lihtsustamiseks.<\/li>\n<li>ALGE_PARTITSIOON, et p\u00fcsida sama Consumeri juures \u00fcmber t\u00f6\u00f6tlemiseks. See parameeter v\u00f5ib olla null, sel juhul omandatakse uus partitsioon originaals\u00f5numi record.key() v\u00f5tme j\u00e4rgi.<\/li>\n<li>Uuendatud COUNTER v\u00e4\u00e4rtus, et j\u00e4rgida korduvate k\u00f5nede strateegiat.<\/li>\n<li>SEND_TO \u2014 konstant, mis n\u00e4itab, kas s\u00f5num saata korduvaks t\u00f6\u00f6tlemiseks RETRY_AT saavutatuna v\u00f5i paigutada DLQ-sse.<\/li>\n<li>P\u00d5HJUS \u2014 p\u00f5hjus, miks s\u00f5numi t\u00f6\u00f6tlemine katkestati.<\/li>\n<\/ul>\n<p><\/p>\n<p>Retryer salvestab s\u00f5numeid korduvaks saatmiseks ja k\u00e4sitsi anal\u00fc\u00fcsimiseks PostgreSQL-is. Ajastatult k\u00e4ivitatakse \u00fclesanne, mis leiab s\u00f5numid, mille RETRY_AT on saabunud, ja saadab need tagasi originaalsesse partitsiooni DESTINATION teemas record.key() v\u00f5tmega.<\/p>\n<p><\/p>\n<p>P\u00e4rast s\u00f5numi saatmist eemaldatakse need PostgreSQL-ist. S\u00f5numite k\u00e4sitsi anal\u00fc\u00fcs toimub lihtsas kasutajaliideses, mis suhtleb Retryeriga REST API kaudu. Selle peamised omadused on s\u00f5numite uuesti saatmine v\u00f5i DLQ-st kustutamine, vigade teabe vaatamine ja s\u00f5numite otsimine, n\u00e4iteks veateate j\u00e4rgi. <\/p>\n<p><\/p>\n<p>Kuna meie klastrites on sisse l\u00fclitatud juurdep\u00e4\u00e4su haldamine, tuleb t\u00e4iendavalt taotleda juurdep\u00e4\u00e4su teemale, mida kuulab Retryer, ja anda Retryerile v\u00f5imalus kirjutada DESTINATION teemas. See on ebamugav, kuid erinevalt ajavahemikus p\u00f5hinevast l\u00e4henemisest on meil t\u00e4ielik DLQ ja UI selle haldamiseks.<\/p>\n<p><\/p>\n<p>On juhtumeid, kus sisenemise teemat loevad erinevad consumer-grupid, mille rakendused rakendavad erinevat loogikat. S\u00f5numi korduv t\u00f6\u00f6tlemine Retryeri kaudu \u00fche sellise rakenduse jaoks p\u00f5hjustab teise rakenduse duplikaadi. Selle kaitsmiseks loome eraldi teema korduvaks t\u00f6\u00f6tlemiseks. Sisenemis- ja retry-teemat v\u00f5ib lugeda sama Consumeriga piiramatu arv kordi. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"S\u00fcndmuste uuesti t\u00f6\u00f6tlemine, mis on saadud Kafka&#039;st\" src=\"\/wp-content\/uploads\/2020\/02\/06f2355839c6dd812ee3304bf2c72226.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Vaikimisi ei paku see l\u00e4henemine circuit breaker\u2019i v\u00f5imalust, kuid seda saab rakendusele lisada <noindex><a rel=\"nofollow\" href=\"https:\/\/spring.io\/projects\/spring-cloud-netflix\">spring-cloud-netflix<\/a><\/noindex> v\u00f5i uue <noindex><a rel=\"nofollow\" href=\"https:\/\/spring.io\/projects\/spring-cloud-circuitbreaker\">spring cloud circuit breaker<\/a><\/noindex>, \u00fcmbritsedes v\u00e4liste teenuste kutsumise kohti vastava abstraktsiooniga. Lisaks ilmneb v\u00f5imalus valida strateegia <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/architecture\/patterns\/bulkhead\">bulkhead<\/a><\/noindex> mustri jaoks, mis v\u00f5ib samuti kasulik olla. N\u00e4iteks spring-cloud-netflixis v\u00f5ib see olla niidi bassein v\u00f5i semafor.<\/p>\n<p><\/p>\n<h2 id=\"vyvod\">Kokkuv\u00f5te<\/h2>\n<p><\/p>\n<p>Kuna tulemuseks on meil eraldi rakendus, mis v\u00f5imaldab s\u00f5numi t\u00f6\u00f6tlemist korrata, kui m\u00f5ni v\u00e4line s\u00fcsteem on ajutiselt k\u00e4ttesaamatu.<\/p>\n<p><\/p>\n<p>Rakenduse \u00fcks peamisi eeliseid on see, et seda saavad kasutada v\u00e4list s\u00fcsteemi, mis t\u00f6\u00f6tab samas Kafka-klistris, ilma oluliste muudatusteta oma poolel! Selline rakendus peab vaid saama juurdep\u00e4\u00e4su retry-teemale, t\u00e4itma m\u00f5ned Kafka-pealkirjad ja saatma s\u00f5numi Retryerisse. Ei ole vaja luua t\u00e4iendavat infrastruktuuri. Ja et v\u00e4hendada s\u00f5numite edasisaatmist rakendusest Retryerisse ja tagasi, oleme eraldanud rakendused, millel on lineaarsed loogikad, ja viime nende kaudu kordust\u00f6\u00f6tlemise l\u00e4bi Consumeri peatamise.<\/p>\n<p>Allikas: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/tinkoff\/blog\/487094\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440. \u041d\u0435\u0434\u0430\u0432\u043d\u043e \u044f \u043f\u043e\u0434\u0435\u043b\u0438\u043b\u0441\u044f \u043e\u043f\u044b\u0442\u043e\u043c \u043e \u0442\u043e\u043c, \u043a\u0430\u043a\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043c\u044b \u0432 \u043a\u043e\u043c\u0430\u043d\u0434\u0435 \u0447\u0430\u0449\u0435 \u0432\u0441\u0435\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0434\u043b\u044f Kafka Producer \u0438 Consumer, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0438\u0431\u043b\u0438\u0437\u0438\u0442\u044c\u0441\u044f \u043a \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0439 \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0435. \u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u0445\u043e\u0447\u0443 \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u0442\u044c, \u043a\u0430\u043a \u043c\u044b \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u0430\u043b\u0438 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u0443\u044e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0441\u043e\u0431\u044b\u0442\u0438\u044f, \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u043e\u0433\u043e \u0438\u0437 Kafka, \u0432 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0441\u0442\u0438 \u0432\u043d\u0435\u0448\u043d\u0435\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b. \u0421\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u0432 \u043e\u0447\u0435\u043d\u044c \u0441\u043b\u043e\u0436\u043d\u043e\u0439 \u0441\u0440\u0435\u0434\u0435. \u0411\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0430, \u043e\u0431\u0435\u0440\u043d\u0443\u0442\u0430\u044f [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-56186","post","type-post","status-publish","format-standard","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.1.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440. \u041d\u0435\u0434\u0430\u0432\u043d\u043e.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/et\/blog\/administrirovanie\/povtornaya-obrabotka-sobytij-poluchennyh-iz-kafka\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"et_EE\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0441\u043e\u0431\u044b\u0442\u0438\u0439, \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0445 \u0438\u0437 Kafka | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440. \u041d\u0435\u0434\u0430\u0432\u043d\u043e.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/et\/blog\/administrirovanie\/povtornaya-obrabotka-sobytij-poluchennyh-iz-kafka\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2020-02-05T21:00:00+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-02-18T11:04:24+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Kordust\u00f6\u00f6tlus, mis on saadud Kafka'st | ProHoster","description":"Tere, Habr. Hiljuti.","canonical_url":"https:\/\/prohoster.info\/et\/blog\/administrirovanie\/povtornaya-obrabotka-sobytij-poluchennyh-iz-kafka","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"et_EE","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0441\u043e\u0431\u044b\u0442\u0438\u0439, \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0445 \u0438\u0437 Kafka | ProHoster","og:description":"\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440. \u041d\u0435\u0434\u0430\u0432\u043d\u043e.","og:url":"https:\/\/prohoster.info\/et\/blog\/administrirovanie\/povtornaya-obrabotka-sobytij-poluchennyh-iz-kafka","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2020-02-05T21:00:00+00:00","article:modified_time":"2020-02-18T11:04:24+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"56186","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":null,"breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 19:29:19","updated":"2022-10-02 15:56:10","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/et\/wp-json\/wp\/v2\/posts\/56186","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/et\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/et\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/et\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/et\/wp-json\/wp\/v2\/comments?post=56186"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/et\/wp-json\/wp\/v2\/posts\/56186\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/et\/wp-json\/wp\/v2\/media?parent=56186"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/et\/wp-json\/wp\/v2\/categories?post=56186"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/et\/wp-json\/wp\/v2\/tags?post=56186"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}