{"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":"Kafa \u00fcrituste t\u00f6\u00f6tlemine","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><img decoding=\"async\" alt=\"Kafa \u00fcrituste t\u00f6\u00f6tlemine\" 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 kogemusi<\/a><\/noindex> selle kohta, milliseid parameetreid me tihti kasutame Kafka producentide ja tarbijate jaoks, et tagada garanteeritud kohaletoimetamine. Selles artiklis r\u00e4\u00e4gin, kuidas korraldasime s\u00fcndmuse taast\u00f6\u00f6tluse, mis saadi Kafka'lt v\u00e4lise s\u00fcsteemi ajutise k\u00e4ttesaamatuse t\u00f5ttu.<\/p>\n<p><\/p>\n<p>Kaasaegsed rakendused toimivad v\u00e4ga keerulises keskkonnas. \u00c4ri\u00fcksus, mis on pakitud kaasaegsesse tehnoloogilisse virna, t\u00f6\u00f6tades Docker'i pildis, mida haldab orkestraator nagu Kubernetes v\u00f5i OpenShift, ja kommunikeerides teiste rakendustega v\u00f5i ettev\u00f5tte lahendustega f\u00fc\u00fcsiliste ja virtuaalsete ruuterite ahela kaudu. Sellises keskkonnas v\u00f5ib alati midagi katki minna, seega on s\u00fcndmuste taast\u00f6\u00f6tlus, kui \u00fcks v\u00e4lisest s\u00fcsteemist ei ole saadaval \u2014 oluline osa meie \u00e4riprotsessidest.<\/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>Varasemalt kasutasime projektis IBM MQ as\u00fcnkroonsete s\u00f5numite saatmiseks. Kui teenuse t\u00f6\u00f6tamise k\u00e4igus tekkis m\u00f5ni viga, v\u00f5is saadud s\u00f5num paigutada dead-letter-queue (DLQ), et seda hiljem k\u00e4sitsi anal\u00fc\u00fcsida. DLQ loodi koos sissetuleva j\u00e4rjekorraga, s\u00f5numi \u00fcmberpaigutamine toimus IBM MQ sees. <\/p>\n<p><\/p>\n<p>Kui viga oli ajutine ja me suutsime selle m\u00e4\u00e4rata (n\u00e4iteks ResourceAccessException HTTP-k\u00f5ne ajal v\u00f5i MongoTimeoutException MongoDb p\u00e4ringu puhul), siis rakendati kordusv\u00e4ljakutsete strateegiat. \u00dcksk\u00f5ik millest rakenduse loogika branch'ist, algne s\u00f5num omistati kas s\u00fcsteemij\u00e4rjekorda edasil\u00fckkamiseks v\u00f5i eraldi rakendusse, mis kunagi ammu loodi s\u00f5numite kordus saatmiseks. S\u00f5numi p\u00e4isesse kirjutatakse kordusv\u00e4ljakutsete number, mis on seotud viivituse intervalliga v\u00f5i rakenduse tasemel strateegia l\u00f5puga. Kui me j\u00f5uame strateegia l\u00f5puni, kuid v\u00e4line s\u00fcsteem on endiselt k\u00e4ttesaamatu, paigutatakse s\u00f5num DLQ-sse k\u00e4sitsi anal\u00fc\u00fcsimiseks.<\/p>\n<p><\/p>\n<h2 id=\"poisk-resheniya\">Lahenduse otsimine<\/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\">lahendus<\/a><\/noindex>. L\u00fchidalt \u00f6eldes pakutakse iga viivituse intervalli jaoks luua teema ja rakenduse k\u00fcljes tarbijaid, mis loevad s\u00f5numid vajaliku viivitusega. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Kafa \u00fcrituste t\u00f6\u00f6tlemine\" src=\"\/wp-content\/uploads\/2020\/02\/718c4c3f71ccf95b5a6f60e1b2c685d2.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Malgr\u00e9 le grand nombre d'avis positifs, cela ne me semble pas tout \u00e0 fait r\u00e9ussi. Tout d'abord, parce que le d\u00e9veloppeur, en plus de mettre en \u0153uvre les exigences commerciales, devra passer beaucoup de temps \u00e0 mettre en \u0153uvre le m\u00e9canisme d\u00e9crit.<\/p>\n<p><\/p>\n<p>Lisaks, kui Kafka klastri juures on lubatud juurdep\u00e4\u00e4su haldamine, tuleb kulutada aega teemade loomisele ja nendele vajalike juurdep\u00e4\u00e4sude tagamisele. Peale selle on vajalik valida \u00f5ige retention.ms parameeter iga retry-teema jaoks, et s\u00f5numid j\u00f5uaksid uuesti edastada ja ei kaoks. Rakenduse loomine ja juurdep\u00e4\u00e4sude taotlemine tuleb korrata iga olemasoleva v\u00f5i uue teenuse jaoks.<\/p>\n<p><\/p>\n<p>N\u00fc\u00fcd vaatame, milliseid s\u00f5numi uuesti t\u00f6\u00f6tlemise mehhanisme pakub meile spring \u00fcldiselt ja spring-kafka konkreetsemalt. Spring-kafka-l on \u00fclekantav s\u00f5ltuvus spring-retry-st, mis pakub abstraktsioone erinevate BackOffPolicy-de haldamiseks. See on \u00fcsna paindlik t\u00f6\u00f6riist, kuid selle oluline puudus on s\u00f5numite hoidmine uuesti saatmiseks rakenduse m\u00e4lus. See t\u00e4hendab, et rakenduse taask\u00e4ivitamine uuenduse v\u00f5i t\u00f6\u00f6ea vigade t\u00f5ttu toob kaasa k\u00f5ikide uuesti t\u00f6\u00f6tlemist ootavate s\u00f5numite kaotuse. Kuna see on meie s\u00fcsteemi jaoks kriitiline punkt, ei uurinud me seda edaspidi.<\/p>\n<p><\/p>\n<p>Ise spring-kafka pakub mitmeid rakendusi ContainerAwareErrorHandler-le, 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>, mille abil saab s\u00f5numit hiljem t\u00f6\u00f6delda, mitte nihutades offset'i vea tekkimise korral. Alates versioonist spring-kafka 2.3 on v\u00f5imalus m\u00e4\u00e4rata BackOffPolicy.<\/p>\n<p><\/p>\n<p>See l\u00e4henemine v\u00f5imaldab korduvkasutatavatel s\u00f5numitel rakenduse taask\u00e4ivitamisest \u00fcle elada, kuid DLQ mehhanism on endiselt puudu. Just selle valiku tegime 2019. aasta alguses, optimistlikult arvates, et DLQ-d ei ole vaja (meil vedas ja see t\u00f5epoolest ei olnud vajalik mitme kuu jooksul rakenduse sellise kordusprotsessimise s\u00fcsteemiga t\u00f6\u00f6tamise ajal). Ajutised vead viisid SeekToCurrentErrorHandleri aktiveerimiseni. \u00dclej\u00e4\u00e4nud vead kanti logisse, mis viis offset'i nihkeni ning t\u00f6\u00f6tlemine j\u00e4tkus j\u00e4rgmise s\u00f5numiga.<\/p>\n<p><\/p>\n<h2 id=\"itogovoe-reshenie\">L\u00f5plik otsus<\/h2>\n<p><\/p>\n<p>SeekToCurrentErrorHandleril p\u00f5hinev rakendus viis meid oma s\u00f5numite kordussaata mehhanismi v\u00e4ljat\u00f6\u00f6tamiseni.<\/p>\n<p><\/p>\n<p>Esiteks soovisime kasutada juba olemasolevat kogemust ja laiendada seda rakenduse loogika p\u00f5hjal. Rakenduse puhul, millel on lineaarne loogika, oleks optimaalne l\u00f5petada uute s\u00f5numite lugemine l\u00fchikese aja jooksul, mis on m\u00e4\u00e4ratud uuestis kutsumise strateegia raames. Muude rakenduste puhul soovime, et oleks olemas \u00fchtne punkt, mis tagab uuestis kutsumise strateegia rakendamise. Lisaks peaks see \u00fchtne punkt omama DLQ funktsionaalsust m\u00f5lema l\u00e4henemise puhul.<\/p>\n<p><\/p>\n<p>Uuestis kutsumise strateegia peaks olema salvestatud rakenduses, mis vastutab j\u00e4rgmise intervalli saamise eest ajutise vea ilmnemisel.<\/p>\n<p><\/p>\n<h3 id=\"ostanovka-consumera-dlya-prilozheniya-s-lineynoy-logikoy\">Consumer'i peatamine lineaarse loogikaga rakendustes<\/h3>\n<p><\/p>\n<p>Spring-kafka kasutamisel v\u00f5ib Consumer'i peatamise kood v\u00e4lja n\u00e4ha umbes 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 jaoks\n    }<\/code><\/pre>\n<p><\/p>\n<p>N\u00e4ites retryAt on aeg, millal tuleb uuesti k\u00e4ivitada MessageListenerContainer, kui see veel t\u00f6\u00f6tab. Uuesti k\u00e4ivitamine toimub eraldi teemas, mis on k\u00e4ivitatud TaskScheduler'is, mille rakendust pakub ka spring. <\/p>\n<p><\/p>\n<p>M\u00e4\u00e4rame retryAt v\u00e4\u00e4rtuse j\u00e4rgmisel viisil:<\/p>\n<p><\/p>\n<ol>\n<li>Otsitakse korduvate kutsete loendi v\u00e4\u00e4rtust.<\/li>\n<li>Loendi v\u00e4\u00e4rtuse p\u00f5hjal otsitakse praegune viivituse intervall korduvate kutsete strateegias. Strateegia kuulutatakse v\u00e4lja rakenduses endas, selle salvestamiseks valisime JSON formaadi.<\/li>\n<li>JSON-massis leitud intervall sisaldab sekundeid, mille m\u00f6\u00f6dumisel tuleb t\u00f6\u00f6tlemine uuesti k\u00e4ivitada. See sekundite arv liidetakse praegusele ajale, luues v\u00e4\u00e4rtuse retryAt jaoks.<\/li>\n<li>Kui intervalli ei leita, siis on retryAt v\u00e4\u00e4rtus null ja s\u00f5num saadetakse DLQ-sse k\u00e4sitsi lahendamiseks.<\/li>\n<\/ol>\n<p><\/p>\n<p>Sellise l\u00e4henemise puhul j\u00e4\u00e4b alles vaid s\u00e4ilitada iga t\u00f6\u00f6tlemisel oleva teadete korduvate k\u00f5nede arv, n\u00e4iteks rakenduse m\u00e4lus. Katsete arvu salvestamine m\u00e4llu ei ole selle l\u00e4henemise jaoks kriitiline, kuna lineaarse loogikaga rakendus ei saa tervikuna t\u00f6\u00f6tlemist teostada. Erinevalt spring-retry'ist, rakenduse taask\u00e4ivitamine ei too kaasa k\u00f5igi teadete kadumist korduvaks t\u00f6\u00f6tlemiseks, vaid lihtsalt strateegia taask\u00e4ivitamist. <\/p>\n<p><\/p>\n<p>See l\u00e4henemine aitab v\u00e4hendada koormust v\u00e4lisele s\u00fcsteemile, mis v\u00f5ib olla kergesti kergesti \u00fcle koormatud ja seet\u00f5ttu mittesaadav. Teisis\u00f5nu, lisaks korduvatele t\u00f6\u00f6tlemistele oleme saavutanud mustri rakendamise. <noindex><a rel=\"nofollow\" href=\"https:\/\/microservices.io\/patterns\/reliability\/circuit-breaker.html\">circuit breaker<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p>Meie juhul on vea k\u00fcnnis vaid 1, ja et minimeerida s\u00fcsteemi seiskamist ajutise n\u00e4tiv\u00f5rgu katkestuse t\u00f5ttu, kasutame v\u00e4ga granulaarset korduvate k\u00f5nede strateegiat v\u00e4ikeste viibimisega intervallidega. See ei pruugi sobida k\u00f5ikidele kontserni rakendustele, seega tuleb vea k\u00fcnnise ja intervalli suuruse vaheline suhe valida, tuginedes s\u00fcsteemi erip\u00e4radele.<\/p>\n<p><\/p>\n<h3 id=\"otdelnoe-prilozhenie-dlya-obrabotki-soobscheniy-ot-prilozheniy-s-nedeterminirovannoy-logikoy\">Eraldi rakendus, mis k\u00e4sitleb rakendustelt saadud s\u00f5numeid, millel on m\u00e4\u00e4ramatu loogika.<\/h3>\n<p><\/p>\n<p>Siin on koodin\u00e4ide, mis saadab s\u00f5numit sellesse rakendusse (Retryer), mis saadab j\u00e4lle teemale DESTINATION, kui j\u00f5uab aega RETRY_AT:<\/p>\n<p><\/p>\n<pre><code class=\"java\">\npublic &lt;K, V&gt; void retry(ConsumerRecord&lt;K, V&gt; record, String retryToTopic, \n                         Instant retryAt, String counter, String groupId, Exception e) {\n        Headers headers = ofNullable(record.headers()).orElse(new RecordHeaders());\n        List&lt;Header&gt; arrayOfHeaders = \n            new ArrayList&lt;&gt;(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, &quot;retry&quot;::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, &quot;backout&quot;::getBytes);\n        }\n        ProducerRecord&lt;K, V&gt; messageToSend =\n            new ProducerRecord&lt;&gt;(retryTopic, null, null, record.key(), record.value(), arrayOfHeaders);\n        kafkaTemplate.send(messageToSend);\n    }<\/code><\/pre>\n<p><\/p>\n<p>N\u00e4iteks on n\u00e4ha, et palju teavet edastatakse p\u00e4istes. RETRY_AT v\u00e4\u00e4rtus asub samamoodi nagu tarbija peatamise kordamise mehhanismi puhul. Peale DESTINATION ja RETRY_AT edastame:<\/p>\n<p><\/p>\n<ul>\n<li>GROUP_ID, mille alusel grupeerime s\u00f5numeid k\u00e4sitsi anal\u00fc\u00fcsimiseks ja otsingu lihtsustamiseks.<\/li>\n<li>ORIGINAL_PARTITION, et p\u00fc\u00fcda s\u00e4ilitada sama tarbija uuesti t\u00f6\u00f6tlemiseks. See parameeter v\u00f5ib olla null, sel juhul saadakse uus partitsioon originaals\u00f5numi record.key() v\u00f5tme j\u00e4rgi.<\/li>\n<li>\u00dcksuste uuendatud COUNTER v\u00e4\u00e4rtus, et j\u00e4rgida korduvate kutsete strateegiat.<\/li>\n<li>SEND_TO \u2014 konstant, mis n\u00e4itab, kas s\u00f5num tuleks saata uuesti t\u00f6\u00f6tlemiseks p\u00e4rast RETRY_AT saavutamist v\u00f5i paigutada DLQ-sse.<\/li>\n<li>REASON \u2014 p\u00f5hjus, miks s\u00f5numi t\u00f6\u00f6tlemine katkestati.<\/li>\n<\/ul>\n<p><\/p>\n<p>Retryer salvestab s\u00f5numid edasise saatmise ja k\u00e4sitsi anal\u00fc\u00fcsimise jaoks PostgreSQL-is. Ajastatult k\u00e4ivitatakse \u00fclesanne, mis leiab s\u00f5numid, mille RETRY_AT on m\u00f6\u00f6dunud, ja saadab need tagasi DESTINATIONi TOPICALi ORIGINAL_PARTITION partitsioonile record.key() v\u00f5tmega.<\/p>\n<p><\/p>\n<p>P\u00e4rast s\u00f5numi saatmist kustutatakse need PostgreSQL-ist. S\u00f5numite k\u00e4sitsi t\u00f6\u00f6tlemine toimub lihtsas kasutajaliideses, mis suheldes Retryeriga \u00fcle REST API. Peamised funktsioonid h\u00f5lmavad s\u00f5numite uuesti saatmist v\u00f5i DLQ-st kustutamist, veateabe vaatamist ja s\u00f5numite otsimist, n\u00e4iteks vea nime j\u00e4rgi. <\/p>\n<p><\/p>\n<p>Kuna meie klasterites on lubatud juurdep\u00e4\u00e4suhaldus, on vaja t\u00e4iendavalt taotleda juurdep\u00e4\u00e4se teemale, mida kuulab Retryer, ja anda Retryerile v\u00f5imalus kirjutada DESTINATION teemale. See on ebamugav, kuid v\u00f5rreldes ajavahemikute p\u00f5hise l\u00e4henemisega on meil t\u00e4ielik DLQ ja kasutajaliides selle haldamiseks.<\/p>\n<p><\/p>\n<p>On juhtumeid, kui sisendteemat loevad mitmed erinevad tarbijagruppide rakendused, millel on erinev loogika. S\u00f5numi uuesti t\u00f6\u00f6tlemine Retryeri kaudu \u00fche sellise rakenduse jaoks toob teises esile duplikaadi. Selle vastu kaitsmiseks loome eraldi teema uuesti t\u00f6\u00f6tlemiseks. Sisend- ja retry-teemat v\u00f5ib lugeda sama tarbija ilma piiranguteta. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Kafa \u00fcrituste t\u00f6\u00f6tlemine\" 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 selle saab rakendusele lisada <noindex><a rel=\"nofollow\" href=\"https:\/\/spring.io\/projects\/spring-cloud-netflix\">spring-cloud-netflix<\/a><\/noindex> v\u00f5i uus <noindex><a rel=\"nofollow\" href=\"https:\/\/spring.io\/projects\/spring-cloud-circuitbreaker\">spring cloud ringmurdja<\/a><\/noindex>, m\u00e4hkides v\u00e4liste teenuste kutsumise kohad vastavatesse abstraktsioonidesse. Lisaks ilmub v\u00f5imalus valida strateegia <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/architecture\/patterns\/bulkhead\">bulkhead<\/a><\/noindex> mustrile, mis v\u00f5ib samuti olla kasulik. N\u00e4iteks spring-cloud-netflixis v\u00f5ib see olla teepool v\u00f5i seeder.<\/p>\n<p><\/p>\n<h2 id=\"vyvod\">Kokkuv\u00f5te<\/h2>\n<p><\/p>\n<p>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>Ere rakenduse peamine eelis on see, et seda saavad kasutada v\u00e4lised s\u00fcsteemid, mis t\u00f6\u00f6tavad samas Kafka klastris, ilma m\u00e4rkimisv\u00e4\u00e4rsete muudatusteta oma k\u00fcljel! Selline rakendus peab ainult saama juurdep\u00e4\u00e4su retry-teemadele, t\u00e4itma m\u00f5ned Kafka pealkirjad ja saatma s\u00f5numi Retryerisse. \u00dchtegi t\u00e4iendavat infrastruktuuri ei pea \u00fcles t\u00f5stma. Ja et v\u00e4hendada s\u00f5numite saadetavaid koguseid rakendusest Retryerisse ja tagasi, oleme eraldanud rakendused, millel on lineaarne loogika, ning teinud neisse uuesti t\u00f6\u00f6tlemise kautta Consumeri peatumise.<\/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 4.9.10 - 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 \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\" \/>\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) 4.9.10\" \/>\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 \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\" \/>\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\udd47S\u00fcndmuste uuesti t\u00f6\u00f6tlemine, mis on saadud Kafka'st | ProHoster","description":"Tere, Habr. Hiljuti jagasin oma kogemust, milliseid parameetreid me meeskonnas k\u00f5ige sagedamini kasutame Kafka Producentide ja Tarbijate puhul, et saavutada garanteeritud kohaletoimetamine. Selles artiklis tahan r\u00e4\u00e4kida, kuidas korraldasime s\u00fcndmuste uuesti t\u00f6\u00f6tlemise, mis saadi Kafka'st, seoses v\u00e4lise s\u00fcsteemi ajutise k\u00e4ttesaamatusega. Kaasaegsed rakendused t\u00f6\u00f6tavad v\u00e4ga keerulises keskkonnas. \u00c4riline loogika, mis on \u00fcmbritsetud","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 \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","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"},"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}]}}