BT

Disseminando conhecimento e inovação em desenvolvimento de software corporativo.

Contribuir

Tópicos

Escolha a região

Início Artigos Arquitetura de Microservices Multi-Runtime

Arquitetura de Microservices Multi-Runtime

Pontos Principais

  • Arquitetura de "microservice" como o design "12 fatores". Fornecendo diretrizes relacionadas ao ciclo de vida da entrega, rede, gerenciamento de estado e ligação a dependências externas.

  • No entanto, implementar esses princípios de forma consistente e com uma abordagem escalável e sustentável é desafiador.

  • As abordagens tradicionais voltadas para a tecnologia para abordar esses princípios incluíam Enterprise Service Bus (ESB) e Message-Oriented Middleware (MOM). Embora essas soluções ofereçam um bom conjunto de recursos, o principal desafio foi a arquitetura monolítica e o forte acoplamento tecnológico entre a lógica de negócios e a plataforma.

  • À medida que o cloud, containers e os orquestradores de containers (Kubernetes) se tornaram populares, surgiram novas soluções para abordar esses princípios. Por exemplo, o Knative para entrega, service meshes para rede e Camel-K para ligação e integração.

  • Com essa abordagem, a lógica de negócios (denominada "micrológica") forma o núcleo da aplicatção e podem ser criados poderosos componentes sidecar "mecha" disponíveis para utilizar.

  • Esse desacoplamento de componentes micrológicos e mecha, pode melhorar as operações do dia seguinte como correções e atualizações, e ajudar a manter a manutenção coesa ao longo prazo das unidades da lógica de negócios .

Criar boas aplicações distribuídas não é uma tarefa fácil: tais sistemas geralmente seguem os princípios de aplicações e microservices de 12 fatores. Precisam ser stateless, escalonáveis, configuráveis, liberados independentemente, distribuídos em containers, automatizáveis ​​e, às vezes, controlados por eventos e serverless. Uma vez criados, devem ser acessíveis e fáceis de atualizar a longo prazo. Encontrar um bom equilíbrio entre esses requisitos concorrentes com a tecnologia de hoje ainda é um empreendimento difícil.

Neste artigo, vou explorar como as plataformas distribuídas estão evoluindo para permitir esse equilíbrio e o mais importante, o que mais precisa acontecer na evolução dos sistemas distribuídos para facilitar a criação de arquiteturas distribuídas sustentáveis. Se você quiser me ver falando ao vivo sobre esse tópico, junte-se a mim na QCon London em março.

Necessidades de aplicações distribuídas

Para esta discussão, irei agrupar as necessidades de aplicativos modernos distribuídos em quatro categorias - ciclo de vida, rede, estado e ligação - e vou analisar brevemente como eles estão evoluindo nos últimos anos.

Necessidades de aplicações distribuídas

Ciclo de vida

Vamos começar com a base. Quando escrevemos uma parte da funcionalidade, a linguagem de programação determina as bibliotecas disponíveis no ecossistema, o formato do pacote e o tempo de execução. Por exemplo, Java usa o formato .jar, todas as dependências do Maven em um ecossistema e a JVM como runtime. Atualmente, com ciclos de releases mais rápidas, o mais importante no ciclo de vida são: capacidade de deploy, se recuperar de erros e dimensionar serviços de maneira automatizada. Esse grupo de recursos representa amplamente nossas necessidades no ciclo de vida das aplicações ..

Rede

Atualmente, quase todos as aplicações são distribuídas em algum sentido e, portanto, precisam de rede. Mas os sistemas distribuídos modernos precisam dominar a rede de uma perspectiva mais ampla. Começando com a descoberta de serviços e a recuperação de erros, permitindo técnicas modernas de release de software e todos os tipos de rastreamento etelemetria. Para nosso propósito, incluiremos nessa categoria os diferentes padrões de troca de mensagens, métodos ponto a ponto e pub/sub e mecanismos de roteamento inteligente.

Estado

Quando falamos de estado, normalmente trata-se do estado de serviço e por que é preferível não ter estado. Mas a própria plataforma que gerencia nossos serviços precisa de estado. Isso é necessário para executar orquestração e fluxos de trabalho confiáveis, singleton distribuído, agendamento temporal (cron jobs), idempotência, recuperação de erro com estado, armazenamento em cache etc. Todos os recursos listados aqui dependem do estado subjacente. Embora o gerenciamento de estado real não seja o escopo desta publicação, as distribuições primitivas e suas abstrações que dependem do estado são pertinentes.

Ligação

Os componentes de sistemas distribuídos não apenas precisam conversar entre si, mas também se integram a sistemas externos modernos ou legados. Isso requer conectores que podem converter vários tipos de protocolos, oferecer suporte a diferentes padrões de troca de mensagens como pesquisa orientada a eventos, solicitação/resposta, transformar formatos de mensagens e até ser capaz de executar procedimentos personalizados de recuperação de erros e mecanismos de segurança.

Sem entrar em cada casos de uso, os itens acima representam uma boa coleção de premissas comuns necessárias para criar bons sistemas distribuídos. Hoje, muitas plataformas oferecem esses recursos, mas o que estamos procurando neste artigo é como o uso desses recursos mudaram na última década e como será na próxima. Para comparação, vamos dar uma olhada na década passada e ver como o middleware baseado em Java atendeu a essas necessidades.

Limitações do middleware tradicional

Uma das soluções tradicionais mais conhecidas das necessidades listadas acima e que satisfazem a velha guarda é o Enterprise Service Bus (ESB) e suas variantes, como o Message Oriented Middleware, estruturas de integração mais leves entre outras. Um ESB é um middleware que permite a interoperabilidade entre ambientes heterogêneos usando uma arquitetura orientada a serviços (ou seja, SOA clássico).

Embora um ESB ofereça um bom conjunto de recursos, o principal desafio dos ESBs foi a arquitetura monolítica e o forte acoplamento tecnológico entre a lógica de negócios e a plataforma, o que levou à centralização tecnológica e organizacional. Quando um serviço era desenvolvido e implantado em um sistema desse tipo, ele era profundamente associado à estrutura do sistema distribuído, o que por sua vez, limitou a evolução do serviço. Frequentemente isso mais tarde acaba se tornando mais evidente na vida do software.

Aqui estão alguns dos problemas e limitações de cada categoria de necessidades que tornam os ESBs não tão úteis na era moderna.

Ciclo de vida

No middleware tradicional, geralmente há uma única linguagem suportada (como Java) em tempo de execução, que determina como o software será empacotado, quais bibliotecas estão disponíveis, com que frequência precisam ser corrigidas, etc. O serviço comercial precisa usar essas bibliotecas que o acopla firmemente à plataforma que está escrita na mesma linguagem. Na prática, isso leva a serviços coordenados e atualizações de plataforma, o que evita versões independentes e comuns de serviços e plataformas.

Rede

Embora uma peça tradicional de middleware tenha um conjunto de recursos avançados focado na interação com outros serviços internos e externos, existem algumas desvantagens importantes. Os recursos de rede estão centrados em uma linguagem principal e em suas tecnologias relacionadas. Por exemplo, na linguagem JAVA seria, JMS, JDBC, JTA, etc. Não menos importante, as responsabilidades e semânticas da rede também estão profundamente inseridas no serviço de negócios. Existem bibliotecas com abstrações para lidar com as responsabilidades da rede (como o projeto Hystrix, que já foi popular), mas as abstrações da biblioteca "vazam" no serviço do seu modelo de programação, padrões de troca e semântica de manipulação de erros e a própria biblioteca. Embora seja prático codificar toda a lógica de negócio no mesmo local que os recursos rede, isso acopla as duas responsabilidades em uma única implementação e, finalmente, em um caminho evolutivo único.

Estado

Para executar orquestração de serviço confiável, gerenciamento de processos de negócio e padrões de implementação como o Saga Pattern e outros processos de execução mais lentos, as plataformas exigem um estado persistente nos bastidores. Da mesma forma, ações temporais, como disparadores de timers e cron jobs, são construídos para terem estado e exigem que um banco de dados seja clusterizado e resiliente em um ambiente distribuído. A principal restrição aqui é o fato de que as bibliotecas e interfaces que interagem com o estado não são completamente abstraídas e desacopladas do service runtime. Normalmente, essas bibliotecas precisam ser configuradas com detalhes do banco de dados e residem no serviço, compartilhando as responsabilidades de semântica e dependência no domínio do aplicativo.

Ligação

Um dos principais motivos para o uso do middleware de integração é a capacidade de conectar-se a vários outros sistemas usando protocolos, formatos de dados e padrões de troca de mensagens diferentes. No entanto o fato desses conectores terem que conviver com a aplicação significa que as dependências precisam ser atualizadas e corrigidas juntamente com a lógica de negócios. Isso significa que o tipo e o formato dos dados devem ser convertidos no serviço. Portanto o código deve ser estruturado e o fluxo projetado de acordo com os padrões de troca de mensagens. Estes são alguns exemplos de como até os endpoint abstraídos influenciam a implementação do serviço no middleware tradicional.

Tendências cloud-native

O middleware tradicional é poderoso. Possui todos os recursos técnicos necessários, mas não possui a capacidade de alterar e dimensionar rapidamente, o que é exigido pelas necessidades de negócios digitais atuais. É para isso que a arquitetura de microservices e seus princípios orientadores para o design de modernas aplicações distribuídas estão sendo abordadas.

As idéias por trás dos microservices e seus requisitos técnicos contribuíram para a popularização e o uso generalizado de containers como Kubernetes. Isso deu início a uma nova maneira de inovação que influenciará a maneira como abordamos as aplicações distribuídas nos próximos anos. Vamos ver como o Kubernetes e as tecnologias relacionadas afetam cada grupo de requisitos.

Ciclo de vida

Containers e Kubernetes evoluíram a maneira como empacotamos, distribuímos e implantamos aplicações em um formato independente da linguagens de programação. Inclusive há muito conteúdo escrito que pretendo destacar-los aqui sobre os Kubernetes Patterns e o Efeito Kubernetes sobre os desenvolvedores. Observe no entanto, que para o Kubernetes a menor primitiva a ser gerenciada é o container e está focada em fornecer primitivas distribuídas no nível do container e no modelo de processo. Isso significa que ele faz um ótimo trabalho no gerenciamento dos aspectos do ciclo de vida das aplicações, verificação de integridade, recuperação, deployment e dimensionamento, mas não faz um bom trabalho aprimorando os outros aspectos das aplicações distribuídas que vivem dentro do container , como rede flexível, gerenciamento de estado e ligações.

Você pode notar que o Kubernetes possui cargas de trabalho com estado, service discovery, cron jobs e outros recursos. Isso é verdade, mas todas essas primitivas estão no nível do container, e dentro do container. Um desenvolvedor ainda precisa usar uma biblioteca específica de linguagem de programação para acessar os recursos mais granulares listados no começo deste artigo. É isso que impulsiona projetos como Envoy, Linkerd, Consul, Knative, Dapr, Camel-K e outros.

Rede

Acontece que a funcionalidade básica de rede em torno da descoberta de serviços fornecida pelo Kubernetes é uma boa base, mas não é suficiente para aplicações modernas. Com o número crescente de microservices e o ritmo mais rápido de implantações, as necessidades de estratégias de lançamento de versão mais avançadas, gerenciando segurança, métricas, rastreamento, recuperação e simulação de erros, etc. Isso tudo sem tocar no serviço, tornaram-se cada vez mais atraentes e criaram um nova categoria de software, chamada service mesh.

O que é mais surpreendente aqui é a tendência de mover as responsabilidades relacionadas de redes do serviço que contém a lógica de negócios, para fora e para um runtime separado, seja um sidecar ou um node level-agent. Hoje os service meshes podem fazer roteamento avançado, ajudar a testar, lidar com certos aspectos de segurança e até mesmo conversar em protocolos específicos de aplicações (por exemplo, o Envoy suporta Kafka, MongoDB, Redis, MySQL, etc.). Embora o service mesh como solução ainda não tenha uma ampla adoção, ele destacou um verdadeiro ponto problemático nos sistemas distribuídos e estou certo de que encontrará sua forma de existência.

Além do mecanismo de serviço típico, também existem outros projetos, como o Skupper, que confirmam a tendência de colocar os recursos de rede em um agente externo. O Skupper resolve os desafios de comunicação de vários clusters através de uma rede virtual de 7 camadas e oferece recursos avançados de roteamento e conectividade. Mas, em vez de incorporar o Skupper no service runtime do negócio, ele executa uma instância por namespace do Kubernetes que atua como um sidecar compartilhado.

Em resumo, o container e o Kubernetes deram um grande passo adiante no gerenciamento do ciclo de vida das aplicações. O service mesh e as tecnologias relacionadas atingem um verdadeiro ponto problemático e estabelecem as bases para mover mais responsabilidades para fora da aplicação e colocando-as nos proxies. Vamos ver o que vem a seguir.

Estado

Listamos anteriormente as principais primitivas de integração que dependem do estado. O gerenciamento de estado é difícil e deve ser delegado ao software de armazenamento especializado e serviços gerenciados. Esse não é o tópico aqui, mas usar estado em abstrações de linguagens neutras para ajudar nos casos de uso de integração. Atualmente, muitos esforços tentam oferecer primitivas com estado por trás de abstrações de linguagens neutras. O gerenciamento de fluxo de trabalho com estado é um recurso obrigatório em serviços baseados em cloud, como por exemplo, AWS Step Functions, Azure Durable Functions, etc. Nas implantações baseadas em container, CloudState e Dapr, ambos contam com o modelo de sidecar para oferecer melhor dissociação as abstrações com estado em aplicativos distribuídos.

O que espero também é abstrair todos os recursos listados acima com estado, em um runtime separado. Isso significaria que os gerenciamentos do fluxo de trabalhos, os singletons, a idempotência, os gerenciamentos de transações, os cron job e os tratamentos de erros com estado acontecem de maneira confiável em um sidecar (ou um agente no nível do host), ao invés de permanecer dentro do serviço. A lógica de negócios não precisa incluir tais dependências e semânticas na aplicação e pode solicitar declarativamente esse comportamento do ambiente de ligação. Por exemplo, um sidecar pode atuar como um cron job, consumidor idempotente e gerenciador de fluxo de trabalho. E a lógica de negócio ersonalizada pode ser chamada como retorno de chamada ou conectada a determinados estágios do fluxo de trabalho, manipulação de erros, invocações temporais ou solicitações idempotente exclusivas, etc.

Outro caso de uso com estado é o armazenamento em cache. Seja no cache da solicitação executada pela camada do service mesh ou no cache de dados, algo como Infinispan, Redis, Hazelcast, etc. Existem exemplos de como separar a aplicação dos recursos do cache.

Ligação

Enquanto estamos no tópico de desacoplar todas as necessidades distribuídas da aplicação, a tendência também continua com as ligações. Conectores, conversores de protocolo, transformações de mensagens, manipulação de erros e mediação de segurança podem ser separados do serviço. Ainda não estamos lá, mas há tentativas nesse sentido em projetos como Knative e Dapr. Mover todas essas responsabilidades para fora da aplicação resultará em um código focado na lógica de negócios muito menor. Esse código permaneceria em um runtime independente das necessidades do sistema distribuído que podem ser consumidas como recursos pré-empacotados.

Outra abordagem interessante é adotada pelo projeto Apache Camel-K. Em vez de usar um runtime do agente para acompanhar a aplicação principal, este projeto conta com um operador Kubernetes inteligente que cria aplicativos em tempo de execução com recursos adicionais da plataforma Kubernetes e Knative. Aqui, o agente único é o operador responsável por incluir as primitivas do sistema distribuído exigidas pela aplicação. A diferença é que algumas das primitivas distribuídas são adicionadas a aplicação em tempo de execução e outras ativadas na plataforma (que também podem incluir um sidecar).

Futuras tendências da arquitetura

Em termos gerais, podemos concluir que a comoditização de aplicações distribuídas movendo recursos para o nível da plataforma, atinge novas fronteiras. Além do ciclo de vida, agora podemos observar redes, abstração de estado, evento declarativo e ligações de endpoint também disponíveis no mercado, e os EIPs são os próximos nesta lista. Curiosamente, a comoditização está usando o modelo fora do processo (sidecars) para extensão de recursos, ao invés de bibliotecas em tempo de execução ou recursos puros da plataforma (como os novos recursos do Kubernetes).

Agora estamos nos aproximando do círculo completo movendo todos os recursos tradicionais do middleware (também conhecidos como ESBs) para outros eu runtimes e em breve, tudo o que precisamos fazer em nosso serviço será escrever a lógica de negócios.

Visão geral de plataformas middleware tradicionais e plataformas cloud native

Comparada à era tradicional do ESB, essa arquitetura desacopla melhor a lógica de negócios da plataforma, mas ainda não totalmente. Muitas primitivas distribuídas como os clássicos Enterprise Integration Patterns(EIPs): splitter, aggregator, filter, content-based router e padrões de processamento de streaming: map, filter, fold, join, merge, sliding windows; ainda precisam ser incluídos na aplicação que contém a lógica de negócios e muitos outros dependem de vários complementos distintos da plataforma e add-ons.

Se empilharmos os projetos cloud-native inovando nos diferentes domínios, teremos a seguinte imagem:

Microservices multi-runtime

O diagrama aqui é apenas para fins ilustrativos, escolhendo propositalmente projetos representativos e os mapeia para uma categoria de primitivas distribuídas. Na prática, você não utilizará todos esses projetos ao mesmo tempo pois alguns deles coincidem e não são modelos de carga de trabalho compatíveis. Então, como interpretar esse diagrama?

  • Kubernetes e containers deram um grande salto no gerenciamento do ciclo de vida de aplicações multi linguagens e estabeleceram as bases para futuras inovações;
  • As tecnologias de service mesh foram aprimoradas no Kubernetes com recursos avançados de rede e começaram a explorar esses recursos nas aplicações;
  • Embora o Knative se concentre principalmente nas cargas de trabalho serverless por meio de dimensionamento rápido, também atende às necessidades de orquestração de serviços e de ligações orientada a eventos;
  • O Dapr se baseia nas idéias do Kubernetes, Knative e Service Mesh e se aprofunda nos runtimes das aplicações para lidar com cargas de trabalho com estado, ligação e necessidades de integração, agindo como um moderno middleware distribuído.

Este diagrama é para ajudá-lo a visualizar que provavelmente no futuro, acabaremos usando vários runtimes para implementar os sistemas distribuídos. Esses múltiplos runtimes não são por causa de vários microservices, mas porque cada microservice será composto por vários runtimes, provavelmente dois - no caso o runtime da lógica de negócios customizado e o runtime das primitivas distribuídas.

Introdução a microservices multi-runtime

Aqui está uma breve descrição da arquitetura de microservices multi-runtime que está começando a se formar.

Você se lembra do filme Avatar e os "trajes mecânicos" da Amplified Mobility Platform (AMP) desenvolvida por cientistas para sair na natureza e poder explorar Pandora? Essa arquitetura multi-runtime é semelhante a esses trajes Mecha que dão superpoderes aos seus pilotos humanóides. No filme, você tem humanos vestindo "roupas" para ganhar força e acessar armas destrutivas. Nesta arquitetura de software, você tem sua lógica de negócios (referida como micrologic) formando o núcleo do aplicativo e o componente sidecar mecha que oferece poderosas primitivas distribuídas prontas para usar. O micrologic combinado com os recursos mecha formam um microservice de multi-runtime que usa recursos fora do processo para suas necessidades de sistema distribuídos. E a melhor parte é que o Avatar 2 será lançado em breve para ajudar a promover essa arquitetura. Finalmente, podemos substituir as sidecar de motos antigas por incríveis imagens mecha em todas as conferências de software ;-). Vamos ver os detalhes dessa arquitetura de software a seguir.

Este é um modelo de dois componentes semelhante a uma arquitetura cliente-servidor, em que cada componente é um runtime (aplicação) separada. É diferente de uma arquitetura pura de cliente-servidor, pois aqui os dois componentes estão localizados no mesmo host em uma rede confiável entre eles. Ambos os componentes têm a mesma importância e podem iniciar ações em qualquer direção e agir como cliente ou servidor. Um dos componentes é chamado Micrologic, e mantém a lógica de negócios desacoplada de quase todas as responsabilidades do sistema distribuído. O outro componente que acompanha é o Mecha, e fornece todos os recursos do sistema distribuído sobre os quais falamos neste artigo (exceto o ciclo de vida, que é um recurso da plataforma).

Arquitetura microservices Multi-runtime (fora de processo)

Pode haver uma implantação individual do Micrologic e do Mecha (conhecido como modelo sidecar) ou pode ser um Mecha compartilhado com runtimes do Micrologic. O primeiro modelo é mais apropriado em ambientes como o Kubernetes, e o último nas implantações de ponta.

Características do runtime Micrologic

Vamos explorar brevemente algumas das características do runtime Micrologic:

  • O componente Micrologic não é um microservice por si só. Ele contém a lógica de negócios que um microservice teria, mas essa lógica só pode funcionar em combinação com o componente Mecha. Por outro lado, os microservices são independentes e não possuem partes da funcionalidade geral ou parte do fluxo de processamento espalhadas em outros runtimes. A combinação de um Micrologic e seu Mecha, forma um microservice;
  • Essa também não é uma função ou arquitetura Serverless. O Serverless é conhecido principalmente por seus recursos que permitem escalar rapidamente para cima ou para baixo. Na arquitetura Serverless, uma função implementa uma única operação, que é a unidade de escalabilidade. Nesse sentido, uma função é diferente de uma Micrologic que implementa várias operações, mas a implementação não é de ponta a ponta. Mais importante ainda, a implementação das operações está espalhada runtimes Mecha e Micrologic;
  • Essa é uma forma especializada de arquitetura cliente-servidor, otimizada para o consumo de primitivas distribuídas conhecidas sem codificação. Além disso, se assumirmos que o Mecha desempenha a função de servidor, cada instância deve ser configurada especificamente para funcionar com o(s) client(s) individual(is). Não é uma instância de servidor genérica que visa oferecer suporte a vários clients ao mesmo tempo que uma arquitetura cliente-servidor típica;
  • O código do usuário no Micrologic não interage diretamente com outros sistemas e não implementa nenhuma primitiva de sistema distribuído. Ele interage com o Mecha usando padrões, como HTTP / gRPC, especificação CloudEvents, e o Mecha se comunica com outros sistemas usando recursos aprimorados e guiado pelas etapas e mecanismos configurados;
  • Embora a Micrologic seja responsável apenas pela implementação da lógica de negócios excluída das responsabilidades do sistema distribuído, ainda precisa no mínimo implementar algumas APIs. Deve permitir que o Mecha e a plataforma interajam através de APIs e protocolos predefinidos (por exemplo, seguindo os princípios de design cloud native para implantação do Kubernetes).

Características do runtime Mecha

Aqui estão algumas das características do runtime Mecha:

  • O Mecha é um componente genérico, altamente configurável e reutilizável, oferecendo primitivas distribuídas como recursos prontos para uso;
  • Cada instância do Mecha deve ser configurada para funcionar com um componente Micrologic (o modelo sidecar) ou configurada para ser compartilhada com alguns componentes;
  • O Mecha não faz nenhuma suposição sobre o Micrologic runtime. Trabalhando com microservices multi-linguagens ou mesmo sistemas monolíticos usando protocolos e formatos abertos, como HTTP / gRPC, JSON, Protobuf, CloudEvents;
  • As configurações do Mecha são declaradas em arquivos YAML ou JSON, que determina quais recursos devem ser ativados e como vinculá-los aos micrologic endpoint. Para interações com APIs especializadas, ela também podem ser fornecidas com algumas especificações, como OpenAPI, AsyncAPI, ANSI-SQL, etc. Para fluxos de trabalho com estado e compostos por várias etapas de processamento, uma especificação que pode ser utilizada é a Amazon State Language. Para integrações sem estado, os EIPs (Enterprise Integration Patterns) podem ser usados ​​com uma abordagem semelhante à DSL Camel-K YAML. O ponto principal aqui é que todas essas são simples definições multi-linguagens, declarativas e baseadas em texto, e que o Mecha pode atender sem codificar nada. Observe que estas são previsões para um futuro pois atualmente, não há Mechas para orquestração com estado ou EIPs, mas espero que os Mechas existentes (Envoy, Dapr, Cloudstate, etc.) comecem a adicionar esses recursos em breve. O Mecha é uma camada de abstração de primitivas distribuídas no nível do aplicativo.
  • Em vez de depender de vários agentes para propósitos diferentes, como proxy de rede, proxy de cache, proxy de ligação, pode haver um único Mecha fornecendo todos esses recursos. A implementação de alguns recursos, como armazenamento, persistência de mensagens, armazenamento em cache e etc. Seria conectada e apoiada por outros serviços local ou cloud;
  • Algumas responsabilidades do sistema distribuído em torno do gerenciamento do ciclo de vida fazem sentido serem fornecidas pela plataforma de gerenciamento, como o Kubernetes ou outros serviços em cloud, ao invés do Mecha runtime usando especificações abertas e genéricas, como o Open App Model.

Quais são os principais benefícios dessa arquitetura?

Os benefícios são evitar o acoplamento entre a lógica de negócios e a crescente lista de preocupações de sistemas distribuídos. Esses dois elementos dos sistemas de software têm uma dinâmica completamente diferente. A lógica de negócios é sempre única e o código personalizado é escrito internamente mudando com frequência, dependendo das prioridades da organização e da capacidade de execução. Por outro lado, as primitivas distribuídas são as que abordam as responsabilidades listadas neste post e são bem conhecidas. São desenvolvidos por fornecedores de software e consumidos como bibliotecas, containers ou serviços. Esse código muda de acordo com as prioridades do fornecedor, ciclos de lançamento, patches de segurança, regras de controle de código aberto, etc. Ambos os grupos têm pouca visibilidade e controle um sobre o outro.

As preocupações com o acoplamento na lógica de negócios e sistema distribuído em diferentes arquiteturas.

Os princípios de microservices ajudam a desacoplar os diferentes domínios de negócios por contextos delimitados, nos quais cada microservice pode evoluir independentemente. Mas a arquitetura de microservices não resolve as dificuldades decorrentes do acoplamento da lógica de negócios com as relacionadas ao middleware. Para certos microservices que são leves nos casos de uso de integração, isso pode não ser um grande fator. Porém, se o seu domínio envolver integrações complexas (o que está se tornando cada vez mais comum), seguir os princípios dos microservices não ajudará a proteger o acoplamento com o middleware. Mesmo que o middleware seja representado como bibliotecas incluídas em seus microservices, no momento em que você começar a migrar e alterar essas bibliotecas, o acoplamento se tornará aparente. E quanto mais primitivas distribuídas você precisar, mais acoplado à plataforma de integração você se tornará. Consumir o middleware em um euntome/processo separado em uma API predefinida ao invés de uma biblioteca, ajuda a evitar o acoplamento e permite a evolução independente de cada componente.

Essa também é a melhor maneira de distribuir e manter software de middleware complexo para fornecedores. Desde que as interações com o middleware sejam feitas através da comunicação entre processos, envolvendo APIs e padrões abertos, os fornecedores de software podem liberar patches e atualizações no seu ritmo. E os consumidores são livres para usar suas linguagens, bibliotecas, runtimes, métodos e processos que acharem melhor.

Quais são as principais desvantagens dessa arquitetura?

Comunicação entre processos. O fato de a lógica de negócios e a mecânica do middleware (aí você vê de onde vem o nome) dos sistemas distribuídos estão em runtimes diferentes e isso requer uma chamada HTTP ou gRPC em vez de uma chamada de método no processo. Porém, observe que não se trata de uma chamada de rede que deve ser enviada para uma máquina ou data center diferente. O Micrologic runtime e o Mecha devem ser colocados no mesmo host com baixa latência e probabilidade mínima de problemas de rede.

Complexidade. A próxima pergunta é se vale a complexidade do desenvolvimento e a manutenção desses sistemas para os benefícios obtidos. Eu acho que a resposta será cada vez mais inclinada a sim. Os requisitos dos sistemas distribuídos e o ritmo dos ciclos de lançamento de versão estão aumentando. E essa arquitetura otimiza isso. Escrevi há algum tempo que os desenvolvedores do futuro terão que ter habilidades de desenvolvimento híbrido. Essa arquitetura confirma e reforça ainda mais essa tendência. Parte da aplicação será escrita em uma linguagem de programação de alto nível e parte da funcionalidade será fornecida por componentes prontos para uso e que precisam ser configurados declarativamente. Ambas as partes são interconectadas, não em tempo de compilação ou por injeção de dependência no processo durante a inicialização, mas em tempo de implantação, por meio de comunicações entre processos. Esse modelo permite uma taxa mais alta de reutilização de software combinada com um ritmo mais rápido de mudança.

O que vem depois dos microservices não são funções

A arquitetura de microservices tem um objetivo claro. Otimização das mudanças! Ao dividir aplicações em domínios de negócios, essa arquitetura oferece o limite de serviço ideal para evolução e manutenção de software por meio de serviços desacoplados, gerenciados por equipes independentes e lançados também em um ritmo independente.

Se olharmos para o modelo de programação da arquitetura serverless, ele é baseado principalmente em funções. As funções são otimizadas para escalabilidade, e com as funções, dividimos todas as operações em um componente independente para que ele possa ser dimensionado rapidamente, de forma independentemente e sob demanda. Nesse modelo, a granularidade de implantação é uma função. E a função é escolhida porque é a construção do código que possui uma entrada cuja a taxa se correlaciona diretamente com o comportamento da escala. Essa é uma arquitetura otimizada para extrema escalabilidade, em vez de manutenção de longo prazo de sistemas complexos.

E o outro aspecto da arquitetura serverless, resultante da popularidade do AWS Lambda e de sua natureza operacional totalmente gerenciada? Nesse sentido, o "AWS Serverless" otimiza a velocidade de provisionamento, em detrimento da falta de controle e do bloqueio. Mas o aspecto totalmente gerenciado não é arquitetura de aplicações, é um modelo de consumo de software. É uma funcionalidade ortogonal, semelhante a consumir uma plataforma baseada em SaaS que em um mundo ideal, deveria estar disponível para qualquer tipo de arquitetura, seja monolítica, microservices, mecha ou funções. De várias maneiras, o AWS Lambda se assemelha a uma arquitetura Mecha totalmente gerenciada, com uma grande diferença: o Mecha não aplica o modelo de função, em vez disso, permite construções de código mais coesas em todo o domínio de negócios, separadas de todas as responsabilidades do middleware.

bution of code

Otimizações de arquitetura

A arquitetura Mecha, por outro lado, otimiza os microservices para um middleware independente. Embora os microservices são independentes um do outro, por outro lado eles são fortemente dependentes de primitivas distribuídas incorporadas. A arquitetura Mecha divide essas duas responsabilidades em euntimes separados, permitindo a liberação independente por equipes independentes. Essa separação aprimora as operações do dia seguinte (como correções e atualizações) e a manutenção a longo prazo das unidades coesas da lógica de negócios. Nesse sentido, a arquitetura Mecha é uma progressão natural da arquitetura de microservices, dividindo o software com base nos limites que causam maior atrito. Essa otimização oferece mais benefícios na forma de reutilização e evolução de software do que o modelo de função, que otimiza extremamente a escalabilidade às custas da distribuição excessiva de código.

Conclusão

Aplicações distribuídas têm muitos requisitos. Criar sistemas distribuídos eficazes requer várias tecnologias e uma boa abordagem para integração. Embora o middleware monolítico tradicional fornecesse todos os recursos técnicos necessários exigidos pelos sistemas distribuídos, faltava a capacidade de alterar, adaptar e escalar rapidamente, o que era exigido pelo negócio. É por isso que as idéias por trás das arquiteturas baseadas em microservices contribuíram para a rápida popularização de containers e Kubernetes. Com os desenvolvimentos mais recentes no cloud-native, agora estamos dando a volta completa ao mover todos os recursos tradicionais de middleware para a plataforma e para os runtimes auxiliares disponíveis no mercado.

Este acúmulo de recursos da aplicação está usando o modelo principalmente para um processo de extensão dos recursos, em vez de bibliotecas do runtime ou recursos puros da plataforma. Isso significa, que no futuro é muito provável que usaremos vários runtimes para implementar sistemas distribuídos. Contudo os múltiplos runtimes não serão por causa de vários microservices, mas porque cada microservice será composto de vários runtimes. Um runtime configurável pronto para usar com primitivas distribuídas.

Sobre o autor

Bilgin Ibryam é um dos principais arquitetos da Red Hat, committer e membro da Apache Software Foundation. Ele é um evangelista do open source, blogueiro, orador ocasional e autor dos livros Kubernetes Patterns e Camel Design Patterns. No seu dia-a-dia, Bilgin gosta de mentorear, codificar e levar os desenvolvedores a serem bem-sucedidos na criação de soluções open source. Seu trabalho atual concentra-se em blockchain, sistemas distribuídos, microservices, devops e desenvolvimento de aplicações cloud-native.

Avalie esse artigo

Relevância
Estilo/Redação

Conteúdo educacional

BT