You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
203 lines
11 KiB
203 lines
11 KiB
11 months ago
|
<!DOCTYPE html>
|
||
|
<html lang="en">
|
||
|
<head>
|
||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||
|
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||
|
|
||
|
<!-- Enable responsiveness on mobile devices-->
|
||
|
<!-- viewport-fit=cover is to support iPhone X rounded corners and notch in landscape-->
|
||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, viewport-fit=cover">
|
||
|
|
||
|
<title>Julio Biason .Me 4.3</title>
|
||
|
|
||
|
<!-- CSS -->
|
||
|
<link rel="stylesheet" href="https://blog.juliobiason.me/print.css" media="print">
|
||
|
<link rel="stylesheet" href="https://blog.juliobiason.me/poole.css">
|
||
|
<link rel="stylesheet" href="https://blog.juliobiason.me/hyde.css">
|
||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=PT+Sans:400,400italic,700|Abril+Fatface">
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
</head>
|
||
|
|
||
|
<body class=" ">
|
||
|
|
||
|
<div class="sidebar">
|
||
|
<div class="container sidebar-sticky">
|
||
|
<div class="sidebar-about">
|
||
|
|
||
|
<a href="https://blog.juliobiason.me"><h1>Julio Biason .Me 4.3</h1></a>
|
||
|
|
||
|
<p class="lead">Old school dev living in a 2.0 dev world</p>
|
||
|
|
||
|
|
||
|
</div>
|
||
|
|
||
|
<ul class="sidebar-nav">
|
||
|
|
||
|
|
||
|
<li class="sidebar-nav-item"><a href="/">English</a></li>
|
||
|
|
||
|
<li class="sidebar-nav-item"><a href="/pt">Português</a></li>
|
||
|
|
||
|
<li class="sidebar-nav-item"><a href="/tags">Tags (EN)</a></li>
|
||
|
|
||
|
<li class="sidebar-nav-item"><a href="/pt/tags">Tags (PT)</a></li>
|
||
|
|
||
|
|
||
|
</ul>
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
|
||
|
<div class="content container">
|
||
|
|
||
|
<div class="post">
|
||
|
<h1 class="post-title">Microserviços com 'Auto Cura'</h1>
|
||
|
<span class="post-date">
|
||
|
2020-01-03
|
||
|
|
||
|
<a href="https://blog.juliobiason.me/pt/tags/microservicos/">#microserviços</a>
|
||
|
|
||
|
<a href="https://blog.juliobiason.me/pt/tags/auto-cura/">#auto cura</a>
|
||
|
|
||
|
<a href="https://blog.juliobiason.me/pt/tags/artefatos/">#artefatos</a>
|
||
|
|
||
|
</span>
|
||
|
<p>As <a href="https://blog.juliobiason.me/pt/code/microservices-artifact-input-state/">discussões</a>
|
||
|
<a href="https://blog.juliobiason.me/pt/code/microservices-artifact-ejection/">anteriores</a> que eu levantei
|
||
|
sobre microserviços foram um prelúdio para uma coisa que eu não consegui uma
|
||
|
solução perfeita: como é que microserviços se "curam" quando faltam dados?</p>
|
||
|
<span id="continue-reading"></span>
|
||
|
<p>Pequena recapitulação antes de falar sobre o problema: Microserviços produzem
|
||
|
artefatos; artefatos ou são enviados para frente por um message broker para
|
||
|
outros serviços ou mantidos no mesmo microserviço para requisições futuras;
|
||
|
microserviços podem escutar mais de uma fonte de dados para construir seus
|
||
|
artefatos.</p>
|
||
|
<p>Anteriormente eu mencionei um exemplo de um microserviço de placares que
|
||
|
produz um artefato com o placar de cada time e o nome dos jogadores que
|
||
|
fizeram os gols. Esse microserviço poderia ouvir:</p>
|
||
|
<ol>
|
||
|
<li>A fila de times: pode ser necessária para que possamos mostrar o nome ou
|
||
|
sigla do time na requisição de placar; uma vez que um time aparece num
|
||
|
campeonato, o microserviço adiciona o mesmo no seu estado para referência
|
||
|
futura<sup class="footnote-reference"><a href="#1">1</a></sup>.</li>
|
||
|
<li>A file da jogadores: o mesmo que acima, para que o microserviço possa
|
||
|
retornar o nome, apelido, número da camisa ou alguma coisa relacionada com
|
||
|
o jogador; de novo, o serviço fica escutando a fila de jogadores e os
|
||
|
guarda em seu estado.</li>
|
||
|
<li>A fila de partidas: se uma partida for acontecer, ela tem que ter um
|
||
|
placar, provavelmente começando com 0 sem nenhum jogador na lista de gols;
|
||
|
isso é feito apenas para evitar problemas com serviços pedindo placares de
|
||
|
partidas que ainda não começaram ou que não tiveram gols ainda; de qualquer
|
||
|
forma, o artefato necessário já vai estar pronto para ser entregue.</li>
|
||
|
<li>A fila de narrações: escutando a fila de narrações, o microserviço de
|
||
|
placar irá detectar gols, atualizar seu estado e produzir o artefato
|
||
|
atualizado.</li>
|
||
|
</ol>
|
||
|
<p>A palavra chave da lista acima é "poderia": dependendo da forma como os
|
||
|
microserviços <em>e</em> as mensagens são construídas, pode não ser necessário ter
|
||
|
acesso a tudo isso.</p>
|
||
|
<h2 id="usando-mensagens-completas">Usando mensagens completas</h2>
|
||
|
<p>Vamos começar com a forma mais simples de evitar escutar todas essas filas:
|
||
|
utilizando mensagens completas.</p>
|
||
|
<p>Numa mensagem completa, todos os campos relacionados são enviados junto com a
|
||
|
informação principal. Usando o exemplo acima, o serviço poderia ouvir apenas
|
||
|
as filas de partidas e narração, mas esperar que a mensagem de "NovaPartida"
|
||
|
teria os nomes dos tipos, suas siglas, escudos, provavelmente o ID e assim
|
||
|
pode diante; da mesma forma para a mensagem de "NovaNarração": ela contém o
|
||
|
nome do jogador, o apelido, número da camisa, ID e assim pode diante.</p>
|
||
|
<p>O problema com mensagens completas é que elas tentem a ficarem maiores com o
|
||
|
tempo: Com mais microserviços sendo adicionados ao sistema, mais campos vai
|
||
|
sendo necessários -- e ignorados por serviços que não os precisam.</p>
|
||
|
<p>O lado positivo de mensagens completas é que um microserviço sempre terá toda
|
||
|
a informação necessária, mantendo o número de filas a serem escutadas baixo.
|
||
|
Esse formato também facilita a adição de outros serviços no sistema: se o
|
||
|
mesmo começar com um estado em branco, ele poderá construir o mesmo a partir
|
||
|
do zero, porque toda a informação <em>já está lá</em>.</p>
|
||
|
<h2 id="escutar-as-filas-basicas-pedir-o-resto">Escutar as filas básicas, pedir o resto</h2>
|
||
|
<p>Quase como a solução acima, o serviço escuta apenas as filas de narrações e
|
||
|
partidas, mas uma vez que detecta alguma informação faltante (por exemplo, o
|
||
|
evento de narração cita um jogador, mas esse jogador não existe no estado), o
|
||
|
serviço faria uma requisição por essa informação mais "fria" (jogadores, times
|
||
|
e produtos não são atualizados com muita frequência, por exemplo) para outro
|
||
|
serviço e preencheria essa informação no seu estado.</p>
|
||
|
<p>Isso significa que esse microserviço agora, ao invés de saber apenas como
|
||
|
escutar filas, também precisa ter informações de outros serviços (aqueles que
|
||
|
processam e armazenam os dados frios) e suas interfaces -- e, de forma geral,
|
||
|
também requisitaria um serviço de descoberta presente no sistema. Esses
|
||
|
microserviços seriam aqueles de "duas caras", que recebem informações,
|
||
|
armazenam o estado, produzem o artefato mas tem uma interface de requisições
|
||
|
ao invés de simplesmente receber, processar e passar pra frente. Fazer cache
|
||
|
aqui também seria recomendado, para que um serviço não faça um "flood" de
|
||
|
requisições da mesma informação -- e atualizações de tempo em tempo podem
|
||
|
fazer sentido em algumas situações.</p>
|
||
|
<p>As mensagens seria menores (porque é enviado apenas o ID do time/jogador) e a
|
||
|
recuperação de informações acontece apenas quando necessária, mas onde é
|
||
|
reduzido o número de escutas nas filas, é aumentado o número de requisições.
|
||
|
Assim como na utilização de mensagens completas, um novo serviço poderia
|
||
|
facilmente construir seu estado a partir do zero sem qualquer problema -- irá
|
||
|
fazer um monte de requisições, mas terá, eventualmente, todas as informações
|
||
|
necessárias.</p>
|
||
|
<h2 id="escutas-todas">Escutas todas</h2>
|
||
|
<p>Essa é exatamente a solução apresentada no exemplo acima: o microserviço fica
|
||
|
escutando todas os eventos das filas com eventos relacionados e constrói o
|
||
|
estado a partir deles.</p>
|
||
|
<p>Um problema dessa solução: uma vez que as filas são assíncronas, pode
|
||
|
acontecer um problema com a ordenação dos dados, com gols chegando antes dos
|
||
|
jogadores (por vários motivos). Nesse caso... o que o serviço faz? Rejeita o
|
||
|
gol na esperança que o jogador apareça, para evitar uma inconsistência dos
|
||
|
dados, e o que o message broker coloque o evento novamente no fim da fila?</p>
|
||
|
<p>Uma solução seriam serviços que, junto com este, escutem por um dado
|
||
|
específico: o microserviço de placares escuta as quatro filas citadas, mas há
|
||
|
um microserviço escutando apenas a fila de jogadores. Esse serviço iria
|
||
|
processar os dados mais rapidamente que o placar, e serviria como "fallback"
|
||
|
no caso de dados faltantes, como na solução acima. Isso reduziria o tráfego de
|
||
|
rede, mas iria gerar dados duplicados em serviços diferentes -- embora esse
|
||
|
último ponto não deveria ser um problema em primeiro lugar.</p>
|
||
|
<p>Novos serviços iriam encontrar problemas, porque apesar de receberem
|
||
|
novos dados, eles não estavam presentes quando os dados frios foram
|
||
|
processados; eles vão precisar se comunicar com outros serviços para recuperar
|
||
|
essa informação, ou alguém teria que manualmente copiar os dados.</p>
|
||
|
<h2 id="fila-unica">Fila única</h2>
|
||
|
<p>As soluções acima trabalham com cada dado em sua própria fila, mas e se
|
||
|
pudéssemos colocar <em>todos</em> os eventos na mesma fila? Dessa forma, a ordenação
|
||
|
é assegurada (jogadores são sempre enfileirados antes dos gols, e os serviços
|
||
|
irão processar os jogadores antes de sequer verem que há um gol).</p>
|
||
|
<p>Isso reduz o número de filas a serem ouvidas, mas requer um bom design de
|
||
|
mensagens, especialmente se for utilizada alguma linguagem de tipagem
|
||
|
estática, que normalmente requer uma estrutura bem definida para serialização
|
||
|
e desserialização.</p>
|
||
|
<p>Mas ao mesmo tempo, resolve praticamente todos os problems: não existe
|
||
|
problema com a ordem de processamento, o número de filas a serem ouvidas é
|
||
|
baixo e as mensagens pequenas. Mas também faz com que novos serviços sofram
|
||
|
com a falta de dados frios, forçando-os a comunicar com outros serviços ou
|
||
|
terem os dados copiados manualmente quando levantados.</p>
|
||
|
<h1 id="e-qual-o-melhor">E qual o melhor?</h1>
|
||
|
<p>Honestamente, não faço ideia. Eu tenho uma certa preferência pelas mensagens
|
||
|
completas simplesmente porque simplifica a estrutura dos serviços, mesmo
|
||
|
sabendo que rede não é de graça; se eu usasse uma linguagem dinâmica, eu
|
||
|
provavelmente utilizaria a fila única. Mas, de novo, não acho que haja um
|
||
|
"tamanho único para todos".</p>
|
||
|
<p>Provavelmente existem outras opções arquiteturais para resolver esses
|
||
|
problemas, mas essas são as que eu consigo lembrar das conversas que tivemos
|
||
|
no trabalho.</p>
|
||
|
<div class="footnote-definition" id="1"><sup class="footnote-definition-label">1</sup>
|
||
|
<p>Vale notar que o microserviço pode simplesmente ignorar parte da
|
||
|
informação. Por exemplo, se o artefato produzido tem apenas a sigla do time,
|
||
|
o serviço pode remover o nome completamente de seu estado.</p>
|
||
|
</div>
|
||
|
|
||
|
</div>
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
</div>
|
||
|
|
||
|
</body>
|
||
|
|
||
|
</html>
|