Conteúdo


Além de palavras da moda: um breve histórico sobre padrões de microsserviços

Explore o impacto de padrões passados de design de software sobre a criação de microsserviços

Comments

Microsserviços são a grande novidade em desenvolvimento de aplicativo comercial. O termo microsserviço substituiu Agile, DevOps e RESTful como a nova grande palavra da moda que todos os currículos e palestras precisam apresentar. Mas os microsserviços são mais do que apenas um modismo passageiro. Na verdade, eles são a evolução de todos esses conceitos anteriores e uma abordagem que começou a mostrar a promessa de cortar diversos problemas de longa data no desenvolvimento de aplicativos. Além de ler este artigo, eu aconselho que você assista MicroservicesTV Episódio 13 e Episódio 14 (veja abaixo) para obter um firme domínio de como, onde e por que microsserviços evoluíram.

Começando pelo início

Para entender essa evolução, precisamos dar um passo para trás no tempo para examinar o que são microsserviços, o que eles substituíram e por que se tornaram necessários. Vamos começar pelo início dos anos 80 com a introdução da primeira principal tecnologia de distribuição de sistemas: Chamadas de Procedimento Remoto (RPC). RPC era o conceito por trás do RPC ONC inicial da Sun Microsystems, assim como a ideia básica por trás do DCE (1988) e do CORBA (1991).

Em cada uma dessas tecnologias, a ideia básica era fazer chamadas remotas transparentes para os desenvolvedores. A promessa era que se os desenvolvedores não precisassem se importar se as chamadas ou métodos de procedimentos que estavam chamando eram locais ou remotas, eles poderiam desenvolver sistemas maiores entre máquinas que poderiam evitar os problemas de escalabilidade e processamento e de memória que afetavam os sistemas da época. (Lembre-se de que os processadores mais comuns naquela época eram processadores de 16 bits com um espaço de endereço de 64 K!)

À medida que os processadores melhoraram e os espaços de endereço locais se tornaram maiores, esse problema se tornou menos importante. Além disso, o primeiro conjuntos de implementações grandes do DCE e do CORBA ensinaram aos arquitetos uma observação importante sobre computação distribuída:

Simplesmente porque algo pode ser distribuído, isso não significa que deve ser distribuído.

Quando os grandes espaços de memória se tornaram comuns, tornou-se evidente que fazer escolhas ruins sobre distribuição de seus métodos entre máquinas pode ter efeitos terríveis no desempenho do sistema. O push anterior para distribuir tudo resultou em muitos sistemas com interfaces muito informais — chegando ao ponto de distribuir getters e setters de variáveis em linguagens orientadas a objetos. Em um sistema como esse, o gasto adicional de rede superava em muito as vantagens de distribuição.

Isso nos levou a nosso primeiro padrão, que tinha a intenção de abordar a observação acima —e aquele foi um padrão que eu descobri de forma independente com John Crupi e Martin Fowler. Em cada caso, iniciamos com o livro de Erich Gamma Design Patterns: Elements of Reusable Object Oriented Design e observamos o padrão Façade. O padrão Façade estava relacionado a encapsular as interfaces não estruturadas de um sistema grande em uma única interface mais estruturada para reduzir a "informalidade". Colocando de outra forma, estava relacionado à redução da transversalidade da interface do sistema. A abordagem Session Façade que foi desenvolvida aplicava esse padrão a sistemas distribuídos identificando as interfaces chave de alta granularidade de um subsistema inteiro e expondo apenas essas para distribuição.

Implementamos nossas primeiras Session Façades com Enterprise JavaBeans (EJBs), que, embora bons quando você estava trabalhando apenas em Java, eram complicados, difíceis de depurar e não interoperáveis com outras linguagens ou com produtos de outros fornecedores. Aquela falta de interoperabilidade levou diretamente ao próximo esforço do início ao meio do ano 2000, o que viria a ser conhecido como Arquitetura Orientada a Serviços (SOA). A SOA não teve início com uma terminologia tão grandiosa, no entanto. Teve início como um esforço "faça a coisa mais simples que poderia possivelmente funcionar" chamado Simple Object Access Protocol (SOAP), liberado originalmente pela Microsoft em 1999.

Em sua essência, SOAP era nada mais do que uma maneira de chamar métodos de objetos por HTTP. Ele aproveitava dois artefatos diferentes do mundo da computação no início do ano 2000: o crescente suporte para HTTP em redes corporativas e o fato de que esse suporte incluía mecanismos para criação de log e depuração de chamadas de rede baseadas em texto.

O primeiro florescimento de esforço em torno do SOAP foi útil, pois estabeleceu rapidamente que seria possível interoperar facilmente entre sistemas implementados em muitas linguagens diferentes e em muitas plataformas diferentes. Mas o SOA falhou como um todo ao ir muito além desse início simples, incluindo camadas e camadas de conceitos adicionais além da simples chamada de método: manipulação de exceção, suporte a transações, segurança e assinaturas digitais foram todos incluídos no que alguns já consideravam como um protocolo complicado. Isso levou à próxima grande observação:

Tentar fazer uma chamada distribuída agir como uma chamada local sempre termina mal.

A indústria como um todo começou lentamente a andar rumo aos conceitos processuais em camadas inerentes ao SOAP e aos padrões WS-*. Em vez disso, o que se tornou mais difundido foi a adoção de Representational State Transfer (REST), que remonta à dissertação de Doutorado de Roy Fielding em 2000. O princípio fundamental de REST é absurdamente simples: trate HTTP como HTTP. Em vez de semântica em camada de chamada processual por HTTP, REST trata os verbos HTTP da maneira como foram especificados em termos de criar, ler, excluir e atualizar semântica. Também delineia uma maneira para especificar nomes de entidades exclusivos por meio de outro princípio aceito da web: o URI.

Ao mesmo tempo, a indústria também estava começando a rejeitar outro legado do mundo de Java Platform, Enterprise Edition (JEE) e SOA: a grande farm de servidores de aplicativos. Desde que Enterprise Java foi introduzido em 1999 (estranhamente como versão 1.2), havia uma tensão contínua entre proprietários de aplicativos e administradores de aplicativos.

Quando o JEE foi introduzido, muitas empresas se movimentaram para adotar a noção de um servidor de aplicativos como um host para diversos aplicativos diferentes, pois era semelhante a modelos de TI existentes do mundo de mainframe. Um único grupo de operações controlaria, monitoraria e manteria uma "farm" de servidores de aplicativos idênticos da Oracle ou da IBM e implementaria diferentes aplicativos departamentais nessa farm. Essa normatização e consistência era excelente para a equipe de operações e reduziu os custos operacionais em geral. Mas isso criava conflito com desenvolvedores de aplicativos, pois os ambientes de desenvolvimento e de teste eram grandes, difíceis de criar e precisavam do envolvimento da equipe de operações. Isso frequentemente significava que poderia levar meses até que novos ambientes fossem criados, tornando os projetos mais lentos e aumentando seus custos de desenvolvimento. Além disso, como esses ambientes estavam fora do controle da equipe, frequentemente havia inconsistências entre versões dos servidores, níveis de correção, dados de aplicativos e instalações de software entre ambientes.

O que os desenvolvedores preferiam eram plataformas de aplicativos menores e mais leves — geralmente servidores de aplicativos de software livre, como o Tomcat ou o Glassfish. Ao mesmo tempo, a complexidade de JEE estava sendo evitada a favor da suposta simplicidade da plataforma Spring, à medida que técnicas como Inversão de Controle e Injeção de Dependência se tornaram comuns. A lição aprendida com isso foi que as equipes de desenvolvimento descobriram que ter a capacidade de desenvolver e implementar de forma consistente seus próprios aplicativos em ambientes de desenvolvimento, teste e produção tão próximos uns aos outros quanto possível não era apenas mais rápido, mas menos fadado a erro, pois classes inteiras de erros decorrentes de inconsistências de ambientes foram eliminadas. Isso levou à próxima observação:

Sempre que possível, seus programas e seus ambientes de tempo de execução devem ser totalmente autocontidos.

Essas três observações estão no âmago do que Fowler descreve como sendo microsserviços. Um dos princípios de design de microsserviços de Fowler é que microsserviços são "organizados em torno de recursos de negócios". Isso origina diretamente da descoberta de que só porque você pode distribuir algo, isso não significa que você deveria. A noção completa do padrão Façade em suas várias encarnações estava relacionada à definição de uma API externa específica para um sistema ou subsistema. O subtexto disso era que essa API seria orientada pelos negócios. Fowler torna o contexto explícito.

Frequentemente, entender o que isso significa é um obstáculo para algumas equipes de desenvolvimento — elas simplesmente não estão acostumadas a projetar em termos de interfaces de negócios e podem se achar rapidamente retornando a interfaces técnicas (como Login ou Criação de Log). Nesses casos, o que muitas equipes acharam aplicáveis são vários padrões do livro de Eric Evans, Domain Driven Design. Especificamente, seus Padrões de Entidades e Agregados são úteis na identificação de conceitos de negócios específicos que são mapeados diretamente para microsserviços. Do mesmo modo, seu padrão de Serviços também fornece uma maneira para mapear operações que não correspondem a uma única entidade ou agregado na abordagem baseada em Entidade necessária para microsserviços.

De forma semelhante, a regra de Fowler para empregar "terminais inteligentes e canais burros" origina da experiência de que equipes usando EJBs, SOAP e outras tecnologias de distribuição complexas havia resultado na observação de que tentar fazer um sistema distribuído parecer local sempre terminava em lágrimas. Por fim, os princípios orientadores de Fowler sobre Governança Descentralizada e Gerenciamento de Dados Descentralizado originam da descoberta com árduo esforço de que seus programas e ambientes de tempo de execução devem ser autocontidos.

Onde isso nos deixa?

Fowler, Adrian Cockcroft e outros agora estabeleceram um caso convincente por que as equipes de desenvolvimento devem adotar microsserviços. Mas se dermos uma olhada nas maneiras como todas as lições que levaram à arquitetura de microsserviços foram aprendidas, podemos chegar à conclusão um pouco diferente da história centrada nos desenvolvedores que acabamos de contar. Especificamente, você precisa observar as realidades em fazer microsserviços funcionarem em um mundo corporativo de aplicativos existentes e também precisa perceber a ênfase adicional que a arquitetura de microsserviços coloca no lado das operações de DevOps.

Vivendo o mundo corporativo

A arquitetura de microsserviços começou a obter atenção após diversas histórias de sucesso serem publicadas por empresas como a Netflix, a Gilt.com e a Amazon. No entanto, todas essas empresas e muitos dos outros sucessos dos microsserviços compartilhavam uma coisa em comum — eram todas de empresas nascidas na web que estavam desenvolvendo novos aplicativos ou que não tinham um código base de legado substancial para substituir. Quando uma empresa tradicional adota microsserviços, um dos problemas que enfrenta após os primeiros aplicativos de crescimento sustentável serem escolhidos para testar o novo mundo de microsserviços é que alguns princípios da arquitetura de microsserviços, especialmente "Gerenciamento de Dados Descentralizado" e "Governança Descentralizada", são difíceis de instituir quando se deve refatorar um grande aplicativo monolítico.

Mas felizmente, uma abordagem para esse problema já existe há muitos anos no formato de um padrão que Martin Fowler documentou originalmente em 2004, vários anos antes de seu trabalho em microsserviços. Seu conceito é chamado de "padrão de aplicativo estrangulador" e deve ser usado para abordar o fato de que quase nunca você vive realmente em um lugar de crescimento sustentável. Os programas que mais precisam de microsserviços são os maiores e mais desagradáveis na web, mas, novamente, aproveitando a arquitetura da web,que pode nos fornecer uma estratégia para gerenciar a refatoração necessária.

O aplicativo estrangulador é um conceito simples que é baseado na analogia de um cipó que estrangula a árvore em torno da qual se enrola. A ideia é que você usa a estrutura de um aplicativo da web — o fato de ser desenvolvido a partir de URIs individuais que mapeiam funcionalidade para diferentes aspectos de um domínio de negócios — para dividir um aplicativo entre diferentes domínios funcionais e para substituir esses domínios por uma nova implementação baseada em microsserviços, um domínio por vez. Esses dois aspectos formam aplicativos separados que vivem lado a lado no mesmo espaço do URI. Ao longo do tempo, o novo aplicativo refatorado estrangula ou substitui o aplicativo original até que seja possível encerrar completamente o aplicativo monolítico.

Mas esse não é o único padrão que descobrimos ser útil para fazer a abordagem de microsserviços funcionar no mundo corporativo. Outro aspecto importante é que em muitos casos, uma equipe de desenvolvimento não tem o controle descentralizado de seus dados. Essa é a razão para um padrão que chamamos de Microsserviço Adaptador, que é uma extensão do padrão Adaptador original de Design Patterns de Erich Gamma e outros.

Em um Microsserviço Adaptador, você adapta entre duas APIs diferentes. A primeira é uma API orientada a negócios desenvolvida usando RESTful ou técnicas de sistema de mensagens leves e projetada usando-se as mesmas técnicas orientadas a domínio que um microsserviço tradicional. Mas a segunda API à qual ela se adapta é uma API de legado existente ou um serviço SOAP baseado em WS-* tradicional. Os puristas podem se objetar a essa abordagem e tentar convencer de que você não está adotando dados descentralizados, você não está usando microsserviços. No entanto, os dados corporativos existem por uma razão e, geralmente, há boas razões, além da inércia organizacional, para que tenham sido deixados lá. Pode ser que há um número significativo de aplicativos de legado que ainda precisarão de acesso a esses dados em seu formato atual que não pode ser facilmente adaptado para uma nova API ou, possivelmente, o enorme peso dos dados (frequentemente medidos em centenas de terabytes ou em petabytes) impossibilita a migração dos mesmos para um novo formato de propriedade de um único serviço.

Devolvendo ops a DevOps

Outro aspecto importante de microsserviços lida com o lado das operações do conjunto de práticas conhecido como DevOps. Isso tem as suas raízes em diversos padrões que foram originalmente desenvolvidos para gerenciamento de aplicativo convencional. Fowler enfatizou a importância disso em seu trabalho original sobre microsserviços, onde afirmou ser necessário adaptar a automação da infraestrutura como parte de um processo de DevOps desenvolvido em Entrega Contínua e Integração Contínua. No entanto, a necessidade disso nem sempre está clara para as equipes que começam a adotar microsserviços em uma pequena escala. O problema é que embora o uso de microsserviços facilite a mudança e a implementação de um único serviço rapidamente, também torna o esforço geral de gerenciar e manter um conjunto de serviços maior do que se fosse em um aplicativo monolítico correspondente.

Por exemplo, essa é uma razão pela qual muitas estruturas comuns, como a estrutura da Netflix para microsserviços e o Amalgam8, adaptam o padrão Registro de Serviço: evitando terminais de microsserviços específicos com codificação permanente em seu código, é possível mudar não apenas a implementação dos microsserviços de recebimento de dados, mas também permite que a escolha do local do serviço varia em diferentes estágios de seu pipeline de DevOps. Sem o Registro de Serviço, seu aplicativo apresentaria problemas rapidamente à medida que mudanças de código começassem a se propagar por uma cadeia de chamadas de microsserviços.

Essa ideia de atingir um melhor isolamento enquanto se torna possível, ao mesmo tempo, depurar mais facilmente os microsserviços está no âmago dos vários padrões de DevOps que identificamos, especialmente o ID de Correlação e o Agregador de Log. O padrão de ID de Correlação foi identificado e documentado em um formato específico no livro de Gregor Hohpe Enterprise Integration Patterns, mas agora vimos o conceito generalizado em projetos, como OpenTracing, que permitem propagação de rastreio por meio de diversos microsserviços gravados em diversas linguagens diferentes. O Agregador de Log é um novo padrão que foi implementado em diversos produtos comerciais de software livre (como o Cloud Foundry e a pilha ELK de software livre). Ele complementa os IDs de Correlação permitindo que os logs de diversos microsserviços diferentes sejam agregados em um único repositório que possa ser pesquisado. Juntos, eles permitem a depuração eficiente e compreensível de microsserviços, independentemente do número de serviços ou da profundidade de cada pilha de chamada.

Por fim, outro aspecto de DevOps que é uma ponte crucial entre os dois é aquele que Fowler destaca em seu artigo: a importância de projetar para falha. Especificamente, a estrutura Hystrix da Netflix se tornou uma parte importante de muitas implementações de microsserviços devido à sua implementação do padrão Disjuntor. Disjuntor foi documentado primeiramente no livro de 2007 de Michael Nygard Release It!. Com o Disjuntor, é possível evitar a perda de tempo manipulando falhas de recebimento de dados se você souber que elas já estão ocorrendo e será possível manipular isso plantando uma seção de código de Disjuntor nas chamadas de serviço de envio de dados que podem detectar quando um serviço de recebimento de dados está com mau funcionamento e evitar tentar chamá-lo. O benefício disso é que cada chamada "falha rapidamente" e é possível fornecer uma experiência geral melhor a seus usuários e evitar o gerenciamento indevido de recursos, como encadeamentos e conjuntos de conexões, quando você sabe que as chamadas de recebimento de dados estão destinadas a falhar.

Esse tipo de gerenciamento de recurso costumava ser território exclusivo das operações do lado de DevOps, mas a arquitetura de microsserviços une os dois lados de forma mais efetiva, já que ambos trabalham para tornarem os aplicativos resultantes mais confiáveis, com um melhor desempenho e mais resilientes.

O que vem em seguida?

Neste artigo e nos vídeos que o acompanham, exploramos os antecedentes históricos de microsserviços e examinamos como a arquitetura de microsserviços surgiu. Também discutimos os tipos de padrões que você precisa seguir para aplicar microsserviços com sucesso no mundo corporativo e que tipo de desafios podem ser encontrados ao aplicar a arquitetura de microsserviços.


Recursos para download


Temas relacionados


Comentários

Acesse ou registre-se para adicionar e acompanhar os comentários.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Cloud computing
ArticleID=1041688
ArticleTitle=Além de palavras da moda: um breve histórico sobre padrões de microsserviços
publish-date=12282016