Este artigo explora nossa decisão de abandonar a arquitetura reativa em nosso projeto de software. Iremos nos aprofundar nos princípios básicos dos sistemas reativos, nos benefícios da E/S sem bloqueio e nos desafios que enfrentamos com uma abordagem reativa.
Reactive engloba um conjunto de princípios e diretrizes que visam a construção de sistemas e aplicações distribuídas responsivas, caracterizado por:
Um dos principais benefícios dos sistemas reativos é o uso de E/S sem bloqueio. Essa abordagem evita o bloqueio de threads durante operações de E/S, permitindo que um único thread lide com múltiplas solicitações simultaneamente. Isso pode melhorar significativamente a eficiência do sistema em comparação com o bloqueio tradicional de E/S.
No multithreading tradicional, as operações de bloqueio representam desafios significativos na otimização de sistemas (Figura 1). Aplicativos gananciosos que consomem memória excessiva são ineficientes e penalizam outros aplicativos, muitas vezes necessitando de solicitações de recursos adicionais, como memória, CPU ou máquinas virtuais maiores.
Figura 1 – Multithreading tradicional
As operações de E/S são essenciais para os sistemas modernos e gerenciá-las com eficiência é fundamental para evitar comportamentos gananciosos. Os sistemas reativos empregam E/S sem bloqueio, permitindo que um baixo número de threads do sistema operacional lide com inúmeras operações de E/S simultâneas.
Embora a E/S sem bloqueio ofereça benefícios substanciais, ela introduz um novo modelo de execução distinto das estruturas tradicionais. A programação reativa surgiu para resolver esse problema, pois mitiga a ineficiência dos threads da plataforma ociosos durante as operações de bloqueio (Figura 2).
Figura 2 – Ciclo de eventos reativos
O Quarkus aproveita um mecanismo reativo desenvolvido com Eclipse Vert.x e Netty, facilitando interações de E/S sem bloqueio. Mutiny, a abordagem preferida para escrever código reativo com Quarkus, adota um paradigma orientado a eventos, em que as reações são desencadeadas por eventos recebidos.
Mutiny oferece dois tipos preguiçosos e orientados a eventos:
Embora os sistemas reativos ofereçam benefícios, encontramos vários desafios durante o desenvolvimento:
"Na verdade, a proporção de tempo gasto lendo versus escrevendo é bem superior a 10 para 1. Estamos constantemente lendo código antigo como parte do esforço para escrever novo código. ...[Portanto,] facilitar a leitura torna é mais fácil escrever."
― Robert C. Martin, Código Limpo: Um Manual de Artesanato de Software Ágil
Aqui está um exemplo de código reativo usando Mutiny para ilustrar a complexidade:
Multi.createFrom().ticks().every(Duration.ofSeconds(15)) .onItem().invoke(() - > Multi.createFrom().iterable(configs()) .onItem().transform(configuration - > { try { return Tuple2.of(openAPIConfiguration, RestClientBuilder.newBuilder() .baseUrl(new URL(configuration.url())) .build(MyReactiveRestClient.class) .getAPIResponse()); } catch (MalformedURLException e) { log.error("Unable to create url"); } return null; }).collect().asList().toMulti().onItem().transformToMultiAndConcatenate(tuples - > { AtomicInteger callbackCount = new AtomicInteger(); return Multi.createFrom().emitter(emitter - > Multi.createFrom().iterable(tuples) .subscribe().with(tuple - > tuple.getItem2().subscribe().with(response - > { emitter.emit(callbackCount.incrementAndGet()); if (callbackCount.get() == tuples.size()) { emitter.complete(); } }) )); }).subscribe().with(s - > {}, Throwable::printStackTrace, () - > doSomethingUponComplete())) .subscribe().with(aLong - > log.info("Tic Tac with iteration: " aLong));
Project Loom, um desenvolvimento recente no ecossistema Java, promete mitigar os problemas associados ao bloqueio de operações. Ao permitir a criação de milhares de threads virtuais sem alterações de hardware, o Project Loom poderia potencialmente eliminar a necessidade de uma abordagem reativa em muitos casos.
"O Projeto Loom vai acabar com a Programação Reativa"
―Brian Goetz
Concluindo, nossa decisão de abandonar o estilo de arquitetura reativa é uma abordagem pragmática para a manutenção de longo prazo do nosso projeto. Embora os sistemas reativos ofereçam benefícios potenciais, os desafios que apresentaram para a nossa equipe superaram essas vantagens em nosso contexto específico.
É importante ressaltar que essa mudança não comprometeu o desempenho. Este é um resultado positivo, pois demonstra que uma arquitetura não reativa (imperativa) bem projetada pode fornecer o desempenho necessário sem a complexidade associada à arquitetura reativa no nosso caso.
À medida que olhamos para o futuro, o foco permanece na construção de uma base de código que não seja apenas funcional, mas também fácil de entender e manter para desenvolvedores de todos os níveis de experiência. Isso não apenas reduz o tempo de desenvolvimento, mas também promove uma melhor colaboração e compartilhamento de conhecimento dentro da equipe.
No gráfico abaixo, o eixo X representa a complexidade crescente de nossa base de código à medida que ela evolui, enquanto o eixo Y representa o tempo necessário para essas mudanças de desenvolvimento.
Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.
Copyright© 2022 湘ICP备2022001581号-3