Operando um grande sistema distribuído: o que aprendi



Lendo vários canais e boletins, frequentemente encontro artigos sobre “dores” e problemas específicos que surgem quando uma empresa cresce, quando a confiabilidade e a escalabilidade vêm à tona. Este artigo é diferente. Não há análise detalhada de soluções arquitetônicas específicas ou um guia passo a passo para mudar a cultura da engenharia. Em vez disso, é uma visão superior dos desafios que surgem ao operar sistemas distribuídos e um ponto de partida que o ajudará a navegar no fluxo de termos, abreviações e tecnologias.

Trago a sua atenção a tradução de um artigo escrito por um engenheiro da Uber.

* * *

Nos últimos anos, criei e mantive um grande sistema de pagamento distribuído no Uber . Durante esse período, aprendi muito sobre os conceitos de arquiteturas distribuídas.e, por experiência própria, descobri como é difícil criar e manter sistemas altamente carregados com alta disponibilidade. Construir esse sistema é um trabalho interessante. Gosto de planejar como o sistema lidará com o crescimento do tráfego de 10 a 100 vezes, para garantir a confiabilidade dos dados, independentemente das falhas de hardware. No entanto, operar um grande sistema distribuído me proporcionou uma experiência inesperada .

Quanto maior o sistema, maior a probabilidade de você encontrar uma manifestação da lei de Murphy: " Tudo o que pode dar errado vai dar errado ". A probabilidade é especialmente alta com lançamentos frequentes, quando muitos desenvolvedores lançam código, ao usar vários data centers e com uma enorme audiência de usuários em todo o mundo. Nos últimos anos, deparei-me com várias falhas no sistema, muitas das quais me surpreenderam: desde problemas previsíveis, como falhas de hardware ou bugs inocentes, até quebras de cabos que conectam os data centers a inúmeras falhas em cascata que ocorrem simultaneamente. Passei por dezenas de falhas nas quais partes do sistema não funcionavam corretamente, o que afetou bastante os negócios.

Este artigo resume as técnicas que se beneficiaram com a operação de um grande sistema no Uber. Minha experiência não é única: outros trabalham com sistemas do mesmo tamanho e passam pelas mesmas aventuras. Conversei com engenheiros do Google, Facebook e Netflix, que enfrentaram situações semelhantes e criaram soluções semelhantes. Muitas das idéias e processos descritos aqui podem ser aplicados a sistemas da mesma escala, independentemente de trabalharem em data centers de propriedade da empresa (como costuma ser o caso do Uber) ou na nuvem (onde o Uber às vezes escala ). No entanto, essas dicas podem ser redundantes para sistemas não tão grandes ou importantes para a empresa.

Vamos falar sobre esses tópicos:

  • Monitoramento
  • Serviço (de plantão), detecção e notificação de anomalias.
  • Gerenciamento de falhas e incidentes.
  • Post Mortems, análise de incidentes e uma cultura de melhoria contínua.
  • Failover, planejamento de recursos e teste de caixa preta.
  • SLO, SLA e relatórios sobre eles.
  • SRE como uma equipe independente.
  • Confiabilidade como investimento permanente.
  • Materiais úteis.

Monitoramento


Para entender se o sistema está íntegro, precisamos responder à pergunta: “Funciona corretamente? " Para isso, é vital coletar informações sobre partes críticas do sistema. E quando você distribui sistemas que consistem em vários serviços em vários servidores e em diferentes datacenters, pode ser difícil decidir quais pontos-chave você realmente precisa acompanhar.

Monitorando o status da infraestrutura.Se uma ou mais máquinas / máquinas virtuais estiverem sobrecarregadas, algumas partes do sistema distribuído poderão sofrer uma redução no desempenho. As métricas de estado das máquinas nas quais o serviço está sendo executado - o consumo de recursos e memória do processador - são os parâmetros básicos que precisam ser monitorados. Existem plataformas que inicialmente rastreiam essas métricas e escalam instâncias automaticamente. O Uber possui uma excelente equipe de infraestrutura principal , que por padrão fornece monitoramento e alerta. Independentemente do método de implementação, você deve ter informações de que instâncias, infraestrutura ou serviços individuais têm problemas.

Monitoramento do status do serviço: tráfego, erros, atraso . Muitas vezes, você precisa ter a resposta para a pergunta "O back-end está funcionando bem? " O monitoramento de métricas como a quantidade de tráfego recebido, a proporção de erros e o atraso na resposta fornece informações valiosas sobre o status do serviço. Eu prefiro criar painéis para todas essas métricas. Ao criar um novo serviço, o uso das respostas HTTP corretas e o monitoramento dos códigos correspondentes podem dizer muito sobre o estado do sistema. Se você tiver certeza de que os códigos 4xx são retornados para erros do cliente e os códigos 5xx são retornados para erros do servidor, será fácil criar e interpretar esse monitoramento.

Há algo mais a ser dito sobre o monitoramento de atrasos nas respostas. O objetivo dos serviços de produção é que a maioria dos usuários finais aproveite o uso deles. E medir o atraso médio não é a melhor solução, porque o valor médio pode ocultar um pequeno número de solicitações com um grande atraso. É muito melhor medir p95, p99 ou p999 - o atraso para o percentil 95, 99 ou 99,9 de solicitações. Esses números ajudam a responder a perguntas como " Qual a rapidez da solicitação para 99% dos visitantes?" "(P99) ou" Quão lenta será a solicitação para um visitante em cada 1000? "(P999). Se você estiver interessado nos detalhes, poderá ler este artigo .


Gráfico para p95 e p99. Observe que o atraso médio para este terminal é inferior a 1 segundo e 1% das solicitações leva 2 segundos. e mais - isso não é visto ao medir o valor médio.

O tópico de monitoramento e observabilidade é muito mais profundo. Eu recomendo a leitura do livro do Google SRE e da seção Quatro sinais dourados sobre o monitoramento de sistemas distribuídos. Se, para um sistema com o qual os usuários interagem, você pode obter apenas quatro métricas, concentre-se no tráfego, erros, atraso e saturação. Há menos material - o e-book de Observabilidade de Sistemas Distribuídos , que descreve ferramentas úteis, como logs, bem como práticas recomendadas para o uso de métricas e rastreamento.

Monitorando métricas de negócios. Os serviços de monitoramento nos informam sobre sua condição geral. Mas, de acordo apenas com os dados de monitoramento, não podemos dizer se os serviços estão funcionando corretamente, se tudo foi processado corretamente do ponto de vista comercial. No caso do sistema de pagamento, a principal questão é: “ As pessoas podem fazer viagens usando um método de pagamento específico? " Uma das etapas mais importantes no monitoramento é identificar e rastrear eventos de negócios criados e processados ​​por este serviço.

Minha equipe criou um monitoramento das métricas de negócios após uma falha que não pudemos detectar de outras maneiras. Tudo parecia como se os serviços estivessem funcionando normalmente, mas na verdade a funcionalidade principal não funcionava. Esse tipo de monitoramento era muito incomum para nossa empresa e campo de atividade. Portanto, tivemos que fazer muitos esforços para configurar esse monitoramento por nós mesmos, criando nosso próprio sistema .

Dever, detecção de anomalias e alerta


O monitoramento é uma ótima ferramenta para verificar o status atual do sistema. Mas este é apenas um passo no caminho para detectar automaticamente falhas e alertar aqueles que devem fazer algo neste caso.

O relógio é um tópico extenso. A Increment Magazine fez um excelente trabalho destacando muitos dos problemas da edição On-Call . Na minha opinião, o dever é uma continuação lógica da abordagem "você criou - você possui". Os serviços são de propriedade das equipes que os criaram e também possuem um sistema de alerta e resposta a incidentes. Minha equipe possuía esse sistema para o serviço de pagamento que criamos. E quando o alerta chega, o engenheiro de plantão deve responder e descobrir o que está acontecendo. Mas como passamos do monitoramento para os alertas?

Determinar anomalias com base nos dados de monitoramento é uma tarefa difícil , e o aprendizado de máquina deve se manifestar completamente aqui. Existem muitos serviços de terceiros para detectar anomalias. Felizmente para minha equipe, nossa empresa tem seu próprio grupo de aprendizado de máquina para resolver os problemas que o Uber enfrenta. A equipe de Nova York escreveu um artigo útil sobre como funciona a detecção de anomalias no Uber . Do ponto de vista da minha equipe, só transmitimos dados de monitoramento para esse pipeline e recebemos alertas com vários graus de confiança. E então decidimos se informaremos o engenheiro sobre isso.

Quando preciso enviar um alerta? A questão é interessante. Se houver poucos alertas, podemos perder uma falha importante. Se for demais, as pessoas não vão dormir à noite.O rastreamento e a categorização de alertas, bem como a medição da relação sinal / ruído, desempenham um papel importante na configuração de um sistema de alerta . Um bom passo em direção a uma rotação constante de engenheiros de serviço será analisar alertas, categorizar eventos como "exigindo ação" e "não exigir" e, em seguida, reduzir o número de alertas que não exigem ação.


Um exemplo de painel de serviço de chamada criado pela equipe Uber Developer Experience em Vilnius.

A equipe do Uber para a criação de ferramentas de desenvolvimento a partir de Vilnius criou uma pequena ferramenta que usamos para comentar alertas e visualizar trocas de tarefas. Nossa equipe gera um relatório semanal sobre o trabalho do último turno de serviço, analisa pontos fracos e aprimora a metodologia do serviço.

Gerenciamento de falhas e incidentes


Imagine: você é um engenheiro de serviço por uma semana. No meio da noite, você acorda uma mensagem no pager. Você está verificando se ocorreu uma falha na produção. Porra, parece que parte do sistema caiu. O que agora? Monitorar e alertar apenas funcionou.

As falhas podem não ser particularmente problemáticas para pequenos sistemas quando o engenheiro de serviço pode entender o que aconteceu e por quê. Geralmente nesses sistemas, você pode identificar rapidamente as causas e se livrar delas. Porém, no caso de sistemas complexos que contêm muitos (micro) serviços, quando muitos engenheiros colocam o código em operação, a razão da falha é bastante difícil. E a conformidade com vários procedimentos padrão pode ser de grande ajuda.

A primeira linha de defesa são runbooks de procedimentos de resposta padrãoque descrevem etapas simples de solução de problemas. Se a empresa tiver essas listas e for ativamente suportada, é improvável que uma idéia superficial do engenheiro de serviço sobre o sistema seja um problema. As listas precisam ser atualizadas, atualizadas e processadas para novas formas de solução de problemas.

Informar sobre falhas de outros funcionáriosTorna-se muito importante se várias equipes estiverem envolvidas na implantação de um serviço. No ambiente em que trabalho, os serviços, conforme necessário, são implementados por milhares de engenheiros, com uma frequência de centenas de lançamentos por hora. E a implantação aparentemente inócua de um serviço pode afetar outro. Em tal situação, um papel importante é desempenhado pela padronização dos canais de comunicação e comunicação de falhas. Eu tive muitos casos em que o alerta não era como qualquer outra coisa e percebi que para outras equipes isso também parece estranho. Em um bate-papo geral sobre falhas, identificamos o serviço responsável pela falha e eliminamos rapidamente as consequências. Juntos, conseguimos fazer isso muito mais rápido do que qualquer um de nós individualmente.

Elimine as consequências agora e descubra amanhã. No meio de um acidente, muitas vezes eu estava sobrecarregado por uma onda de adrenalina por causa do desejo de corrigir erros. Muitas vezes, a causa do problema estava lançando código incorreto com um bug óbvio. Anteriormente, eu teria encerrado tudo e começado a corrigir erros, enviar uma correção e reverter o código com falha. Mas consertar a causa no meio de um acidente é uma péssima idéia . Com uma solução rápida, você pode conseguir pouco e perder muito . Como a correção precisa ser feita rapidamente, ela deve ser testada em batalha. E este é o caminho para um novo bug - ou uma nova falha - além do existente. Vi como isso causou uma série de falhas. Apenas concentre-se em corrigir as consequências, não fique tentado a corrigir o código ou a encontrar a causa. A investigação vai esperar até amanhã.

Post Mortems, análise de incidentes e uma cultura de melhoria contínua


Um relatório de incidente é uma característica importante de como uma equipe lida com as consequências de uma falha. A situação preocupa as pessoas? Eles fazem uma pequena pesquisa ou dedicam uma quantidade incrível de esforço à observação, interrompem o produto e fazem correções?

O post-mortem corretamente escrito é um dos principais elementos da construção de sistemas sustentáveis. Não condena ninguém e não procura os culpados; este é um estudo e análise cuidadosos do incidente. Com o tempo, nossos modelos para tais relatórios evoluíram, seções com conclusões finais, avaliação de impacto, cronologia de eventos, análise do principal motivo, lições aprendidas e uma lista detalhada de elementos para observação adicional.


Eu usei esse padrão de manipulação de erros no Uber.

Em um bom post-mortem, a causa da falha é minuciosamente investigada e são propostas medidas para prevenir, detectar ou eliminar rapidamente as consequências de falhas semelhantes. E quando digo "profundamente", quero dizer que os autores não param no fato de que o motivo foi a rolagem do código com um bug que o revisor não notou. Os autores devem aplicar a metodologia Five Why para chegar a uma conclusão mais útil. Por exemplo:

  • Por que existe um problema? -> O bug foi carregado como parte do código.
  • Por que ninguém pegou um bug? -> Quem fez a revisão não percebeu que alterar o código poderia levar a esse problema.
  • , ? --> .
  • ? --> .
  • ? --> .
  • : . , .

A análise de incidentes é uma ferramenta complementar importante para o trabalho em bugs. Enquanto algumas equipes trabalham com cuidado em bugs, outras podem se beneficiar de dados adicionais e fazer melhorias preventivas. Também é importante que as equipes se considerem responsáveis ​​e capazes de fazer as melhorias que propõem no nível do sistema.

Nas organizações que levam a sério a confiabilidade, os incidentes mais graves são analisados ​​e eliminados por engenheiros experientes. Também é necessário gerenciar a engenharia no nível da empresa para garantir que as correções possam ser feitas - especialmente se elas consomem tempo ou interferem em outros trabalhos. Um sistema confiável não pode ser criado em uma noite: serão necessárias iterações constantes. Iterações resultantes de uma cultura da empresa de melhoria contínua com base nas lições aprendidas com os incidentes.

Failover, planejamento de recursos e teste de blackbox


Existem vários procedimentos regulares que requerem investimento significativo, mas são críticos para manter um grande sistema distribuído. Cheguei a essas idéias no Uber, não era necessário aplicá-las a outras empresas devido à menor escala e indisponibilidade da infraestrutura. Eu considerei o

failover estúpido no data center (failover) até me deparar com isso. Inicialmente, eu acreditava que o design de um sistema distribuído estável é a estabilidade dos data centers em queda. Por que deveria ser testado regularmente se tudo deveria funcionar teoricamente ? A resposta depende do dimensionamento e do teste da capacidade dos serviços de lidar com eficiência com o aumento inesperado do tráfego no novo data center.

O cenário de falha mais comum que me deparei é que o serviço não possui recursos suficientes no novo datacenter para lidar com o tráfego global no caso de um failover. Suponha que o serviço A funcione em um data center e o serviço B. Deixe o consumo de recursos ser de 60% - dezenas ou centenas de máquinas virtuais giram em cada data center, e alertas são acionados quando um limite de 70% é atingido. Todo o tráfego do datacenter A para o datacenter B. falhou. O segundo datacenter não pode lidar com esse aumento de carga sem implantar novas máquinas. No entanto, isso pode levar muito tempo, portanto, as solicitações começam a se acumular e a cair. O bloqueio começa a afetar outros serviços, causando uma falha em cascata de outros sistemas que não estão relacionados ao failover primário.


Uma situação possível em que o failover leva a problemas.

Outro cenário de falha popular envolve problemas no nível de roteamento, problemas com largura de banda da rede ou contrapressão . O failover de data centers é um desenvolvimento que qualquer sistema distribuído confiável deve executar sem causar impacto nos usuários. Eu enfatizo - que deve , este desenvolvimento é um dos exercícios mais úteis para verificar a confiabilidade de sistemas web distribuídos.

Exercícios agendados de inatividade do serviçoUma ótima maneira de testar a estabilidade de um sistema inteiro. Também é uma ótima ferramenta para detectar dependências ocultas ou usos inadequados / inesperados de um sistema específico. Exercícios programados de inatividade podem ser relativamente fáceis de executar com serviços com os quais os clientes interagem e possuem dependências pouco conhecidas. No entanto, se estamos falando de sistemas críticos para os quais é permitido um tempo de inatividade muito curto ou dos quais muitos outros sistemas dependem, será difícil realizar esses exercícios. Mas o que acontece se esse sistema ficar indisponível um dia? É melhor responder a essa pergunta em um experimento controlado, para que todas as equipes estejam avisadas e prontas.

Teste de caixa preta(o método "caixa preta") é uma maneira de avaliar a correção do sistema em uma situação o mais próxima possível de como ocorre a interação com o usuário final. Isso é semelhante ao teste de ponta a ponta, exceto pelo fato de que, para a maioria dos produtos, o teste correto de caixa preta exige investimento próprio. Bons candidatos a esses testes são os principais processos e cenários do usuário que envolvem a interação do usuário: para testar o sistema, verifique se eles podem ser iniciados a qualquer momento.

Usando o Uber como exemplo, um teste óbvio de caixa preta está verificando a interação motorista-passageiro no nível da cidade: um passageiro pode encontrar um motorista e fazer uma viagem mediante uma solicitação específica? Após automatizar esse cenário, o teste pode ser executado regularmente, emulando diferentes cidades. Um sistema confiável de teste de caixa preta facilita a verificação da operação correta do sistema ou de suas partes. Também ajuda bastante no teste de failover: a maneira mais rápida de obter feedback de troca é executar o teste de caixa preta.


Um exemplo de teste de caixa preta durante failover de failover e reversão manual.

Planejamento de recursosdesempenha um papel particularmente importante para grandes sistemas distribuídos. Em geral, quero dizer aqueles em que o custo de computação e armazenamento é calculado em dezenas ou centenas de milhares de dólares por mês. Nessa escala, pode ser mais barato ter um número fixo de implantações do que usar soluções em nuvem auto-escaláveis. Em casos extremos, as implantações fixas devem lidar com o tráfego característico dos "negócios normais", com dimensionamento automático apenas em cargas de pico. Mas qual é o número mínimo de instâncias a serem aplicadas no próximo mês? Nos próximos três meses? Próximo ano?

Não é difícil prever futuros padrões de tráfego para sistemas maduros com grandes volumes de estatísticas. Isso é importante para o orçamento, a escolha de fornecedores ou para a fixação de descontos de provedores de nuvem. Se o seu serviço gera grandes contas e você não pensou no planejamento de recursos, está faltando uma maneira fácil de reduzir custos e gerenciá-los.

SLO, SLA e relatórios sobre eles


SLO significa Service Level Objective , uma métrica para a disponibilidade do sistema. É uma boa prática definir SLOs no nível de serviço para desempenho, tempo de resposta, correção e disponibilidade. Esses SLOs podem ser usados ​​como limites de alerta. Exemplo:

Métrica SLOSubcategoriaValor do Serviço
atuaçãoLargura de banda mínima500 solicitações por segundo
2 500
50-90
p99500-800
0,5 %
99,9 %

SLO no nível de negócios . ou SLOs funcionais, isso é uma abstração sobre serviços. Eles cobrem métricas personalizadas ou comerciais. Por exemplo, um SLO em nível de negócios pode ser o seguinte: 99,99% dos recebimentos devem ser enviados por correio dentro de 1 minuto após a conclusão de uma viagem. Esse SLO pode ser comparado ao SLO no nível de serviço (por exemplo, com o atraso do sistema de pagamento ou do sistema de envio de cheques) e pode ser medido separadamente.

SLA - Contrato de Nível de Serviço . Este é um acordo mais geral entre um provedor de serviços e seu consumidor. Normalmente, vários SLOs compõem um SLA. Por exemplo, a disponibilidade de um sistema de pagamento no nível de 99,99% pode ser SLA, que é dividido em SLOs específicos para cada sistema correspondente.

Depois de determinar o SLO, você precisa medi-los e fazer um relatório.O monitoramento e os relatórios automáticos de SLAs e SLOs geralmente são um projeto complexo que nem os engenheiros nem as empresas desejam priorizar. Os engenheiros não estarão interessados, porque eles já têm níveis diferentes de monitoramento para identificar falhas em tempo real. Uma empresa prioriza melhor a entrega da funcionalidade, em vez de investir em um projeto complexo que não trará benefícios imediatos.

E isso nos leva a outro tópico: organizações que operam grandes sistemas distribuídos, mais cedo ou mais tarde, precisam alocar pessoas para garantir a confiabilidade desses sistemas. Vamos falar sobre a equipe do SRE - Site Reliability Engineering.

SRE como equipe independente


O termo Engenharia de confiabilidade do site foi cunhado pelo Google por volta de 2003 e hoje existem mais de 1.500 engenheiros do SRE. À medida que a operação do ambiente de produção está se tornando uma tarefa cada vez mais complexa, exigindo mais automação, em breve se tornará um trabalho completo. Isso acontece quando as empresas percebem que os engenheiros trabalham na automação do ambiente de produção quase o dia inteiro: quanto mais importante o sistema e mais falhas ocorrem, mais cedo o SRE se torna uma posição separada.

As empresas de tecnologia de rápido crescimento geralmente montam uma equipe de SRE desde o início, que planejam para si mesmas. No Uber, essa equipe foi criada em 2015.Seu objetivo era gerenciar a complexidade do sistema. Em outras empresas, a alocação de equipes de SRE pode ser associada à criação de uma equipe de infraestrutura separada. Quando a empresa cresce a um nível tal que garantir a confiabilidade do serviço exija a atenção de um número significativo de engenheiros, é hora de criar uma equipe separada.

A equipe do SRE simplifica bastante a manutenção de grandes sistemas distribuídos para todos os engenheiros. A equipe do SRE provavelmente possui ferramentas padrão de monitoramento e alertas. Eles provavelmente estão comprando ou criando ferramentas de plantão e estão dispostos a compartilhar sua experiência. Eles podem facilitar a análise de incidentes e criar sistemas que facilitam a detecção de falhas, reduzem suas consequências e as previnem no futuro. O comando SRE certamente facilita as operações de failover. É frequentemente usado para testar caixas pretas e planejar o desempenho. Os engenheiros do SRE gerenciam a seleção, personalização ou criação de ferramentas padrão para determinar e medir os SLOs e gerar relatórios sobre eles.

Dado que todas as empresas têm seus próprios problemas, cuja solução recrutam SRE, essas equipes em diferentes empresas têm estruturas diferentes. Até os nomes podem variar: pode ser um serviço de operação, engenharia de plataforma ou serviço de infraestrutura. O Google publicou dois livros de leitura obrigatória para garantir a confiabilidade dos serviços . Eles estão disponíveis gratuitamente e são uma excelente fonte de informações para um estudo mais aprofundado do tópico da SRE.

Confiabilidade como investimento permanente


Ao criar qualquer produto, montar a primeira versão é apenas o começo. Depois disso, haverá novas iterações, com novos recursos. Se o produto for bem-sucedido e lucrativo, o trabalho continuará.

Os sistemas distribuídos têm o mesmo ciclo de vida, exceto que precisam de mais investimento não apenas em novos recursos, mas também em acompanhar o dimensionamento. Quando a carga no sistema aumenta, você precisa armazenar mais dados, mais engenheiros trabalham no sistema, você precisa cuidar constantemente do seu funcionamento normal. Muitos dos que criam sistemas distribuídos pela primeira vez os consideram algo como uma máquina: depois de fazer isso, é suficiente realizar determinadas manutenções a cada poucos meses. É difícil chegar a uma comparação mais distante da realidade.

, , . Para que o hospital funcione bem, são necessárias verificações constantes (monitoramento, alertas, teste de caixa preta). O tempo todo é necessário para contratar novos funcionários e equipamentos: para hospitais, são enfermeiros, médicos e dispositivos médicos, e para sistemas distribuídos, novos engenheiros e serviços. À medida que o número de funcionários e serviços aumenta, os velhos métodos de trabalho se tornam ineficazes: uma pequena clínica no interior funciona de maneira diferente de um grande hospital em uma metrópole. Conseguir maneiras mais eficazes de funcionar se transforma em um trabalho completo, e medições e alertas estão se tornando cada vez mais importantes. Como um hospital grande requer mais pessoal, como contabilidade, recursos humanos e segurança,e a operação de grandes sistemas distribuídos depende de equipes de serviço como infraestrutura e SRE.

Para que a equipe mantenha um sistema distribuído confiável, a organização deve investir constantemente em seu funcionamento, bem como no trabalho das plataformas nas quais o sistema foi construído.

Materiais úteis


Embora o artigo tenha sido longo, apresenta apenas os momentos mais superficiais. Para saber mais sobre os recursos dos sistemas distribuídos operacionais, recomendo estas fontes:

Livros


Sites


Veja comentários sobre este artigo no Hacker News .

All Articles