APIs e Microsserviços

15 set, 2016

Engenharia Trade-Offs e a rearquitetura da API Netflix

Publicidade

A cultura de engenharia da Netflix é algo realmente motivador, eles mantém uma cultura baseada na liberdade e responsabilidade. A ideia é que todos que façam parte da equipe tenham depositada em si uma responsabilidade central; assim, todos têm a autonomia para operar com liberdade com o intuito de satisfazer seus desejos. E, como consequência disso, as equipes são responsáveis por todos os aspectos dos seus sistemas: design, arquitetura, desenvolvimento, deployment e operações. Embora muitos projetos e ferramentas sejam desenvolvidas, é antiprodutivo pensar que todas as equipes construam tudo a partir do zero, principalmente se levarmos em conta que boa parte das infraestruturas são compartilhadas. Sendo assim, a Netflix dá muita importância à reutilização de código.

Pensando nesse modelo, é complicado gerenciar como uma equipe ou um indivíduo da equipe irá escolher para otimizar ou o que ele deve herdar de outro time. Assim, esses trade-offs são universais na tomada de decisões da Netflix.

Para isso, a Netflix desenvolveu uma API que lida com inserção, descoberta e reprodução de tráfego de todos os usuários. Porém, essa API já está próxima do seu limite, visto o contínuo crescimento da empresa, da sua base de assinantes, da sua complexidade e do volume de requisições. Para resolver esse problema, eles começaram a trabalhar em uma nova arquitetura com o intuito de mantê-los à frente no futuro. Este artigo tem o objetivo de relatar o desafio durante o curso de desenvolvimento dessa rearquitetura. Foi necessário conciliar os princípios de engenharia que eram conflitantes: velocidade e propriedade plena vs máxima reutilização de código e consolidação.

Orquestração de microsserviços na API Netflix

A API Netflix é o primeiro passo, a porta de entrada para o ecossistema de microsserviços da Netflix. Todas as requisições são provenientes de dispositivos, assim, a API é responsável por fornecer a lógica de compor chamadas para todos os serviços necessários para construir uma resposta. Além disso, ela agrupa todas as informações necessárias dos serviços de back-end, na ordem necessária, formata, filtra os dados e retorna uma resposta.

Dessa maneira, o core da API Netlix é incumbido de orquestrar e expor as APIs de alta granularidade, tornando essas funcionalidades serviços de baixa granularidade fornecidos pelos microsserviços.

Isso é possível pelo fato de a API ter quatro requisitos principais:

  • Fornecer um protocolo de pedido flexível;
  • Mapear solicitações para uma ou mais APIs refinadas para microsserviços de back-end;
  • Fornecer uma abstração resiliente comum para proteger os microsserviços de backend;
  • Criar um limite de contexto (“buffer”) entre o dispositivo e back-end;

Atualmente, o serviço API da Netflix expõe três categorias de APIs da alta granularidade:

  • Non-member – Inscrição, faturamento, experimentação gratuita etc.;
  • Discovery – Show, filmes recomendados, busca etc.;
  • Playback – Decisões sobre a experiência de transmissão, licenciamento para garantir que os usuários possam visualizar conteúdo específico, histórico de exibições, heartbeats para bookmarking do usuário etc.

Aqui segue um exemplo. Pense na categoria de reprodução de APIs. Suponha que um usuário clique no botão “play” para Stranger Things Episódio 1 em seu smartphone. Para que a reprodução se inicie, o celular envia uma solicitação de “play” para a API. A API, por sua vez, chama vários microsserviços. Algumas dessas chamadas podem ser feitas em paralelo, porque elas não dependem umas das outras. Outras têm que ser sequenciadas em uma ordem específica. A API contém toda a lógica para orquestrar essas chamadas conforme necessário. O dispositivo, por sua vez, não precisa saber nada sobre o que está acontecendo quando o usuário clica em “play”.

Figure1Figura 1: Dispositivos enviam pedidos para a API, que orquestra o ecossistema de microsserviços.

Solicitações de reprodução, salvo algumas exceções, têm apenas o objetivo de mapear a reprodução de serviços de back-end. Existem muitos serviços que dependem mais da descoberta e de serviços de terceiros do que dos serviços de reprodução. Então, a separação é relativamente simples. Existem apenas alguns serviços que são necessários tanto para pedidos de reprodução como para pedidos de não reprodução.

Porém, isso não é novidade para o time da Netflix, tanto que sua estrutura organizacional reflete esse cenário. Atualmente, dois times, o de API e o de reprodução, contribuem em conjunto para a camada de orquestração junto com a equipe de reprodução, com foco na API de reprodução. Entretanto, somente o time de API tem total responsabilidade pela API, incluindo: lançamentos, suporte 24/7, reversões etc. Embora isso seja ótimo para a reutilização de código, não está em conformidade com o princípio de times que são responsáveis e operam diretamente na produção daquilo que constroem.

Elencadas essas características, foram definidas as seguintes metas para a rearquitetura:

  • É necessário que cada equipe possua e opere em produção o que construir. Isso permitirá alertas mais direcionados, e MTTR mais rápido;
  • Da mesma forma, é preciso que cada equipe possua sua própria agenda de lançamentos e sempre que possível não possua versões retidas por mudanças não relacionadas.

Duas abordagens concorrentes

Pensando no futuro da empresa, a Netflix considerou as seguintes opções. Na opção 1 (figura 2), a camada de orquestração na API, para todas as solicitações de reprodução, será um pass-through e simplesmente enviará os pedidos para a camada de orquestração específica de reprodução. Assim, a camada de orquestração de reprodução desempenhará o papel de orquestrar todos os serviços de reprodução. A única exceção para um modelo de repasse integral é o pequeno conjunto de serviços compartilhados, nos quais a camada de orquestração na API iria enriquecer o pedido com qualquer informação na camada de orquestração de reprodução, a fim de atender à solicitação.

Figure2Figura 2: Opção 1: Camada de orquestração pass-through com camada de orquestração específica de reprodução.

A outra abordagem seria dividir em duas APIs distintas.

Figure3Figura 3: Opção 2: Reprodução separada e APIs discovery/non-member.

Assim, qualquer uma das abordagens é capaz de resolver os desafios a que a Netflix se propôs. Independentemente da opção, cada equipe seria a dona do ciclo de lançamento, assim como das operações de produção de sua própria camada de orquestração. Isso significa que a escolha entre as duas opções se resume a outros fatores.

Experiência do desenvolvedor

Um dos principais fatores que devem ser levados em consideração são os desenvolvedores da Netflix. Eles são vistos como prioridade na concepção da nova arquitetura, visto que são eles que irão programar diariamente a API. Sendo assim, é de extrema importância que a experiência do desenvolvedor e a produtividade sejam excelentes. Para isso, a Netflix tem duas preocupações: descoberta e documentação.

As equipes precisam ter um bom entendimento em relação à API, elas precisam saber quais parâmetros passar e o que esperar. Outra meta é a flexibilidade: devido às complexas necessidades que a Netflix tem para mais de 1.000 tipos de dispositivo, a API deve ser extremamente flexível. Por exemplo, um dispositivo pode querer solicitar um número diferente de vídeos e propriedades diferentes, e outro pode solicitar outras. Todo esse trabalho será importante tanto para as APIs de reprodução quanto para as de não-reprodução. Sendo assim, como isso se relaciona com a discussão uma ou duas APIs? Uma API facilita uma maior uniformidade nessas áreas: como as solicitações são feitas e compostas, como a API está documentada, onde e como as equipes descobrem sobre as alterações ou adições à API, controle de versão da API, ferramentas para otimizar a experiência do desenvolvedor etc. Se existirem duas APIs, tudo isso é ainda possível, mas o trabalho das duas equipes será maior.

Implicações organizacionais e componentes compartilhados

Atualmente, as duas equipes estão muito juntas e colaboram efetivamente na API. No entanto, a Netflix diz estar ciente de que a decisão de criar duas APIs, de propriedade de duas equipes separadas, pode ter implicações complexas. Os objetivos seriam, e deveriam ser, a mínima divergência entre as duas APIs. A experiência do desenvolvedor, como elencado anteriormente, é uma das principais razões para isso. Porém, de forma mais ampla, é necessário maximizar a reutilização de quaisquer componentes que são relevantes para ambas as APIs. Isso também inclui quaisquer mecanismos de orquestração, e todas as ferramentas, mecanismos e bibliotecas relacionadas com escalabilidade, confiabilidade e resiliência. O risco é que as duas APIs poderiam se afastar ao longo do tempo, o que, por um lado, poderia ter consequências organizacionais (por exemplo, a necessidade de mais pessoal). Eles poderiam acabar em uma situação em que a valorização da posse de componentes chegue a um grau tal que eles tenham abandonado a reutilização de componentes. E esse não é um resultado desejável desde o início e, assim, seria necessário ter muito cuidado sobre qualquer divergência entre as duas APIs.

Mesmo em um mundo onde existe uma quantidade significativa de uso de código, a Netflix reconhece que a sobrecarga operacional será maior. Como visto anteriormente, a API é crítica para o que serviço Netflix funcione corretamente. Até o momento, apenas uma das equipes foi encarregada de tornar o sistema altamente escalável e resistente, e essa equipe tem carregado todo o fardo operacional. Ela construiu o conhecimento e a experiência em escala do sistema e resiliência durante anos de desenvolvimento. Com a criação de duas APIs, eles estariam distribuindo essas tarefas e responsabilidades para ambas as equipes.

Simplicidade

Se a Netflix coloca as considerações organizacionais de lado, duas APIs separadas são realmente a arquitetura mais viável. Na opção 1, se a API atua em grande parte como um pass-through, vale a pena incorrer no hop extra? A cada pedido de reprodução que viria, a API seria simplesmente repassada à camada de orquestração de reprodução sem fornecer muito valor funcional (além do pequeno conjunto de funcionalidades necessárias a partir dos serviços compartilhados). Se os componentes construídos para a descoberta, insights, resiliência, orquestração etc. podem ser reutilizados em ambas as APIs, a simplicidade de ter uma separação clara entre as duas APIs é atraente. Além disso, como mencionado brevemente, a opção 1 exige que duas equipes sejam envolvidas em pushs da API de reprodução que mudam o modelo de interação, enquanto a opção 2 realmente separa os deployments.

Onde é que tudo isso leva a Netlifx? Eles estão cientes de que essa decisão trará consequências duradouras. Mas levando todos os itens acima em consideração, também é perceptível que não há solução perfeita, não existe bala de prata. Não há certo ou errado, apenas trade-offs. O caminho a seguir é fazer suposições informadas e, então, experimentar e trabalhar com base nelas. A princípio, eles estão experimentando como generalizar os blocos já construídos e então iniciam o planejamento e a construção, de modo que eles poderiam ser usados em ambas as APIs. Se tal estratégia se revelar promissora, eles devem construir duas APIs. Apesar dos desafios, a Netlix está otimista com o caminho e animada com o futuro dos serviços.

Você se interessou pelos desafios da netflix? Junte-se a eles nessa empreitada!

***

Fonte: http://techblog.netflix.com/2016/08/engineering-trade-offs-and-netflix-api.html