{"id":35786,"date":"2019-10-31T22:06:19","date_gmt":"2019-10-31T19:06:19","guid":{"rendered":"https:\/\/prohoster.info\/blog\/kniga-kafka-streams-v-dejstvii-prilozheniya-i-mikroservisy-dlya-raboty-v-realnom-vremeni\/"},"modified":"2019-10-31T22:06:19","modified_gmt":"2019-10-31T19:06:19","slug":"kniga-kafka-streams-v-dejstvii-prilozheniya-i-mikroservisy-dlya-raboty-v-realnom-vremeni","status":"publish","type":"post","link":"https:\/\/prohoster.info\/pt\/blog\/administrirovanie\/kniga-kafka-streams-v-dejstvii-prilozheniya-i-mikroservisy-dlya-raboty-v-realnom-vremeni","title":{"rendered":"O livro 'Kafka Streams em A\u00e7\u00e3o. Aplica\u00e7\u00f5es e Microservi\u00e7os para Trabalhar em Tempo Real'","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/piter\/blog\/457756\/\"><img decoding=\"async\" alt=\"O livro &#039;Kafka Streams em A\u00e7\u00e3o. Aplica\u00e7\u00f5es e Microservi\u00e7os para Trabalhar em Tempo Real&#039;\" src=\"\/wp-content\/uploads\/6f8bd2b31b87b0c760c1148515893c43.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/a><\/noindex> Ol\u00e1, Habrojeitos! Este livro \u00e9 adequado para qualquer desenvolvedor que deseja entender o processamento em fluxo. Compreender a programa\u00e7\u00e3o distribu\u00edda ajudar\u00e1 a explorar melhor o Kafka e o Kafka Streams. Seria bom conhecer o pr\u00f3prio framework Kafka, mas n\u00e3o \u00e9 obrigat\u00f3rio: eu irei explicar tudo o que voc\u00ea precisa saber. Desenvolvedores experientes em Kafka, assim como os iniciantes, dominar\u00e3o a cria\u00e7\u00e3o de aplica\u00e7\u00f5es interessantes para processamento em fluxo com a biblioteca Kafka Streams atrav\u00e9s deste livro. Desenvolvedores Java de n\u00edvel m\u00e9dio e avan\u00e7ado, j\u00e1 familiarizados com conceitos como serializa\u00e7\u00e3o, aprender\u00e3o a aplicar suas habilidades na cria\u00e7\u00e3o de aplica\u00e7\u00f5es Kafka Streams. O c\u00f3digo-fonte do livro est\u00e1 escrito em Java 8 e faz uso significativo da sintaxe de express\u00f5es lambda do Java 8, ent\u00e3o a habilidade de trabalhar com fun\u00e7\u00f5es lambda (mesmo em outra linguagem de programa\u00e7\u00e3o) ser\u00e1 \u00fatil para voc\u00ea.<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h3>Fragmento. 5.3. Agrega\u00e7\u00e3o e opera\u00e7\u00f5es de janela<\/h3>\n<p>\nNesta se\u00e7\u00e3o, iremos abordar as partes mais promissoras do Kafka Streams. At\u00e9 agora, examinamos os seguintes aspectos do Kafka Streams:<\/p>\n<ul>\n<li>cria\u00e7\u00e3o da topologia de processamento;<\/li>\n<li>uso de estado em aplica\u00e7\u00f5es de fluxo;<\/li>\n<li>execu\u00e7\u00e3o de jun\u00e7\u00f5es de fluxos de dados;<\/li>\n<li>diferen\u00e7as entre fluxos de eventos (KStream) e fluxos de atualiza\u00e7\u00f5es (KTable).<\/li>\n<\/ul>\n<p>\nNos pr\u00f3ximos exemplos, iremos unir todos esses elementos. Al\u00e9m disso, voc\u00ea ser\u00e1 apresentado \u00e0s opera\u00e7\u00f5es de janela \u2014 mais uma capacidade not\u00e1vel das aplica\u00e7\u00f5es de fluxo. Nosso primeiro exemplo ser\u00e1 uma agrega\u00e7\u00e3o simples.<\/p>\n<h3>5.3.1. Agrega\u00e7\u00e3o do volume de vendas de a\u00e7\u00f5es por setores da ind\u00fastria<\/h3>\n<p>\nAgrega\u00e7\u00e3o e agrupamento s\u00e3o ferramentas essenciais ao trabalhar com dados em fluxo. Analisar registros individuais \u00e0 medida que chegam muitas vezes n\u00e3o \u00e9 suficiente. Para extrair informa\u00e7\u00f5es adicionais dos dados, \u00e9 necess\u00e1rio agrup\u00e1-los e combin\u00e1-los.<\/p>\n<p>Neste exemplo, voc\u00ea assumir\u00e1 o papel de um trader intraday que precisa acompanhar os volumes de vendas das a\u00e7\u00f5es de empresas em v\u00e1rios setores da ind\u00fastria. Em particular, voc\u00ea se interessar\u00e1 pelas cinco empresas com os maiores volumes de vendas em cada setor da ind\u00fastria.<\/p>\n<p>Para tal agrega\u00e7\u00e3o, ser\u00e3o necess\u00e1rios alguns passos subsequentes para transformar os dados na forma desejada (falando de maneira geral).<\/p>\n<ol>\n<li>Criar uma fonte baseada em t\u00f3picos que publique informa\u00e7\u00f5es brutas sobre negocia\u00e7\u00f5es de a\u00e7\u00f5es. Precisamos mapear o objeto do tipo StockTransaction para o objeto do tipo ShareVolume. O fato \u00e9 que o objeto StockTransaction cont\u00e9m metadados de vendas, mas precisamos apenas dos dados sobre a quantidade de a\u00e7\u00f5es vendidas.<\/li>\n<li>Agrupar os dados de ShareVolume por s\u00edmbolos das a\u00e7\u00f5es. Ap\u00f3s a agrega\u00e7\u00e3o pelos s\u00edmbolos, podemos resumir esses dados em somas intermedi\u00e1rias dos volumes de vendas de a\u00e7\u00f5es. Vale ressaltar que o m\u00e9todo KStream.groupBy retorna uma inst\u00e2ncia do tipo KGroupedStream. E para obter uma inst\u00e2ncia do KTable, devemos chamar o m\u00e9todo KGroupedStream.reduce em seguida.<\/li>\n<\/ol>\n<p><\/p>\n<blockquote><p><b>O que \u00e9 a interface KGroupedStream?<\/b><\/p>\n<p>Os m\u00e9todos KStream.groupBy e KStream.groupByKey retornam uma inst\u00e2ncia de KGroupedStream. KGroupedStream \u00e9 uma representa\u00e7\u00e3o intermedi\u00e1ria de um fluxo de eventos ap\u00f3s a agrega\u00e7\u00e3o por chaves. Ele n\u00e3o \u00e9 destinado a ser trabalhado diretamente. Em vez disso, KGroupedStream \u00e9 utilizado para opera\u00e7\u00f5es de agrega\u00e7\u00e3o, cujo resultado \u00e9 sempre um KTable. E como o resultado das opera\u00e7\u00f5es de agrega\u00e7\u00e3o \u00e9 um KTable e um armazenamento de estado \u00e9 aplicado a elas, pode ser que nem todas as atualiza\u00e7\u00f5es resultantes sejam enviadas adiante pelo pipeline.<\/p>\n<p>O m\u00e9todo KTable.groupBy retorna um KGroupedTable an\u00e1logo \u2014 uma representa\u00e7\u00e3o intermedi\u00e1ria de um fluxo de atualiza\u00e7\u00f5es, reorganizado por chave.<\/p><\/blockquote>\n<p>\nFaremos uma pequena pausa e olharemos para a Fig. 5.9, que mostra o que conseguimos. Esta topologia j\u00e1 deve ser familiar para voc\u00ea.<\/p>\n<p><img decoding=\"async\" alt=\"O livro &#039;Kafka Streams em A\u00e7\u00e3o. Aplica\u00e7\u00f5es e Microservi\u00e7os para Trabalhar em Tempo Real&#039;\" src=\"\/wp-content\/uploads\/9fd61317cde376362adcaeec72908919.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nAgora vamos olhar para o c\u00f3digo desta topologia (ele pode ser encontrado no arquivo src\/main\/java\/bbejeck\/chapter_5\/AggregationsAndReducingExample.java) (listagem 5.2).<\/p>\n<p><img decoding=\"async\" alt=\"O livro &#039;Kafka Streams em A\u00e7\u00e3o. Aplica\u00e7\u00f5es e Microservi\u00e7os para Trabalhar em Tempo Real&#039;\" src=\"\/wp-content\/uploads\/f937287e448295fbd467c283ceca316a.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nO c\u00f3digo apresentado \u00e9 conciso e realiza uma grande quantidade de opera\u00e7\u00f5es em poucas linhas. No primeiro par\u00e2metro do m\u00e9todo builder.stream, voc\u00ea pode notar algo novo para voc\u00ea: o valor do tipo enumerado AutoOffsetReset.EARLIEST (tamb\u00e9m existe o LATEST), definido usando o m\u00e9todo Consumed.withOffsetResetPolicy. Com este tipo enumerado, voc\u00ea pode especificar a estrat\u00e9gia de redefini\u00e7\u00e3o de deslocamentos para cada um dos KStream ou KTable, que tem prioridade sobre o par\u00e2metro de redefini\u00e7\u00e3o de deslocamentos na configura\u00e7\u00e3o.<\/p>\n<blockquote><p><b>GroupByKey e GroupBy<\/b><\/p>\n<p>Na interface KStream, existem dois m\u00e9todos para agrupar registros: GroupByKey e GroupBy. Ambos retornam KGroupedTable, ent\u00e3o voc\u00ea pode se perguntar: qual \u00e9 a diferen\u00e7a entre eles e quando usar cada um?<\/p>\n<p>O m\u00e9todo GroupByKey \u00e9 aplicado quando as chaves no KStream j\u00e1 n\u00e3o est\u00e3o vazias. E o principal, a flag \"exige novo particionamento\" nunca foi definida.<\/p>\n<p>O m\u00e9todo GroupBy pressup\u00f5e que voc\u00ea mudou as chaves para agrupamento, ent\u00e3o a flag de novo particionamento est\u00e1 definida como verdadeira. A execu\u00e7\u00e3o ap\u00f3s o m\u00e9todo GroupBy de joins, agrega\u00e7\u00f5es, etc., resultar\u00e1 em novo particionamento autom\u00e1tico.<br \/>\nResumo: deve-se usar o GroupByKey sempre que poss\u00edvel, ao inv\u00e9s do GroupBy.<\/p><\/blockquote>\n<p>\nO que os m\u00e9todos mapValues e groupBy fazem \u00e9 claro, ent\u00e3o vamos dar uma olhada no m\u00e9todo sum() (ele pode ser encontrado no arquivo src\/main\/java\/bbejeck\/model\/ShareVolume.java) (listagem 5.3).<\/p>\n<p><img decoding=\"async\" alt=\"O livro &#039;Kafka Streams em A\u00e7\u00e3o. Aplica\u00e7\u00f5es e Microservi\u00e7os para Trabalhar em Tempo Real&#039;\" src=\"\/wp-content\/uploads\/8e9a6f873594f9b5fef9a96c42353a61.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nO m\u00e9todo ShareVolume.sum retorna a soma intermedi\u00e1ria do volume de vendas das a\u00e7\u00f5es, e o resultado de toda a cadeia de c\u00e1lculos representa um objeto KTable. Agora voc\u00ea entende qual \u00e9 o papel do KTable. Ao receber objetos ShareVolume, a \u00faltima atualiza\u00e7\u00e3o relevante \u00e9 armazenada no respectivo objeto KTable. \u00c9 importante n\u00e3o esquecer que todas as atualiza\u00e7\u00f5es s\u00e3o refletidas no shareVolumeKTable anterior, mas nem todas s\u00e3o enviadas adiante.<\/p>\n<p>Com esse KTable, realizamos a agrega\u00e7\u00e3o (com base no n\u00famero de a\u00e7\u00f5es vendidas) para obter as cinco empresas com os maiores volumes de vendas de a\u00e7\u00f5es em cada setor. Nossas a\u00e7\u00f5es ser\u00e3o semelhantes \u00e0s do primeiro agrupamento.<\/p>\n<ol>\n<li>Executar outra opera\u00e7\u00e3o groupBy para agrupar objetos ShareVolume por setores.<\/li>\n<li>Come\u00e7ar a somar os objetos ShareVolume. Desta vez, o objeto de agrega\u00e7\u00e3o \u00e9 uma fila de prioridade de tamanho fixo. Nessa fila de tamanho fixo, s\u00e3o mantidas apenas cinco empresas com os maiores n\u00fameros de a\u00e7\u00f5es vendidas.<\/li>\n<li>Mapear as filas do item anterior para um valor de string e retornar as cinco mais vendidas em termos de quantidade de a\u00e7\u00f5es, por setores.<\/li>\n<li>Gravar os resultados em formato string em um t\u00f3pico.<\/li>\n<\/ol>\n<p>\nA figura 5.10 mostra o gr\u00e1fico da topologia do fluxo de dados. Como voc\u00ea pode ver, o segundo ciclo de processamento \u00e9 bastante simples.<\/p>\n<p><img decoding=\"async\" alt=\"O livro &#039;Kafka Streams em A\u00e7\u00e3o. Aplica\u00e7\u00f5es e Microservi\u00e7os para Trabalhar em Tempo Real&#039;\" src=\"\/wp-content\/uploads\/dabd1507eee267038edb7f8d76d8d8ae.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nAgora, ao entender claramente a estrutura deste segundo ciclo de processamento, voc\u00ea pode consultar seu c\u00f3digo-fonte (voc\u00ea o encontrar\u00e1 no arquivo src\/main\/java\/bbejeck\/chapter_5\/AggregationsAndReducingExample.java) (listagem 5.4).<\/p>\n<p>Neste inicializador, h\u00e1 uma vari\u00e1vel fixedQueue. Este \u00e9 um objeto personalizado - um adaptador para java.util.TreeSet, que \u00e9 usado para rastrear os N maiores resultados em ordem decrescente da quantidade de a\u00e7\u00f5es vendidas.<\/p>\n<p><img decoding=\"async\" alt=\"O livro &#039;Kafka Streams em A\u00e7\u00e3o. Aplica\u00e7\u00f5es e Microservi\u00e7os para Trabalhar em Tempo Real&#039;\" src=\"\/wp-content\/uploads\/488b072b0d91b81c925ca72291e69e48.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nVoc\u00ea j\u00e1 se deparou com as chamadas groupBy e mapValues, ent\u00e3o n\u00e3o vamos nos deter nelas (chamamos o m\u00e9todo KTable.toStream, uma vez que o m\u00e9todo KTable.print \u00e9 considerado obsoleto). No entanto, voc\u00ea ainda n\u00e3o viu a vers\u00e3o KTable do m\u00e9todo aggregate(), ent\u00e3o vamos gastar um tempo discutindo isso.<\/p>\n<p>Como voc\u00ea se lembra, a KTable se distingue pelo fato de que registros com chaves iguais s\u00e3o considerados atualiza\u00e7\u00f5es. A KTable substitui o registro antigo pelo novo. A agrega\u00e7\u00e3o ocorre de forma semelhante: agregam-se os registros mais recentes com uma mesma chave. Quando um registro \u00e9 recebido, ele \u00e9 adicionado \u00e0 inst\u00e2ncia da classe FixedSizePriorityQueue usando um somador (o segundo par\u00e2metro na chamada do m\u00e9todo aggregate), mas se j\u00e1 existir outro registro com a mesma chave, o registro antigo \u00e9 removido usando um subtrator (o terceiro par\u00e2metro na chamada do m\u00e9todo aggregate).<\/p>\n<p>Isso significa que nosso agregador, FixedSizePriorityQueue, n\u00e3o agrega todos os valores com uma mesma chave, mas mant\u00e9m uma soma m\u00f3vel das quantidades dos N tipos de a\u00e7\u00f5es mais vendidas. Em cada registro recebido, est\u00e1 contida a quantidade total de a\u00e7\u00f5es vendidas at\u00e9 o momento. A KTable ir\u00e1 fornecer informa\u00e7\u00f5es sobre quais a\u00e7\u00f5es de quais empresas est\u00e3o sendo vendidas mais no momento, a agrega\u00e7\u00e3o m\u00f3vel de cada uma das atualiza\u00e7\u00f5es n\u00e3o \u00e9 necess\u00e1ria.<\/p>\n<p>Aprendemos a fazer duas coisas importantes:<\/p>\n<ul>\n<li>agrupar valores na KTable por uma chave comum a eles;<\/li>\n<li>realizar opera\u00e7\u00f5es \u00fateis sobre esses valores agrupados, como redu\u00e7\u00e3o e agrega\u00e7\u00e3o.<\/li>\n<\/ul>\n<p>\nSer capaz de realizar essas opera\u00e7\u00f5es \u00e9 importante para entender o significado dos dados que est\u00e3o se movendo atrav\u00e9s do aplicativo Kafka Streams e descobrir qual informa\u00e7\u00e3o eles transportam.<\/p>\n<p>Tamb\u00e9m reunimos alguns dos conceitos-chave discutidos anteriormente neste livro. No Cap\u00edtulo 4, falamos sobre a import\u00e2ncia do estado local e tolerante a falhas para um aplicativo de streaming. O primeiro exemplo deste cap\u00edtulo demonstrou por que o estado local \u00e9 t\u00e3o cr\u00edtico \u2014 ele permite rastrear quais informa\u00e7\u00f5es voc\u00ea j\u00e1 viu. O acesso local evita atrasos de rede, tornando o aplicativo mais eficiente e resiliente a erros.<\/p>\n<p>Ao realizar qualquer opera\u00e7\u00e3o de redu\u00e7\u00e3o ou agrega\u00e7\u00e3o, \u00e9 necess\u00e1rio especificar o nome do reposit\u00f3rio de estado. As opera\u00e7\u00f5es de redu\u00e7\u00e3o e agrega\u00e7\u00e3o retornam uma inst\u00e2ncia de KTable, e KTable usa o reposit\u00f3rio de estado para substituir os resultados antigos pelos novos. Como voc\u00ea viu, nem todas as atualiza\u00e7\u00f5es s\u00e3o enviadas adiante pelo pipeline, e isso \u00e9 importante, uma vez que as opera\u00e7\u00f5es de agrega\u00e7\u00e3o visam obter informa\u00e7\u00f5es finais. Se o estado local n\u00e3o for aplicado, KTable enviar\u00e1 todos os resultados de agrega\u00e7\u00e3o e redu\u00e7\u00e3o adiante.<\/p>\n<p>Em seguida, vamos explorar a execu\u00e7\u00e3o de opera\u00e7\u00f5es como agrega\u00e7\u00e3o dentro de um intervalo de tempo espec\u00edfico \u2014 as chamadas opera\u00e7\u00f5es de janela (windowing operations).<\/p>\n<h3>5.3.2. Opera\u00e7\u00f5es de Janela<\/h3>\n<p>\nNa se\u00e7\u00e3o anterior, conhecemos as opera\u00e7\u00f5es de redu\u00e7\u00e3o e agrega\u00e7\u00e3o 'deslizantes'. O aplicativo realizava uma redu\u00e7\u00e3o cont\u00ednua do volume de vendas de a\u00e7\u00f5es seguida da agrega\u00e7\u00e3o das cinco a\u00e7\u00f5es mais vendidas na bolsa.<\/p>\n<p>\u00c0s vezes, opera\u00e7\u00f5es cont\u00ednuas de agrega\u00e7\u00e3o e redu\u00e7\u00e3o de resultados s\u00e3o necess\u00e1rias. E em outras situa\u00e7\u00f5es, precisamos realizar opera\u00e7\u00f5es apenas em um intervalo de tempo espec\u00edfico. Por exemplo, calcular quantas opera\u00e7\u00f5es de a\u00e7\u00f5es de uma empresa espec\u00edfica foram feitas nos \u00faltimos 10 minutos. Ou quantos usu\u00e1rios clicaram em um novo banner publicit\u00e1rio nos \u00faltimos 15 minutos. O aplicativo pode realizar essas opera\u00e7\u00f5es repetidamente, mas com resultados relacionados apenas aos intervalos de tempo especificados (janelas temporais).<\/p>\n<h3>Contabilizando transa\u00e7\u00f5es de a\u00e7\u00f5es por comprador<\/h3>\n<p>\nNo pr\u00f3ximo exemplo, nos ocuparemos em rastrear transa\u00e7\u00f5es de a\u00e7\u00f5es por v\u00e1rios traders \u2014 seja grandes institui\u00e7\u00f5es ou investidores individuais astutos.<\/p>\n<p>Existem duas poss\u00edveis raz\u00f5es para esse tipo de rastreamento. Uma delas \u00e9 a necessidade de saber o que os l\u00edderes do mercado est\u00e3o comprando\/vendendo. Se esses grandes players e investidores experientes identificam oportunidades, faz sentido seguir suas estrat\u00e9gias. A segunda raz\u00e3o \u00e9 o desejo de notar quaisquer sinais potenciais de transa\u00e7\u00f5es ilegais usando informa\u00e7\u00f5es privilegiadas. Para isso, ser\u00e1 necess\u00e1rio analisar a correla\u00e7\u00e3o entre grandes picos de vendas e importantes comunicados de imprensa.<\/p>\n<p>Esse rastreamento consiste em etapas como:<\/p>\n<ul>\n<li>a cria\u00e7\u00e3o de um fluxo de leitura a partir do t\u00f3pico stock-transactions;<\/li>\n<li>agrupamento de registros de entrada por identificador de comprador e s\u00edmbolo da a\u00e7\u00e3o. A chamada do m\u00e9todo groupBy retorna uma inst\u00e2ncia da classe KGroupedStream;<\/li>\n<li>o retorno do fluxo de dados delimitado por um per\u00edodo de tempo com o m\u00e9todo KGroupedStream.windowedBy, permitindo a agrega\u00e7\u00e3o em janelas. Dependendo do tipo de janela, retorna-se um TimeWindowedKStream ou SessionWindowedKStream;<\/li>\n<li>a contagem de transa\u00e7\u00f5es para a opera\u00e7\u00e3o de agrega\u00e7\u00e3o. O fluxo de dados em janela determina se um registro espec\u00edfico \u00e9 levado em considera\u00e7\u00e3o durante essa contagem;<\/li>\n<li>o registro dos resultados em um t\u00f3pico ou sua sa\u00edda no console durante o desenvolvimento.<\/li>\n<\/ul>\n<p>\nA topologia dessa aplica\u00e7\u00e3o \u00e9 simples, mas uma representa\u00e7\u00e3o visual n\u00e3o faria mal. Vamos olhar para a Fig. 5.11.<\/p>\n<p>Em seguida, abordaremos a funcionalidade das opera\u00e7\u00f5es em janela e o c\u00f3digo correspondente.<\/p>\n<p><img decoding=\"async\" alt=\"O livro &#039;Kafka Streams em A\u00e7\u00e3o. Aplica\u00e7\u00f5es e Microservi\u00e7os para Trabalhar em Tempo Real&#039;\" src=\"\/wp-content\/uploads\/67d9d8d8acb1370a7c7fe5cd9a8b6aa7.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<h3>Tipos de janelas<\/h3>\n<p>\nNo Kafka Streams, existem tr\u00eas tipos de janelas:<\/p>\n<ul>\n<li>janelas de sess\u00e3o;<\/li>\n<li>janelas de \u201ctumbling\u201d;<\/li>\n<li>janelas deslizantes\/\u201chopping\u201d.<\/li>\n<\/ul>\n<p>\nQual escolher depende dos requisitos de neg\u00f3cios. As janelas de \u201ctumbling\u201d e \u201chopping\u201d t\u00eam limites de tempo, enquanto as janelas de sess\u00e3o est\u00e3o ligadas \u00e0s a\u00e7\u00f5es dos usu\u00e1rios \u2014 a dura\u00e7\u00e3o da(s) sess\u00e3o(\u00f5es) \u00e9 determinada exclusivamente pela atividade do usu\u00e1rio. O principal \u00e9 n\u00e3o esquecer que todos os tipos de janelas s\u00e3o baseados em carimbos de data\/hora dos registros, e n\u00e3o no tempo do sistema.<\/p>\n<p>Em seguida, implementaremos nossa topologia com cada um dos tipos de janelas. O c\u00f3digo completo ser\u00e1 apresentado apenas no primeiro exemplo, para os outros tipos de janelas nada mudar\u00e1, exceto o tipo da opera\u00e7\u00e3o em janelas.<\/p>\n<h3>Janelas de sess\u00e3o<\/h3>\n<p>\nAs janelas de sess\u00e3o diferem significativamente de todos os outros tipos de janelas. Elas s\u00e3o limitadas n\u00e3o tanto pelo tempo, mas pela atividade do usu\u00e1rio (ou pela atividade da entidade que voc\u00ea gostaria de rastrear). Janelas de sess\u00e3o s\u00e3o demarcadas por per\u00edodos de inatividade.<\/p>\n<p>A Figura 5.12 ilustra o conceito de janelas de sess\u00e3o. Uma sess\u00e3o menor se fundir\u00e1 com a sess\u00e3o \u00e0 sua esquerda. J\u00e1 a sess\u00e3o \u00e0 direita ser\u00e1 separada, pois ocorre ap\u00f3s um longo per\u00edodo de inatividade. As janelas de sess\u00e3o s\u00e3o baseadas nas a\u00e7\u00f5es dos usu\u00e1rios, mas aplicam marcas de data\/hora das entradas para determinar a qual sess\u00e3o a entrada pertence.<\/p>\n<p><img decoding=\"async\" alt=\"O livro &#039;Kafka Streams em A\u00e7\u00e3o. Aplica\u00e7\u00f5es e Microservi\u00e7os para Trabalhar em Tempo Real&#039;\" src=\"\/wp-content\/uploads\/d39a5db7af7d6aa2194802622b2b47fd.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<h3>Uso de janelas de sess\u00e3o para rastrear transa\u00e7\u00f5es de mercado<\/h3>\n<p>\nUsaremos janelas de sess\u00e3o para capturar informa\u00e7\u00f5es sobre transa\u00e7\u00f5es de mercado. A implementa\u00e7\u00e3o das janelas de sess\u00e3o \u00e9 mostrada na listagem 5.5 (que pode ser encontrada no arquivo src\/main\/java\/bbejeck\/chapter_5\/CountingWindowingAndKTableJoinExample.java).<\/p>\n<p><img decoding=\"async\" alt=\"O livro &#039;Kafka Streams em A\u00e7\u00e3o. Aplica\u00e7\u00f5es e Microservi\u00e7os para Trabalhar em Tempo Real&#039;\" src=\"\/wp-content\/uploads\/2dcbd9a36baec0e746aad165121451b3.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nA maioria das opera\u00e7\u00f5es desta topologia voc\u00ea j\u00e1 encontrou antes, ent\u00e3o n\u00e3o h\u00e1 necessidade de revis\u00e1-las aqui novamente. Mas h\u00e1 alguns novos elementos que discutiremos agora.<\/p>\n<p>Em qualquer opera\u00e7\u00e3o groupBy, geralmente alguma opera\u00e7\u00e3o de agrega\u00e7\u00e3o \u00e9 realizada (agrega\u00e7\u00e3o, resumo ou contagem). Pode-se realizar uma agrega\u00e7\u00e3o acumulativa com um total \u00e0 vista ou uma agrega\u00e7\u00e3o de janela, na qual registros dentro de uma janela de tempo definida s\u00e3o considerados.<\/p>\n<p>O c\u00f3digo da listagem 5.5 conta o n\u00famero de transa\u00e7\u00f5es dentro das janelas de sess\u00e3o. Na Fig. 5.13, essas a\u00e7\u00f5es s\u00e3o analisadas passo a passo.<\/p>\n<p>Com a chamada windowedBy(SessionWindows.with(twentySeconds).until(fifteenMinutes)), criamos uma janela de sess\u00e3o com um intervalo de inatividade de 20 segundos e um intervalo de reten\u00e7\u00e3o de 15 minutos. O intervalo de inatividade de 20 segundos significa que o aplicativo incluir\u00e1 qualquer registro que chegar dentro de 20 segundos do t\u00e9rmino ou in\u00edcio da sess\u00e3o atual na sess\u00e3o atual (ativa).<\/p>\n<p><img decoding=\"async\" alt=\"O livro &#039;Kafka Streams em A\u00e7\u00e3o. Aplica\u00e7\u00f5es e Microservi\u00e7os para Trabalhar em Tempo Real&#039;\" src=\"\/wp-content\/uploads\/9bd47b04698086872fd135b4c67eb938.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nEm seguida, indicamos qual opera\u00e7\u00e3o de agrega\u00e7\u00e3o deve ser realizada na janela de sess\u00e3o \u2014 neste caso, contagem. Se o registro de entrada estiver fora do intervalo de inatividade (de qualquer lado do carimbo de data\/hora), o aplicativo cria uma nova sess\u00e3o. O intervalo de reten\u00e7\u00e3o significa manter a sess\u00e3o durante um determinado tempo e permite dados atrasados que ficam al\u00e9m do per\u00edodo de inatividade da sess\u00e3o, mas ainda podem ser anexados. Al\u00e9m disso, o in\u00edcio e o fim da nova sess\u00e3o resultante da fus\u00e3o correspondem ao carimbo de data\/hora mais antigo e mais recente.<\/p>\n<p>Vamos considerar alguns registros do m\u00e9todo count para ver como as sess\u00f5es funcionam (tabela 5.1).<\/p>\n<p><img decoding=\"async\" alt=\"O livro &#039;Kafka Streams em A\u00e7\u00e3o. Aplica\u00e7\u00f5es e Microservi\u00e7os para Trabalhar em Tempo Real&#039;\" src=\"\/wp-content\/uploads\/ec04aae466d88c2d2349474069c8d541.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nAo receber registros, procuramos por sess\u00f5es existentes com a mesma chave, com o tempo de t\u00e9rmino inferior ao carimbo de data\/hora atual \u2014 intervalo de inatividade e um tempo de in\u00edcio maior que o carimbo de data\/hora atual + intervalo de inatividade. Com isso, quatro registros da tabela 5.1 se fundem em uma \u00fanica sess\u00e3o da seguinte forma.<\/p>\n<p>1. O registro 1 \u00e9 recebido primeiro, portanto, o tempo de in\u00edcio \u00e9 igual ao tempo de t\u00e9rmino e \u00e9 00:00:00.<\/p>\n<p>2. Em seguida, o registro 2 \u00e9 recebido, e procuramos por sess\u00f5es que terminam n\u00e3o antes de 23:59:55 e come\u00e7am n\u00e3o depois de 00:00:35. Encontramos o registro 1 e fundimos as sess\u00f5es 1 e 2. Pegamos o tempo de in\u00edcio da sess\u00e3o 1 (o mais antigo) e o tempo de t\u00e9rmino da sess\u00e3o 2 (o mais recente), ent\u00e3o nossa nova sess\u00e3o come\u00e7a em 00:00:00 e termina em 00:00:15.<\/p>\n<p>3. O registro 3 \u00e9 recebido, procuramos sess\u00f5es entre 00:00:30 e 00:01:10 e n\u00e3o encontramos nenhuma. Adicionamos a segunda sess\u00e3o para a chave 123-345-654,FFBE, que come\u00e7a e termina em 00:00:50.<\/p>\n<p>4. O registro 4 \u00e9 recebido, e procuramos sess\u00f5es entre 23:59:45 e 00:00:25. Desta vez, ambos os sessions \u2014 1 e 2 s\u00e3o encontrados. Todas as tr\u00eas sess\u00f5es se fundem em uma, com tempo de in\u00edcio 00:00:00 e tempo de t\u00e9rmino 00:00:15.<\/p>\n<p>Dos pontos discutidos nesta se\u00e7\u00e3o, vale lembrar os seguintes aspectos importantes:<\/p>\n<ul>\n<li>as sess\u00f5es n\u00e3o s\u00e3o janelas de tamanho fixo. A dura\u00e7\u00e3o da sess\u00e3o \u00e9 determinada pela atividade dentro de um intervalo de tempo definido;<\/li>\n<li>os carimbos de data\/hora nos dados determinam se um evento se encaixa em uma sess\u00e3o existente ou em um intervalo de inatividade.<\/li>\n<\/ul>\n<p>\nEm seguida, discutiremos o pr\u00f3ximo tipo de janela \u2014 as janelas \"rolantes\".<\/p>\n<h3>Janelas \"rolantes\"<\/h3>\n<p>\nAs janelas \"em tumulo\" (tumbling) capturam eventos que ocorrem dentro de um determinado intervalo de tempo. Imagine que voc\u00ea precisa capturar todas as transa\u00e7\u00f5es de bolsa de uma empresa a cada 20 segundos, ent\u00e3o voc\u00ea coleta todos os eventos durante esse intervalo. Ao final do intervalo de 20 segundos, a janela \"em tumulo\" se move para um novo intervalo de observa\u00e7\u00e3o de 20 segundos. A Figura 5.14 ilustra essa situa\u00e7\u00e3o.<\/p>\n<p><img decoding=\"async\" alt=\"O livro &#039;Kafka Streams em A\u00e7\u00e3o. Aplica\u00e7\u00f5es e Microservi\u00e7os para Trabalhar em Tempo Real&#039;\" src=\"\/wp-content\/uploads\/e90e560d9ddda5e2e524b7c387ad9874.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nComo voc\u00ea pode ver, todos os eventos recebidos nos \u00faltimos 20 segundos est\u00e3o inclu\u00eddos na janela. Quando esse intervalo de tempo termina, uma nova janela \u00e9 criada.<\/p>\n<p>O Listado 5.6 apresenta o c\u00f3digo que demonstra o uso de janelas \"em tumulo\" para capturar transa\u00e7\u00f5es de bolsa a cada 20 segundos (voc\u00ea pode encontr\u00e1-lo no arquivo src\/main\/java\/bbejeck\/chapter_5\/CountingWindowingAndKtableJoinExample.java).<\/p>\n<p><img decoding=\"async\" alt=\"O livro &#039;Kafka Streams em A\u00e7\u00e3o. Aplica\u00e7\u00f5es e Microservi\u00e7os para Trabalhar em Tempo Real&#039;\" src=\"\/wp-content\/uploads\/e055bb1b288c7d500b64372fe3fbf064.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nGra\u00e7as a essa pequena mudan\u00e7a na chamada do m\u00e9todo TimeWindows.of, \u00e9 poss\u00edvel usar uma janela \"em tumulo\". Neste exemplo, n\u00e3o h\u00e1 chamada para o m\u00e9todo until(), portanto, ser\u00e1 utilizado o intervalo de reten\u00e7\u00e3o padr\u00e3o, que \u00e9 de 24 horas.<\/p>\n<p>Finalmente, \u00e9 hora de passar para a \u00faltima das op\u00e7\u00f5es de janelas \u2014 as janelas \"deslizantes\" (hopping).<\/p>\n<h3>Janelas deslizantes (hopping)<\/h3>\n<p>\nAs janelas deslizantes \/ \"hopping\" (sliding\/hopping) s\u00e3o semelhantes \u00e0s janelas \"em tumulo\", mas com uma pequena diferen\u00e7a. As janelas deslizantes n\u00e3o esperam o t\u00e9rmino do intervalo de tempo antes de criar uma nova janela para processar eventos recentes. Elas iniciam novos c\u00e1lculos ap\u00f3s um intervalo de espera menor do que a dura\u00e7\u00e3o da janela.<\/p>\n<p>Para ilustrar as diferen\u00e7as entre janelas \"em tumulo\" e \"deslizantes\", voltamos ao exemplo de contagem de transa\u00e7\u00f5es de bolsa. Nosso objetivo ainda \u00e9 contar o n\u00famero de transa\u00e7\u00f5es, mas n\u00e3o gostar\u00edamos de esperar todo o per\u00edodo de tempo antes de atualizar o contador. Em vez disso, vamos atualizar o contador a cada intervalos de tempo mais curtos. Por exemplo, vamos contar o n\u00famero de transa\u00e7\u00f5es a cada 20 segundos, mas atualizar o contador a cada 5 segundos, como mostrado na Fig. 5.15. Isso nos d\u00e1 tr\u00eas janelas de resultados com dados sobrepostos.<\/p>\n<p><img decoding=\"async\" alt=\"O livro &#039;Kafka Streams em A\u00e7\u00e3o. Aplica\u00e7\u00f5es e Microservi\u00e7os para Trabalhar em Tempo Real&#039;\" src=\"\/wp-content\/uploads\/415c8cd9f2b60d453a1a01c3bc99331f.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nO Listado 5.7 apresenta o c\u00f3digo para definir janelas deslizantes (voc\u00ea pode encontr\u00e1-lo no arquivo src\/main\/java\/bbejeck\/chapter_5\/CountingWindowingAndKtableJoinExample.java).<\/p>\n<p><img decoding=\"async\" alt=\"O livro &#039;Kafka Streams em A\u00e7\u00e3o. Aplica\u00e7\u00f5es e Microservi\u00e7os para Trabalhar em Tempo Real&#039;\" src=\"\/wp-content\/uploads\/ab2d1a64380d256d3fb084e16597417c.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nUma janela \"rolante\" pode ser transformada em uma janela \"saltitante\" ao adicionar uma chamada ao m\u00e9todo advanceBy(). No exemplo dado, o intervalo de reten\u00e7\u00e3o \u00e9 de 15 minutos.<\/p>\n<p>Neste se\u00e7\u00e3o, voc\u00ea viu como limitar os resultados de agrega\u00e7\u00e3o com janelas temporais. Em particular, gostaria que voc\u00ea se lembrasse das seguintes tr\u00eas coisas desta se\u00e7\u00e3o:<\/p>\n<ul>\n<li>o tamanho das janelas de sess\u00e3o \u00e9 limitado n\u00e3o pelo intervalo de tempo, mas pela atividade dos usu\u00e1rios;<\/li>\n<li>as janelas \"rolantes\" fornecem uma vis\u00e3o dos eventos dentro de um per\u00edodo de tempo definido;<\/li>\n<li>a dura\u00e7\u00e3o das janelas \"saltitantes\" \u00e9 fixa, mas elas s\u00e3o frequentemente atualizadas e podem conter entradas que se sobrep\u00f5em em todas as janelas.<\/li>\n<\/ul>\n<p>\nA seguir, aprenderemos como converter KTable de volta em KStream para jun\u00e7\u00e3o.<\/p>\n<h3>5.3.3. Jun\u00e7\u00e3o de objetos KStream e KTable<\/h3>\n<p>\nNo cap\u00edtulo 4, discutimos a jun\u00e7\u00e3o de dois objetos KStream. Agora, precisamos aprender como juntar KTable e KStream. Isso pode ser necess\u00e1rio pela seguinte raz\u00e3o simples. KStream \u00e9 um fluxo de registros, enquanto KTable \u00e9 um fluxo de atualiza\u00e7\u00f5es de registros, mas \u00e0s vezes pode ser necess\u00e1rio adicionar contexto adicional ao fluxo de registros com atualiza\u00e7\u00f5es do KTable.<\/p>\n<p>Vamos pegar dados sobre o n\u00famero de transa\u00e7\u00f5es de a\u00e7\u00f5es e juntar isso com not\u00edcias do mercado relacionadas a setores espec\u00edficos da ind\u00fastria. Isso \u00e9 o que precisamos fazer para alcan\u00e7ar isso, levando em considera\u00e7\u00e3o o c\u00f3digo j\u00e1 existente.<\/p>\n<ol>\n<li>Converter o objeto KTable com dados sobre o n\u00famero de transa\u00e7\u00f5es de a\u00e7\u00f5es em KStream, substituindo a chave pela chave que representa o setor correspondente a esse s\u00edmbolo de a\u00e7\u00f5es.<\/li>\n<li>Criar um objeto KTable que l\u00ea dados do t\u00f3pico de not\u00edcias do mercado. Este novo KTable ser\u00e1 categorizado por setores da ind\u00fastria.<\/li>\n<li>Juntar as atualiza\u00e7\u00f5es de not\u00edcias com as informa\u00e7\u00f5es sobre o n\u00famero de transa\u00e7\u00f5es de a\u00e7\u00f5es por setores da ind\u00fastria.<\/li>\n<\/ol>\n<p>\nAgora vamos ver como implementar este plano de a\u00e7\u00e3o.<\/p>\n<h3>Converter KTable em KStream<\/h3>\n<p>\nPara converter KTable em KStream, \u00e9 necess\u00e1rio fazer o seguinte.<\/p>\n<ol>\n<li>Chamar o m\u00e9todo KTable.toStream().<\/li>\n<li>Usando a chamada do m\u00e9todo KStream.map, substituir a chave pelo nome do setor da ind\u00fastria e, em seguida, extrair do objeto Windowed uma inst\u00e2ncia de TransactionSummary.<\/li>\n<\/ol>\n<p>\nVamos encadear essas opera\u00e7\u00f5es da seguinte forma (o c\u00f3digo pode ser encontrado no arquivo src\/main\/java\/bbejeck\/chapter_5\/CountingWindowingAndKtableJoinExample.java) (listagem 5.8).<\/p>\n<p><img decoding=\"async\" alt=\"O livro &#039;Kafka Streams em A\u00e7\u00e3o. Aplica\u00e7\u00f5es e Microservi\u00e7os para Trabalhar em Tempo Real&#039;\" src=\"\/wp-content\/uploads\/0d43c2650f6e66e2816ed383da3a29c2.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nComo estamos realizando a opera\u00e7\u00e3o KStream.map, a reparti\u00e7\u00e3o para a inst\u00e2ncia KStream retornada \u00e9 feita automaticamente ao seu uso em uma jun\u00e7\u00e3o.<\/p>\n<p>Conclu\u00edmos o processo de convers\u00e3o, agora precisamos criar um objeto KTable para ler as not\u00edcias de mercado.<\/p>\n<h3>Cria\u00e7\u00e3o do KTable para not\u00edcias de mercado<\/h3>\n<p>\nFelizmente, para criar o objeto KTable, \u00e9 suficiente uma \u00fanica linha de c\u00f3digo (este c\u00f3digo pode ser encontrado no arquivo src\/main\/java\/bbejeck\/chapter_5\/CountingWindowingAndKtableJoinExample.java) (listagem 5.9).<\/p>\n<p><img decoding=\"async\" alt=\"O livro &#039;Kafka Streams em A\u00e7\u00e3o. Aplica\u00e7\u00f5es e Microservi\u00e7os para Trabalhar em Tempo Real&#039;\" src=\"\/wp-content\/uploads\/6e83a393fdc9ab74fda4cbdddddb5213.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nVale ressaltar que n\u00e3o \u00e9 necess\u00e1rio especificar nenhum objeto Serde, uma vez que nas configura\u00e7\u00f5es s\u00e3o utilizados Serde de string. Al\u00e9m disso, com a aplica\u00e7\u00e3o da enumera\u00e7\u00e3o EARLIEST, a tabela \u00e9 preenchida com registros desde o in\u00edcio.<\/p>\n<p>Agora podemos prosseguir para a etapa final \u2014 a jun\u00e7\u00e3o.<\/p>\n<h3>Jun\u00e7\u00e3o de atualiza\u00e7\u00f5es de not\u00edcias com dados sobre o n\u00famero de transa\u00e7\u00f5es<\/h3>\n<p>\nA cria\u00e7\u00e3o da jun\u00e7\u00e3o n\u00e3o apresenta dificuldades. Usaremos a jun\u00e7\u00e3o \u00e0 esquerda, caso n\u00e3o haja not\u00edcias de mercado para a ind\u00fastria correspondente (o c\u00f3digo necess\u00e1rio pode ser encontrado no arquivo src\/main\/java\/bbejeck\/chapter_5\/CountingWindowingAndKtableJoinExample.java) (listagem 5.10).<\/p>\n<p><img decoding=\"async\" alt=\"O livro &#039;Kafka Streams em A\u00e7\u00e3o. Aplica\u00e7\u00f5es e Microservi\u00e7os para Trabalhar em Tempo Real&#039;\" src=\"\/wp-content\/uploads\/92ed70f98927d2f778ad14dbc2a5aa26.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nEste operador leftJoin \u00e9 bastante simples. Ao contr\u00e1rio das jun\u00e7\u00f5es do cap\u00edtulo 4, o m\u00e9todo JoinWindow n\u00e3o \u00e9 utilizado, pois na jun\u00e7\u00e3o KStream-KTable, para cada chave no KTable, existe apenas um registro. Essa jun\u00e7\u00e3o n\u00e3o \u00e9 restrita por tempo: o registro pode estar presente ou n\u00e3o no KTable. A principal conclus\u00e3o \u00e9 que objetos KTable podem enriquecer o KStream com dados de refer\u00eancia menos frequentemente atualizados.<\/p>\n<p>Agora vamos considerar uma maneira mais eficiente de enriquecer eventos do KStream.<\/p>\n<h3>5.3.4. Objetos GlobalKTable<\/h3>\n<p>\nComo voc\u00ea entendeu, h\u00e1 a necessidade de enriquecer os fluxos de eventos ou de adicionar contexto a eles. No cap\u00edtulo 4, voc\u00ea viu jun\u00e7\u00f5es entre dois objetos KStream, e na se\u00e7\u00e3o anterior \u2014 a jun\u00e7\u00e3o entre KStream e KTable. Em todos esses casos, \u00e9 necess\u00e1rio a reparti\u00e7\u00e3o do fluxo de dados ao mapear chaves para um novo tipo ou valor. \u00c0s vezes, a reparti\u00e7\u00e3o \u00e9 feita explicitamente, e \u00e0s vezes o Kafka Streams faz isso automaticamente. A reparti\u00e7\u00e3o \u00e9 necess\u00e1ria porque as chaves mudaram, e os registros devem estar nas novas parti\u00e7\u00f5es, caso contr\u00e1rio, a jun\u00e7\u00e3o se tornar\u00e1 imposs\u00edvel (isso foi discutido no cap\u00edtulo 4, na se\u00e7\u00e3o 'Reparti\u00e7\u00e3o de dados' da subse\u00e7\u00e3o 4.2.4).<\/p>\n<h3>A re-partitioning tem seu custo<\/h3>\n<p>\nA re-partitioning requer gastos \u2014 custos adicionais de recursos para criar t\u00f3picos intermedi\u00e1rios, manter dados duplicados em mais um t\u00f3pico; isso tamb\u00e9m significa aumento da lat\u00eancia devido \u00e0 grava\u00e7\u00e3o e leitura desse t\u00f3pico. Al\u00e9m disso, se for necess\u00e1rio realizar jun\u00e7\u00e3o em mais de um aspecto ou dimens\u00e3o, \u00e9 preciso organizar as jun\u00e7\u00f5es em cadeia, mapear registros com novas chaves e repetir o processo de re-partitioning.<\/p>\n<h3>Juntando conjuntos de dados menores<\/h3>\n<p>\nEm alguns casos, o volume de dados de refer\u00eancia com o qual se pretende juntar \u00e9 relativamente pequeno, de modo que suas c\u00f3pias completas podem caber localmente em cada um dos n\u00f3s. Para tais situa\u00e7\u00f5es, o Kafka Streams possui a classe GlobalKTable.<\/p>\n<p>As inst\u00e2ncias de GlobalKTable s\u00e3o \u00fanicas, pois o aplicativo replica todos os dados em cada n\u00f3. E como em cada n\u00f3 est\u00e3o presentes todos os dados, n\u00e3o h\u00e1 necessidade de particionar o fluxo de eventos pela chave dos dados de refer\u00eancia para que esteja dispon\u00edvel em todas as parti\u00e7\u00f5es. Com os objetos GlobalKTable, tamb\u00e9m \u00e9 poss\u00edvel realizar jun\u00e7\u00f5es sem chave. Vamos voltar a um dos exemplos anteriores para demonstrar essa possibilidade.<\/p>\n<h3>Juntando objetos KStream com objetos GlobalKTable<\/h3>\n<p>\nNa subse\u00e7\u00e3o 5.3.2, realizamos a agrega\u00e7\u00e3o em janelas de transa\u00e7\u00f5es de bolsa por clientes. Os resultados dessa agrega\u00e7\u00e3o eram mais ou menos os seguintes:<\/p>\n<pre><code class=\"plaintext\">{customerId='074-09-3705', stockTicker='GUTM'}, 17\n{customerId='037-34-5184', stockTicker='CORK'}, 16<\/code><\/pre>\n<p>\nEmbora esses resultados correspondessem ao objetivo proposto, seria mais conveniente se tamb\u00e9m fossem exibidos o nome do cliente e o nome completo da empresa. Para adicionar o nome do comprador e a raz\u00e3o social, podem ser realizadas jun\u00e7\u00f5es normais, mas isso exigiria duas mapea\u00e7\u00f5es de chaves e re-partitioning. Com GlobalKTable, \u00e9 poss\u00edvel evitar os custos dessas opera\u00e7\u00f5es.<\/p>\n<p>Para isso, utilizaremos o objeto countStream do listagem 5.11 (o c\u00f3digo correspondente pode ser encontrado no arquivo src\/main\/java\/bbejeck\/chapter_5\/GlobalKTableExample.java), unindo-o a dois objetos GlobalKTable.<\/p>\n<p><img decoding=\"async\" alt=\"O livro &#039;Kafka Streams em A\u00e7\u00e3o. Aplica\u00e7\u00f5es e Microservi\u00e7os para Trabalhar em Tempo Real&#039;\" src=\"\/wp-content\/uploads\/fc4d91bbe062ceb94f5650224840b81e.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nJ\u00e1 discutimos isso anteriormente, ent\u00e3o n\u00e3o vou repetir. Mas quero ressaltar que o c\u00f3digo na fun\u00e7\u00e3o toStream().map foi abstra\u00eddo em um objeto-fun\u00e7\u00e3o para melhorar a legibilidade em vez de usar uma express\u00e3o lambda incorporada.<\/p>\n<p>A pr\u00f3xima etapa \u00e9 declarar duas inst\u00e2ncias de GlobalKTable (o c\u00f3digo correspondente pode ser encontrado no arquivo src\/main\/java\/bbejeck\/chapter_5\/GlobalKTableExample.java) (listagem 5.12).<\/p>\n<p><img decoding=\"async\" alt=\"O livro &#039;Kafka Streams em A\u00e7\u00e3o. Aplica\u00e7\u00f5es e Microservi\u00e7os para Trabalhar em Tempo Real&#039;\" src=\"\/wp-content\/uploads\/db3918895f174c8cfb5fc927b55f87a1.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p>Observe que os nomes dos t\u00f3picos s\u00e3o descritos por meio de tipos enumerados.<\/p>\n<p>Agora que preparamos todos os componentes, resta escrever o c\u00f3digo para a jun\u00e7\u00e3o (que pode ser encontrado no arquivo src\/main\/java\/bbejeck\/chapter_5\/GlobalKTableExample.java) (listagem 5.13).<\/p>\n<p><img decoding=\"async\" alt=\"O livro &#039;Kafka Streams em A\u00e7\u00e3o. Aplica\u00e7\u00f5es e Microservi\u00e7os para Trabalhar em Tempo Real&#039;\" src=\"\/wp-content\/uploads\/799360cc99f1920c190a61fd4685d4ff.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nEmbora este c\u00f3digo contenha duas jun\u00e7\u00f5es, elas est\u00e3o organizadas em uma cadeia, pois separadamente nenhum de seus resultados \u00e9 utilizado. Os resultados s\u00e3o exibidos no final de toda a opera\u00e7\u00e3o.<\/p>\n<p>Ao executar a opera\u00e7\u00e3o de jun\u00e7\u00e3o acima, voc\u00ea receber\u00e1 resultados da seguinte forma:<\/p>\n<pre><code class=\"plaintext\">{customer='Barney, Smith' company=\"Exxon\", transactions= 17}<\/code><\/pre>\n<p>\nA ess\u00eancia n\u00e3o mudou, mas esses resultados s\u00e3o mais claros.<\/p>\n<p>Se contar com o cap\u00edtulo 4, voc\u00ea j\u00e1 viu v\u00e1rios tipos de jun\u00e7\u00f5es em a\u00e7\u00e3o. Eles est\u00e3o listados na tabela 5.2. Esta tabela reflete as capacidades de jun\u00e7\u00e3o relevantes para a vers\u00e3o 1.0.0 do Kafka Streams; em lan\u00e7amentos futuros, pode haver altera\u00e7\u00f5es.<\/p>\n<p><img decoding=\"async\" alt=\"O livro &#039;Kafka Streams em A\u00e7\u00e3o. Aplica\u00e7\u00f5es e Microservi\u00e7os para Trabalhar em Tempo Real&#039;\" src=\"\/wp-content\/uploads\/8e4cf35c64a8431bda43a5e748de275f.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nPara concluir, quero lembrar o essencial: voc\u00ea pode juntar fluxos de eventos (KStream) e fluxos de atualiza\u00e7\u00f5es (KTable) usando um estado local. Al\u00e9m disso, se o tamanho dos dados de refer\u00eancia n\u00e3o for muito grande, voc\u00ea pode usar o objeto GlobalKTable. O GlobalKTable replica todas as parti\u00e7\u00f5es em cada um dos n\u00f3s da aplica\u00e7\u00e3o Kafka Streams, garantindo assim a disponibilidade de todos os dados, independentemente de qual parti\u00e7\u00e3o o chave corresponde.<\/p>\n<p>Em seguida, veremos a capacidade do Kafka Streams que permite observar mudan\u00e7as de estado sem consumir dados do t\u00f3pico Kafka.<\/p>\n<h3>5.3.5. Estado dispon\u00edvel para consultas<\/h3>\n<p>\nJ\u00e1 realizamos v\u00e1rias opera\u00e7\u00f5es envolvendo estado e sempre exibimos os resultados no console (para fins de desenvolvimento) ou os gravamos em um t\u00f3pico (para fins de produ\u00e7\u00e3o). Ao gravar resultados em um t\u00f3pico, \u00e9 necess\u00e1rio usar um consumidor Kafka para visualiz\u00e1-los.<\/p>\n<p>A leitura de dados desses t\u00f3picos pode ser considerada uma forma de vis\u00f5es materializadas (materialized views). Para nossas necessidades, podemos usar a defini\u00e7\u00e3o de vis\u00e3o materializada da \"Wikipedia\": \"... um objeto f\u00edsico do banco de dados que cont\u00e9m os resultados da execu\u00e7\u00e3o de uma consulta. Por exemplo, pode ser uma c\u00f3pia local de dados remotos, ou um subconjunto de linhas e\/ou colunas de uma tabela ou resultados de jun\u00e7\u00e3o, ou uma tabela din\u00e2mica obtida atrav\u00e9s de agrega\u00e7\u00e3o\" (https:\/\/en.wikipedia.org\/wiki\/Materialized_view).<\/p>\n<p>Kafka Streams tamb\u00e9m permite executar consultas interativas (interactive queries) em reposit\u00f3rios de estado, o que possibilita a leitura direta dessas vis\u00f5es materializadas. \u00c9 importante notar que a consulta ao reposit\u00f3rio de estado \u00e9 uma opera\u00e7\u00e3o de \"somente leitura\". Com isso, voc\u00ea n\u00e3o precisa se preocupar em acidentalmente deixar o estado inconsistente durante o processamento de dados pelo aplicativo.<\/p>\n<p>A capacidade de realizar consultas diretas nos reposit\u00f3rios de estado \u00e9 de grande import\u00e2ncia. Isso significa que \u00e9 poss\u00edvel criar aplicativos \u2014 pain\u00e9is de informa\u00e7\u00f5es sem precisar obter os dados do consumidor Kafka previamente. Isso tamb\u00e9m aumenta a efici\u00eancia do aplicativo, j\u00e1 que n\u00e3o \u00e9 necess\u00e1rio reescrever os dados:<\/p>\n<ul>\n<li>gra\u00e7as \u00e0 localidade dos dados, eles podem ser acessados rapidamente;<\/li>\n<li>\u00e9 evitada a duplica\u00e7\u00e3o de dados, pois eles n\u00e3o s\u00e3o gravados em um reposit\u00f3rio externo.<\/li>\n<\/ul>\n<p>\nO mais importante que eu gostaria que voc\u00ea lembrasse: \u00e9 poss\u00edvel executar consultas diretamente ao estado a partir do aplicativo. N\u00e3o se pode subestimar as possibilidades que isso oferece. Em vez de consumir dados do Kafka e armazenar registros em um banco de dados para o aplicativo, voc\u00ea pode consultar os reposit\u00f3rios de estado com o mesmo resultado. As consultas diretas aos reposit\u00f3rios de estado significam menos volume de c\u00f3digo (aus\u00eancia de consumidor) e menos software (aus\u00eancia de necessidade de uma tabela de banco de dados para armazenar resultados).<\/p>\n<p>Cobrimos uma quantidade consider\u00e1vel de informa\u00e7\u00f5es neste cap\u00edtulo, portanto, por um momento, vamos pausar nossa discuss\u00e3o sobre consultas interativas a reposit\u00f3rios de estado. Mas n\u00e3o se preocupe: no cap\u00edtulo 9, vamos criar um aplicativo simples \u2014 um painel de controle com consultas interativas. Para demonstrar as consultas interativas e as possibilidades de integr\u00e1-las em aplicativos Kafka Streams, ser\u00e3o utilizados alguns exemplos deste e do cap\u00edtulo anterior.<\/p>\n<h3>Resumo<\/h3>\n<p><\/p>\n<ul>\n<li>Os objetos KStream representam fluxos de eventos, compar\u00e1veis a inser\u00e7\u00f5es em um banco de dados. Os objetos KTable representam fluxos de atualiza\u00e7\u00f5es, mais semelhantes a atualiza\u00e7\u00f5es em um banco de dados. O tamanho do objeto KTable n\u00e3o cresce, registros antigos s\u00e3o substitu\u00eddos por novos.<\/li>\n<li>Os objetos KTable s\u00e3o necess\u00e1rios para opera\u00e7\u00f5es de agrega\u00e7\u00e3o.<\/li>\n<li>Com opera\u00e7\u00f5es de janelas, \u00e9 poss\u00edvel segmentar dados agregados em intervalos de tempo.<\/li>\n<li>Gra\u00e7as aos objetos GlobalKTable, \u00e9 poss\u00edvel acessar dados de refer\u00eancia em qualquer parte do aplicativo, independentemente da divis\u00e3o por parti\u00e7\u00f5es.<\/li>\n<li>\u00c9 poss\u00edvel realizar jun\u00e7\u00f5es entre os objetos KStream, KTable e GlobalKTable.<\/li>\n<\/ul>\n<p>\nAt\u00e9 agora, concentramos nossa aten\u00e7\u00e3o na cria\u00e7\u00e3o de aplicativos Kafka Streams usando o DSL KStream de alto n\u00edvel. Embora a abordagem de alto n\u00edvel permita criar programas limpos e concisos, seu uso envolve um certo compromisso. Trabalhar com o DSL KStream significa aumentar a concis\u00e3o do c\u00f3digo em troca de uma diminui\u00e7\u00e3o no controle. No pr\u00f3ximo cap\u00edtulo, abordaremos a API de baixo n\u00edvel de n\u00f3s processadores e exploraremos outros compromissos. Os programas ser\u00e3o mais longos do que eram at\u00e9 agora, mas teremos a possibilidade de criar praticamente qualquer n\u00f3 processador que pudermos precisar.<\/p>\n<p>\u2192 Saiba mais sobre o livro em <noindex><a rel=\"nofollow\" href=\"https:\/\/www.piter.com\/collection\/best\/product\/kafka-streams-v-deystvii-prilozheniya-i-mikroservisy-dlya-raboty-v-realnom-vremeni\">site da editora<\/a><\/noindex><\/p>\n<p>\u2192 Para os usu\u00e1rios do Habr, desconto de 25% com o cupom \u2014 <b>Kafka Streams<\/b><\/p>\n<p>\u2192 Ap\u00f3s o pagamento da vers\u00e3o impressa do livro, um livro eletr\u00f4nico ser\u00e1 enviado por e-mail.<br \/>\n<br \/>Fonte: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/piter\/blog\/457756\/\">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\u043e\u0436\u0438\u0442\u0435\u043b\u0438! \u042d\u0442\u0430 \u043a\u043d\u0438\u0433\u0430 \u043f\u043e\u0434\u043e\u0439\u0434\u0435\u0442 \u0434\u043b\u044f \u043b\u044e\u0431\u043e\u0433\u043e \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0445\u043e\u0447\u0435\u0442 \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u0432 \u043f\u043e\u0442\u043e\u043a\u043e\u0432\u043e\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0435. \u041f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u043b\u0443\u0447\u0448\u0435 \u0438\u0437\u0443\u0447\u0438\u0442\u044c Kafka \u0438 Kafka Streams. \u0411\u044b\u043b\u043e \u0431\u044b \u043d\u0435\u043f\u043b\u043e\u0445\u043e \u0437\u043d\u0430\u0442\u044c \u0438 \u0441\u0430\u043c \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a Kafka, \u043d\u043e \u044d\u0442\u043e \u043d\u0435 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e: \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u0432\u0430\u043c \u0432\u0441\u0435, \u0447\u0442\u043e \u043d\u0443\u0436\u043d\u043e. \u041e\u043f\u044b\u0442\u043d\u044b\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 Kafka, \u043a\u0430\u043a \u0438 \u043d\u043e\u0432\u0438\u0447\u043a\u0438, \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u044d\u0442\u043e\u0439 \u043a\u043d\u0438\u0433\u0435 \u043e\u0441\u0432\u043e\u044f\u0442 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 [&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-35786","post","type-post","status-publish","format-standard","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.3 - aioseo.com -->\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\/pt\/blog\/administrirovanie\/kniga-kafka-streams-v-dejstvii-prilozheniya-i-mikroservisy-dlya-raboty-v-realnom-vremeni\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.3\" \/>\n\t\t<meta property=\"og:locale\" content=\"pt_PT\" \/>\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\u041a\u043d\u0438\u0433\u0430 \u00abKafka Streams \u0432 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0438. \u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u044b \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438\u00bb | ProHoster\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/pt\/blog\/administrirovanie\/kniga-kafka-streams-v-dejstvii-prilozheniya-i-mikroservisy-dlya-raboty-v-realnom-vremeni\" \/>\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=\"2019-10-31T19:06:19+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2019-10-31T19:06:19+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\udd47Livro \"Kafka Streams em a\u00e7\u00e3o. Aplicativos e microsservi\u00e7os para trabalho em tempo real\" | ProHoster","description":"","canonical_url":"https:\/\/prohoster.info\/pt\/blog\/administrirovanie\/kniga-kafka-streams-v-dejstvii-prilozheniya-i-mikroservisy-dlya-raboty-v-realnom-vremeni","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"pt_PT","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\u041a\u043d\u0438\u0433\u0430 \u00abKafka Streams \u0432 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0438. \u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u044b \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438\u00bb | ProHoster","og:url":"https:\/\/prohoster.info\/pt\/blog\/administrirovanie\/kniga-kafka-streams-v-dejstvii-prilozheniya-i-mikroservisy-dlya-raboty-v-realnom-vremeni","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":"2019-10-31T19:06:19+00:00","article:modified_time":"2019-10-31T19:06:19+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"35786","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":"2026-01-22 00:45:19","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-03-01 01:56:32","updated":"2026-01-22 00:45:19","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/posts\/35786","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/comments?post=35786"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/posts\/35786\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/media?parent=35786"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/categories?post=35786"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/tags?post=35786"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}