Usando Golang para criar microsserviços no The Economist: uma retrospectiva

Olá pessoal! Já em 28 de maio, estamos lançando o primeiro grupo no curso Golang Developer . E hoje estamos compartilhando com você a primeira publicação dedicada ao lançamento deste curso. Vamos lá



Trechos principais

  • O Economist precisava de mais flexibilidade para distribuir conteúdo para um canal digital cada vez mais diversificado. Para atingir esse objetivo e manter um alto nível de desempenho e confiabilidade, a plataforma mudou de uma arquitetura monolítica para uma de microsserviço.
  • As ferramentas escritas em Go foram um componente-chave do novo sistema, o que permitiu à The Economist fornecer serviços escaláveis ​​e de alto desempenho e criar rapidamente novos produtos.
  • O Go, voltado para simultaneidade e suporte à API, junto com a construção de uma linguagem estática compilada, facilitou o desenvolvimento de sistemas de processamento de eventos distribuídos que podiam ser escalados. O suporte a testes também foi uma vantagem.
  • Em geral, a experiência da equipe da Economist com a Go foi positiva, e esse foi um dos fatores decisivos que ajudaram a escalar a plataforma de conteúdo.
  • O Go nem sempre será uma ferramenta adequada, e isso é normal. O Economist possui uma plataforma poliglota e usa diferentes idiomas onde faz sentido.

Entrei para a equipe de desenvolvimento do The Economist como desenvolvedor do Drupal. No entanto, minha verdadeira tarefa era participar de um projeto que mudaria fundamentalmente a tecnologia de entrega de conteúdo da Economist. Nos primeiros meses que passei estudando o Go, vários meses trabalhando com um consultor externo para criar um MVP (produto mínimo viável) e, em seguida, ingressei na equipe novamente para supervisionar sua imersão no Go.
Essa mudança na tecnologia foi desencadeada pela missão do The Economist de expandir sua audiência digital, à medida que o consumo de notícias se afastava da mídia impressa. O Economist precisava de mais flexibilidade para fornecer conteúdo para canais digitais cada vez mais diversos. Para atingir esse objetivo e manter um alto nível de desempenho e confiabilidade, a plataforma mudou de uma arquitetura monolítica para uma de microsserviço. As ferramentas escritas em Go foram um componente-chave do novo sistema, o que permitiu à The Economist fornecer serviços escaláveis ​​e de alto desempenho e criar rapidamente novos produtos.

Implementando Go in The Economist:

  • Engenheiros autorizados a desenvolver e implementar rapidamente novas funcionalidades.
  • Práticas recomendadas aprovadas para serviços de falha rápida com manipulação inteligente de erros.
  • Forneceu suporte confiável para simultaneidade e operação de rede em um sistema distribuído.
  • Mostrou falta de maturidade e suporte em algumas áreas necessárias para conteúdo e mídia.
  • Facilitou uma plataforma que poderia ser escalada para publicação digital.

Por que o The Economist escolheu Go?

Para responder a essa pergunta, será útil destacar a arquitetura geral da nova plataforma. A plataforma, chamada Plataforma de Conteúdo, é um sistema de manipulação de eventos. Ele responde a eventos de diferentes plataformas de criação de conteúdo e inicia um fluxo de processos executados em microsserviços que trabalham separadamente. Esses serviços executam funções como padronizar dados, analisar tags semânticas, indexar no ElasticSearch e enviar conteúdo para plataformas externas, como Apple News ou Facebook. A plataforma também possui uma API RESTful, que em combinação com o GraphQL é o principal ponto de entrada para clientes e produtos front-end.

Ao desenvolver uma arquitetura comum, a equipe investigou quais idiomas atenderiam às necessidades da plataforma. O Go foi comparado com Python, Ruby, Node, PHP e Java. Embora cada idioma tenha seus próprios pontos fortes, o Go é mais adequado à arquitetura da plataforma. O Go, voltado para simultaneidade e suporte à API, junto com a construção de uma linguagem estática compilada, facilitou o desenvolvimento de sistemas de processamento de eventos distribuídos que podiam ser escalados. Além disso, a sintaxe relativamente simples do Go tornou fácil se envolver no desenvolvimento e começar a escrever o código de trabalho, que prometia benefícios imediatos para uma equipe que passava por uma transição tecnológica tão grande. Em geral, foi determinado que o Go é o idioma mais adequado para usabilidade e eficiência em um sistema em nuvem distribuído.

Três anos depois: a Go se encaixou nesses objetivos ambiciosos?

Alguns elementos de design da plataforma estavam bem alinhados com o idioma Go. Failing Fast era uma parte crítica do sistema porque consistia em serviços independentes distribuídos. De acordo com os princípios do aplicativo de doze fatores ("aplicativo de 12 fatores"), o aplicativo teve que iniciar com rapidez e rapidez (falha rápida). O design da Go como uma linguagem estática e compilada fornece tempos de inicialização rápidos, e o desempenho do compilador está constantemente melhorando e nunca foi um problema para o design ou a implantação. Além disso, o design de manipulação de erros da Go permitiu que os aplicativos falhassem não apenas mais rapidamente, mas também de maneira mais inteligente.

Tratamento de erros

Um recurso que os engenheiros notam rapidamente no Go é que ele é do tipo Erro, e não um sistema de exceção. No Go, todos os erros são valores. O tipo de erro é predefinido e é uma interface. Uma interface no Go é essencialmente uma coleção de métodos nomeada e qualquer outro tipo de usuário pode satisfazer uma interface se ela tiver os mesmos métodos. O tipo de erro é uma interface que pode se descrever como uma string.

type error interface { Error() string } 

Isso oferece aos engenheiros mais controle e funcionalidade no tratamento de erros. Ao adicionar um método Error que retorna uma string em qualquer módulo do usuário, você pode criar seus próprios erros e gerá-los, por exemplo, usando a função Novo abaixo, que vem do pacote de Erros.

 type errorString struct { s string } func (e *errorString) Error() string { return es } 

O que isso significa na prática? No Go, as funções permitem vários valores de retorno; portanto, se sua função não funcionar, provavelmente retornará um valor de erro. O idioma o incentiva a verificar explicitamente os erros onde eles ocorrem (em vez de lançar e capturar uma exceção); portanto, seu código geralmente deve incluir uma verificação “if err! = Nulo " No início, esse tratamento frequente de erros pode parecer monótono. No entanto, erro como um valor permite que você use Erro para simplificar o tratamento de erros. Por exemplo, em um sistema distribuído, você pode implementar facilmente tentativas de repetir consultas quebrando erros.

Problemas de rede sempre ocorrerão no sistema, seja enviando dados para outros serviços internos ou transferindo-os para ferramentas de terceiros. Este exemplo de pacote de rede mostra como você pode usar o erro como um tipo para distinguir erros temporários de rede de erros permanentes. A equipe do Economist usou um invólucro de erro semelhante para criar novas tentativas incrementais ao enviar conteúdo para APIs externas.

 package net type Error interface { error Timeout() bool // Is the error a timeout? Temporary() bool // Is the error temporary? } if nerr, ok := err.(net.Error); ok && nerr.Temporary() { time.Sleep(1e9) continue } if err != nil { log.Fatal(err) } 

Os autores do Go acreditam que nem todas as exceções são excepcionais. É mais provável que os engenheiros se recuperem de forma inteligente de erros do que travem o aplicativo. Além disso, o tratamento de erros Go permite controlar melhor os erros, o que pode melhorar aspectos como depuração ou usabilidade de erros. Na plataforma de conteúdo, esse recurso de design do Go permitiu que os desenvolvedores tomassem decisões informadas sobre erros, o que levou a um aumento na confiabilidade do sistema como um todo.

Consistência dos dados

A consistência dos dados é um fator crítico na plataforma de conteúdo. Na The Economist, o conteúdo é a base dos negócios, e o objetivo da Plataforma de Conteúdo é garantir que o conteúdo possa ser publicado uma vez e esteja disponível em qualquer lugar. Portanto, é importante que cada produto e consumidor tenham consistência de dados com a API da plataforma de conteúdo. Os produtos usam principalmente o GraphQL para solicitações de API, o que requer um esquema estático, que serve como um tipo de contrato entre os consumidores e a plataforma. O conteúdo processado pela Plataforma deve ser consistente com este esquema. A linguagem estática ajudou a implementar isso e facilitou o alcance da consistência dos dados.

Testando com Go

Outro recurso que promove consistência é o conjunto de testes Go. O rápido tempo de compilação da Go, combinado com os testes de primeira classe como um recurso do idioma, permitiu à equipe incorporar métodos de teste eficazes nos fluxos de trabalho de projeto e falhas rápidas nos pipelines de montagem. As ferramentas de teste da Go facilitam a configuração e a execução. A execução de "go test" executará todos os testes no diretório atual e o comando test possui vários sinalizadores úteis. O sinalizador de capa fornece um relatório detalhado de cobertura de código. O teste de bancada executa testes de benchmark, que são indicados executando o nome da função de teste com a palavra "Bench" em vez de "Test". A função TestMain fornece métodos para configuração de teste adicional, como um servidor de autenticação fictícia.

Além disso, o Go tem a capacidade de criar testes tabulares com estruturas anônimas e stubs com interfaces, melhorando a cobertura do teste. Embora o teste não seja novo em termos de recursos de idioma, o Go facilita a criação de testes robustos e a integração fácil nos fluxos de trabalho. Desde o início, os engenheiros da The Economist puderam executar testes como parte dos pipelines de montagem sem configuração especial e até adicionaram o Git Hooks para executar os testes antes de inserir o código no Github.

No entanto, o projeto não foi sem esforço para alcançar a consistência dos dados. O primeiro grande problema da plataforma foi gerenciar conteúdo dinâmico a partir de back-ends imprevisíveis. A plataforma consome conteúdo dos sistemas CMS originais principalmente por pontos de extremidade JSON, onde a estrutura e os tipos de dados não são garantidos. Isso significa que a plataforma não pode usar o pacote Go padrão para interpretar o json, que suporta a desserialização JSON em estruturas, mas soa um alarme se os tipos dos campos de dados struct e de entrada não corresponderem.

Para superar esse problema, foi necessário um método especial para mapear a parte do servidor para o formato padrão. Após várias iterações da abordagem escolhida, a equipe introduziu seu próprio processo de desserialização. Embora essa abordagem tenha sido um pouco como refazer um pacote de biblioteca padrão, ela deu aos engenheiros controle total sobre o processamento dos dados de origem.

Suporte de rede

A escalabilidade estava na vanguarda da nova plataforma, e isso foi fornecido pelas bibliotecas padrão Go para redes e APIs. No Go, você pode implementar rapidamente pontos de extremidade HTTP escaláveis ​​sem a necessidade de estruturas. No exemplo abaixo, o pacote da biblioteca padrão net / http é usado para configurar um manipulador que aceita um gravador de solicitação e resposta. Quando a API da plataforma de conteúdo foi implementada pela primeira vez, ela usou a estrutura da API. Ele acabou sendo substituído por uma biblioteca padrão, pois a equipe reconheceu que atende a todas as suas necessidades de rede sem compromissos desnecessários adicionais. Os manipuladores HTTP Golang são dimensionados porque cada solicitação ao manipulador é executada em paralelo na Goroutine, um encadeamento leve, sem a necessidade de personalização.

 package main import ( "fmt" "log" "net/http" ) func handler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello World!") } func main() { http.HandleFunc("/", handler) log.Fatal(http.ListenAndServe(":8080", nil)) } 

Modelo de simultaneidade

O modelo de concorrência Go ofereceu vários benefícios na melhoria do desempenho em toda a plataforma. Trabalhar com dados distribuídos envolve mexer com as garantias prometidas aos consumidores. De acordo com o teorema da PAC, não é possível fornecer mais de duas das três garantias a seguir ao mesmo tempo: Consistência dos dados. Disponibilidade Resistente à separação. Na plataforma Economist, a consistência foi finalmente adotada, o que significa que a leitura das fontes de dados será consistente e atrasos moderados em todas as fontes de dados que atingem um estado consistente são aceitáveis. Uma maneira de minimizar essa lacuna é usar Goroutines.

Goroutines são threads leves, gerenciados pelo tempo de execução Go, para impedir que fiquem sem threads. Goroutines permitiu otimizar tarefas assíncronas na plataforma. Por exemplo, um dos repositórios de dados da plataforma é o Elasticsearch. Quando o conteúdo é atualizado no sistema, o conteúdo que referencia esse item no Elasticsearch é atualizado e reindexado. Graças à implementação das Goroutines, o tempo de processamento foi reduzido, o que garantiu a consistência rápida dos elementos. Este exemplo mostra como os itens adequados para reprocessamento são reprocessados ​​na Goroutine.

 func reprocess(searchResult *http.Response) (int, error) { responses := make([]response, len(searchResult.Hits)) var wg sync.WaitGroup wg.Add(len(responses)) for i, hit := range searchResult.Hits { wg.Add(1) go func(i int, item elastic.SearchHit) { defer wg.Done() code, err := reprocessItem(item) responses[i].code = code responses[i].err = err }(i, *hit) } wg.Wait return http.StatusOK, nil } 

Projetar sistemas é mais do que apenas programação. Os engenheiros precisam entender quais ferramentas, onde e quando são apropriadas. Embora o Go fosse uma ferramenta poderosa para a maioria das necessidades da plataforma de conteúdo do The Economist, algumas limitações exigiam outras soluções.

Gerenciamento de Dependências

Quando o Go foi lançado, ele não tinha um sistema de gerenciamento de dependências. Dentro da comunidade, várias ferramentas foram desenvolvidas para atender a essa necessidade. O Economist usou sub-módulos Git, que faziam sentido no momento em que a comunidade estava promovendo ativamente uma ferramenta padrão de gerenciamento de dependências. Hoje, embora a comunidade já esteja muito mais próxima de uma abordagem coerente ao gerenciamento de dependências, ela não existe. A abordagem The Economist usando submódulos não apresentou problemas sérios, mas era difícil para outros desenvolvedores do Go, e isso deve ser levado em consideração ao mudar para o Go.

Também havia requisitos de plataforma para os quais os recursos ou o design do Go não eram a melhor solução. Como a plataforma adicionou suporte ao processamento de áudio, as ferramentas da Go para extrair metadados eram limitadas na época e, portanto, a equipe escolheu o Exiftool Python. Os serviços de plataforma funcionam em contêineres de docker, o que permitiu instalar o Exiftool e iniciá-lo a partir do aplicativo Go.

 func runExif(args []string) ([]byte, error) { cmdOut, err := exec.Command("exiftool", args...).Output() if err != nil { return nil, err } return cmdOut, nil } 

Outro cenário comum para a plataforma é a recepção de código HTML que não funciona dos sistemas CMS de origem, análise do código HTML para correção e limpeza do código HTML. Inicialmente, o Go era usado para esse processo, mas como a biblioteca HTML padrão do Go requer HTML correto, era necessária uma grande quantidade de código personalizado para analisar o HTML antes do processamento. Esse código rapidamente se tornou frágil e perdeu casos limítrofes, então uma nova solução foi implementada em Javascript. O Javascript forneceu grande flexibilidade e adaptabilidade para controlar o processo de verificação e limpeza do HTML.

Javascript também foi uma escolha comum para filtrar e rotear eventos na plataforma. Os eventos são filtrados usando o AWS Lambdas, que são funções leves que só são executadas quando chamadas. Um caso de uso é filtrar eventos em diferentes bandas, como rápida e lenta. Essa filtragem é baseada em um único campo de metadados no objeto JSON do shell do manipulador de eventos. A implementação de filtragem usou um pacote de ponteiro JSON Javascript para capturar um elemento em um objeto JSON. Essa abordagem foi muito mais eficiente em comparação à desmontagem completa do JSON que o Go precisaria. Embora a funcionalidade desse tipo pudesse ser alcançada com o Go, o uso de Javascript era mais fácil para os engenheiros e fornecia lambdas mais simples.

Retrospective Go

Depois de implementar a plataforma de contato e apoiá-la na produção, se eu conduzir uma retrospectiva do Go e da plataforma de conteúdo, meus comentários serão os seguintes:

O que já é bom?

  • Elementos chave de design de linguagem para sistemas distribuídos.
  • Um modelo de simultaneidade que é relativamente fácil de implementar.
  • Boa codificação e comunidade divertida.

O que pode ser melhorado?

  • Avanço adicional nos padrões de controle de versão e vendas.
  • Não há maturidade suficiente em algumas áreas.
  • Detalhes para casos de usuário específicos.

Em geral, foi uma experiência positiva e o Go é um dos elementos mais importantes que permitiram escalar a plataforma de conteúdo. O Go nem sempre será uma ferramenta adequada, e isso é normal. O Economist possui uma plataforma poliglota e usa diferentes idiomas onde faz sentido. Provavelmente, o Go nunca será a melhor opção quando você precisar mexer em objetos de texto e conteúdo dinâmico; portanto, o Javascript ainda está na caixa de ferramentas. No entanto, os pontos fortes da Go são a base que permite ao sistema escalar e crescer.
Ao considerar se o Go é adequado para você, considere os principais problemas do design do sistema:

  • Quais são as tarefas do seu sistema?
  • Que garantias você oferece aos seus consumidores?
  • Que arquitetura e padrões são apropriados para o seu sistema?
  • Como o seu sistema deve escalar?

Se você estiver desenvolvendo um sistema que lide com os desafios de dados distribuídos, fluxos de trabalho assíncronos e alto desempenho e escalabilidade, recomendo que você considere o Go e seus recursos para acelerar seus objetivos de sistema.

Amigos, aguardamos seus comentários e convidamos todos para o seminário on-line aberto , que será realizado no dia 16 pelo desenvolvedor sênior da Yandex e, em conjunto, nosso professor é Dmitry Smal .

Source: https://habr.com/ru/post/pt451760/


All Articles