Este artigo é um artigo espelhado de tradução automática, por favor clique aqui para ir para o artigo original.

Vista: 13027|Resposta: 0

O StackOverflow é tão grande, qual é a arquitetura dele?

[Copiar link]
Publicado em 11/04/2018 17:33:18 | | | |
Para facilitar a compreensão do que este artigo trata, vamos começar com a mudança na estatística média diária do Stack Overflow. Os números a seguir são das estatísticas de 12 de novembro de 2013:

  • O balanceador de carga aceitou 148.084.833 requisições HTTP
  • Dessas, 36.095.312 eram de páginas carregadas
  • 833.992.982.627 bytes (776 GB) de tráfego HTTP são usados para enviar
  • Um total de 286.574.644.032 bytes (267 GB) de dados foi recebido
  • Um total de 1.125.992.557.312 bytes (1.048 GB) de dados foi enviado
  • 334.572.103 consultas SQL (incluindo apenas de requisições HTTP)
  • 412.865.051 pedidos Redis
  • 3.603.418 pedidos de Tag Engine
  • Levou 558.224.585 ms (155 horas) em consultas SQL
  • Levou 99.346.916 ms (27 horas) para solicitações Redis
  • Gastei 132.384.059 ms (36 horas) no pedido de motor de etiquetas
  • Levou 2.728.177.045 ms (757 horas) de processamento ASP.Net processo



Os dados a seguir mostram as mudanças nas estatísticas até 9 de fevereiro de 2016, para que você possa comparar:

  • Solicitações HTTP recebidas pelo balanceador de carga: 209.420.973 (+61.336.090)
  • 66.294.789 (+30.199.477) das quais carregam a página
  • Dados HTTP enviados: 1.240.266.346.053 (+406.273.363.426) bytes (1,24 TB)
  • Quantidade total de dados recebidos: 569.449.470.023 (+282.874.825.991) bytes (569 GB)
  • Quantidade total de dados enviados: 3.084.303.599.266 (+1.958.311.041.954) bytes (3,08 TB)
  • Consultas SQL (apenas de requisições HTTP): 504.816.843 (+170.244.740)
  • Acertos no cache Redis: 5.831.683.114 (+5.418.818.063)
  • Buscas Elastic: 17.158.874 (não rastreadas em 2013)
  • Pedidos de Motor de Tags: 3.661.134 (+57.716)
  • Tempo acumulado consumido para executar consultas SQL: 607.073.066 (+48.848.481) ms (168 horas)
  • Tempo consumido no cache Redis Hit: 10.396.073 (-88.950.843) ms (2,8 horas)
  • Tempo consumido pelos pedidos do motor de tags: 147.018.571 (+14.634.512) ms (40,8 horas)
  • Tempo consumido no processamento ASP.Net: 1.609.944.301 (-118.232.744) ms (447 horas)
  • 22,71 (-5,29) ms 49.180.275 páginas de edição Tempo médio de renderização (dos quais 19,12 ms são consumidos em ASP.Net)
  • 11,80 (-53,2) ms 6.370.076 primeiras páginas Tempo médio de renderização (dos quais 8,81 ms são consumidos em ASP.Net)



Você pode estar se perguntando por que ASP.Net está processando 61 milhões de solicitações a mais por dia, mas reduzindo o tempo de processamento em 757 horas (em comparação com 2013). Isso se deve principalmente às atualizações que fizemos em nossos servidores no início de 2015, além de muito trabalho de otimização de desempenho dentro do app. Não esqueça: desempenho ainda é um atrativo. Se você estiver mais curioso sobre os detalhes específicos do hardware, não se preocupe, vou fornecer os detalhes específicos do hardware dos servidores usados para rodar esses sites em forma de apêndice no próximo artigo (atualizarei este link quando chegar a hora).

Então, o que mudou nos últimos dois anos? Não muito, só estou substituindo alguns servidores e equipamentos de rede. Aqui está uma visão geral dos servidores usados para rodar seu site hoje (note como eles mudaram desde 2013)

  • 4 servidores Microsoft SQL Server (2 dos quais usam hardware novo)
  • 11 Servidores Web IIS (Novo Hardware)
  • 2 servidores Redis (novo hardware)
  • 3 servidores Tag Engine (2 dos quais usam hardware novo)
  • 3 Servidores Elasticsearch (iguais aos acima)
  • 4 servidores de balanceamento de carga HAProxy (2 adicionados para suportar o CloudFlare)
  • 2 dispositivos de rede (núcleo Nexus 5596 + Extensor de Tecido 2232TM, todos os dispositivos atualizados para largura de banda de 10Gbps)
  • 2 x Fortinet 800C Firewall (substitui os Cisco 5525-X ASAs)
  • 2 roteadores Cisco ASR-1001 (substituem roteadores Cisco 3945)
  • 2 roteadores Cisco ASR-1001-x (NOVO!) )



O que precisamos para fazer o Stack Overflow funcionar? Pouca coisa mudou desde 2013, mas graças às otimizações e ao novo hardware mencionado acima, agora só precisamos de um servidor web. Já testamos essa situação inadvertidamente, com sucesso várias vezes. Por favor, note: eu só disse que funciona, não disse que é uma boa ideia. Mas toda vez que isso acontece, é bem interessante.

Agora que temos alguns números básicos sobre ideias de escalonamento de servidores, vamos ver como criamos essas páginas web legais. Poucos sistemas existem completamente de forma independente (e os nossos não são exceção), e sem uma visão holística que integre essas partes, o significado do planejamento arquitetônico é muito reduzido. Nosso objetivo é compreender a situação geral. Haverá muitos artigos que aprofundarão cada área específica no futuro. Este artigo é apenas um resumo da estrutura lógica do hardware chave, e o próximo artigo conterá detalhes específicos sobre esses hardwares.

Se você quiser ver como esse hardware está hoje, aqui estão algumas fotos que tirei do Gabinete A (o Gabinete B é exatamente igual a ele) quando atualizei o servidor em fevereiro de 2015:



Agora, vamos mergulhar no layout da arquitetura. A seguir está um resumo da arquitetura lógica dos principais sistemas existentes:



Princípios básicos

Aqui estão alguns princípios comuns que não precisam ser introduzidos em seguida:

  • Tudo tem backups redundantes.
  • Todos os servidores e dispositivos de rede possuem pelo menos duas conexões de largura de banda de 10Gbps.
  • Todos os servidores possuem duas fontes de energia que fornecem energia por meio de dois conjuntos de unidades UPS, dois geradores atrás deles e dois feedforwards de tensão da rede.
  • Todos os servidores têm um backup redundante localizado no Rack A e no Rack B.
  • Todos os servidores e serviços têm backups redundantes duplos em um data center separado (no Colorado), embora eu esteja cobrindo principalmente Nova York.
  • Tudo tem backups redundantes.


Internet

Primeiro você precisa encontrar nosso site, que é uma questão de DNS. Encontrar sites é rápido, então agora estamos entregando para a CloudFlare porque eles têm servidores DNS em todos os cantos do mundo. Atualizamos os registros DNS por meio de APIs, e eles são responsáveis por "gerenciar" o DNS. No entanto, em nossas mentes vilãs, ainda temos nossos próprios servidores DNS por causa dos problemas profundos de confiança. Quando o apocalipse é apocalíptico – talvez por causa da GPL, Punyon ou problemas de cache – e as pessoas ainda querem programar para desviar a atenção, mudamos para nossos próprios servidores DNS.

Assim que seu navegador encontra nosso esconderijo, o tráfego HTTP dos nossos quatro provedores (Level 3, Zayo, Cogent e Lightower em Nova York) entra em um dos nossos quatro roteadores avançados. Usamos o Border Gateway Protocol (BGP, um protocolo muito padrão) para fazer peer-to-peer de provedores de rede para controlá-lo e fornecer a forma mais eficiente de acessar nossos serviços. Os roteadores ASR-1001 e ASR-1001-X são divididos em dois grupos, cada um dos quais deve usar o modo ativo/ativo para lidar com o tráfego de ambos os provedores de rede – aqui há backups redundantes. Embora todas tenham a mesma largura de banda física de 10Gbps, o tráfego externo ainda é independente do tráfego da VLAN externa e está conectado ao balanceamento de carga separadamente. Depois que o tráfego passa pelo roteador, você chegará ao balanceador de carga.

Acho que é hora de mencionar que temos MPLS com largura de banda de 10Gbps entre os dois data centers, embora isso não esteja diretamente relacionado a serviços de sites. Utilizamos essa tecnologia para realizar replicação fora do local e recuperação rápida de dados para lidar com certas emergências. "Mas Nick, não há redundância nisso!" Bem, do ponto de vista técnico você está certo (em um sentido positivo), é realmente um ponto único de falha nesse nível. Mas espere! Por meio do provedor de rede, também temos duas rotas adicionais de failover OSPF (MPLS é a primeira escolha, e essas são a segunda e terceira opções por motivos de custo). Cada um dos conjuntos de dispositivos mencionados anteriormente será conectado ao data center do Colorado para balancear o tráfego de rede em caso de failover. Claro, poderíamos ter conectado esses dois conjuntos de dispositivos entre si, de modo que houvesse quatro conjuntos de caminhos, mas esqueça, vamos seguir em frente.

Balanceamento de Carga (HAProxy)

O balanceamento de carga é implementado com o HAProxy 1.5.15, rodando no CentOS 7 (nossa versão favorita do Linux). E adicionar o protocolo de transmissão segura TLS (SSL) no HAProxy. Também estamos de olho no HAProxy 1.7, que fornecerá suporte para o protocolo HTTP/2 imediatamente.

Diferente de outros servidores com conexões de rede LACP duplas de 10Gbps, cada balanceador de carga possui duas conexões de 10Gbps: uma para a rede externa e outra para a DMZ. Esses servidores possuem 64GB ou mais de memória para lidar com a camada do protocolo SSL de forma mais eficiente. Quando conseguimos armazenar e reutilizar mais sessões TLS na memória, consumimos menos recursos de computação ao nos conectar ao mesmo cliente. Isso significa que conseguimos restaurar as sessões de forma mais rápida e barata. A memória é tão barata que é uma escolha fácil.

O balanceamento de carga em si é fácil de configurar. Ouvimos diferentes sites em múltiplos IPs diferentes (principalmente por razões de gerenciamento de certificados e DNS) e depois direcionamos o tráfego para backends diferentes (principalmente com base nos cabeçalhos do host). A única coisa que fazemos aqui é limitar a taxa e extrair algumas informações de cabeçalho (da camada web) para fazer login nas mensagens de log do sistema do HAProxy, assim podemos registrar métricas de desempenho para cada requisição. Vamos mencionar isso em detalhes depois.

Camada web (IIS 8.5, ASP.Net MVC 5.2.3 e .Net 4.6.1)

O balanceamento de carga distribui o tráfego entre 9 servidores web primários (01-09) e 2 servidores web de desenvolvimento (10-11, nosso ambiente de teste). O servidor principal roda o Stack Overflow, Careers e todos os sites do Stack Exchange, enquanto meta.stackoverflow.com e meta.stackexchange.com estão rodando em outros dois servidores. O aplicativo principal de perguntas e respostas é multi-inquilino, ou seja, um único app lida com todas as solicitações do site de perguntas e respostas. Em outras palavras, podemos rodar todo o aplicativo de perguntas e respostas em um único pool de aplicações em um servidor. Outros aplicativos como Careers, API v2, Mobile API, etc., são independentes. Veja o que você vê no IIS para os servidores master e dev:



Aqui está a distribuição da camada web do Stack Overflow conforme vista no Opserver (nosso painel interno de monitoramento):



E aqui está o consumo de recursos desses servidores web:



Vou entrar em mais detalhes em um artigo posterior sobre por que estamos fornecendo tantos recursos em excesso, focando em build rolante, margem de manobra e redundância.

Camada de serviço (IIS, ASP.Net MVC 5.2.3, . NET 4.6.1 e HTTP. SYS)

Ao lado da camada web está a camada de serviço. Eles também rodam sobre o IIS 2012 no Windows 8.5R2. Essa camada executa alguns serviços internos que suportam a camada web e outros sistemas internos do ambiente de produção. Os dois principais serviços são: "Stack Server", que roda um motor de tags e é baseado em http.sys (não IIS); API da Providence (baseada no IIS). Um fato interessante: tive que correlacionar os dois processos para se conectar a sockets diferentes, porque o Stack Server acessava os caches L2 e L3 com muita frequência ao atualizar a lista de problemas a cada dois minutos.

As máquinas que executam esses serviços são críticas para o motor de tags e as APIs backend, então devem ser redundantes, mas não 9x redundantes. Por exemplo, carregamos todos os artigos e suas tags do banco de dados a cada n minutos (atualmente 2 minutos), o que não é baixo. Não queremos repetir essa operação de carregamento 9 vezes na camada web, 3 vezes já é seguro o suficiente para nós. Também usamos diferentes configurações de hardware para esses servidores para otimizar melhor as características computacionais e de carregamento de dados do motor de tags e dos trabalhos de índice elástico (também rodando nesta camada). O próprio "motor de tags" é um tema relativamente complexo que será abordado em um artigo dedicado. O princípio básico é que, quando você acessa o endereço /questions/tagged/java, você visita o motor de marcação para pegar as perguntas que correspondem a ele. O motor gerencia toda a correspondência de tags, exceto /search, então em todos os lugares, inclusive a nova navegação, recebem dados por meio desse serviço.

Cache & Publicação/Subscrição (Redis)

Usamos Redis em alguns lugares, e ele tem estabilidade sólida. Embora haja até 160 bilhões de operações por mês, a CPU por instância não ultrapassa 2%, o que geralmente é menor:



Usamos o Redis para sistemas de cache em nível L1/L2. O nível "L1" é o cache HTTP que funciona em um servidor web ou qualquer aplicação similar. O nível "L2" serve para obter dados via Redis após a falha do cache do nível anterior. Nossos dados são armazenados no formato Protobuf, implementado por meio do protobuf-dot-net escrito por Marc Gravel. Para o cliente Redis, usamos a biblioteca StackExchange.Redis, que é uma biblioteca open-source desenvolvida internamente. Se um servidor web não acerta nos caches L1 e L2, ele busca dados de suas fontes de dados (consultas de banco de dados, chamadas de API, etc.) e salva os resultados no cache local e no Redis. O próximo servidor pode estar ausente do cache L1 ao recuperar os mesmos dados, mas ele recuperará os dados em L2/Redis, eliminando a necessidade de consultas ao banco de dados ou chamadas de API.

Também rodamos muitos sites de perguntas e respostas, cada um com seu próprio cache L1/L2: key como prefixo no cache L1 e ID de banco de dados no cache L2/Redis. Vamos aprofundar esse tema em artigos futuros.

Além dos dois principais servidores Redis (um mestre e um escravo) rodando todas as instâncias do site, também configuramos uma instância para aprendizado de máquina (principalmente por razões de memória) usando outros dois servidores escravos dedicados. Esse grupo de servidores é usado para fornecer serviços como recomendar perguntas na página inicial e melhorar o alinhamento de trabalhos. Essa plataforma se chama Providence, e Kevin Montrose escreveu sobre ela.

O servidor principal Redis tem 256GB de RAM (cerca de 90GB usados), e o servidor Providence tem 384GB de memória (cerca de 125GB usados).

O Redis não é apenas para armazenamento em cache, ele também possui um mecanismo de publicação e assinatura onde um servidor pode publicar uma mensagem e outros assinantes podem recebê-la (incluindo o Redis dos clientes posteriores no servidor). Usamos esse mecanismo para limpar o cache L1 em outros serviços para manter a consistência do cache no servidor web. Mas tem outro uso importante: websockets.

Websockets (NetGain)

Usamos websockets para enviar atualizações em tempo real aos usuários, como notificações na barra superior, votos, nova navegação, novas respostas, comentários e muito mais.

O próprio servidor socket roda na camada web, usando sockets nativos. Esta é uma aplicação muito pequena baseada em nossa implementação de biblioteca open-source: StackExchange.NetGain. Nos horários de pico, tínhamos cerca de 500.000 conexões websocket simultâneas, o que é muita quantidade de navegadores. Curiosidade: alguns desses navegadores estão abertos há mais de 18 meses, e você vai precisar encontrar alguém para ver se esses desenvolvedores ainda estão vivos. O gráfico a seguir mostra o padrão de concorrência por websocket nesta semana:



Por que usar websockets? Na nossa escala, é muito mais eficiente do que pesquisar. Dessa forma, podemos simplesmente enviar mais dados com menos recursos e ser mais em tempo real para os usuários. Essa abordagem não está isenta de problemas: portas temporárias, controles de arquivos esgotados nos balanceadores de carga são problemas muito interessantes, e falaremos sobre eles depois.

Busca (Elasticsearch)

Spoiler: Não há muito para se empolgar aqui. A camada web utiliza o Elasticsearch 1.4 e implementa um cliente StackExchange.Elastic ultra-leve e de alto desempenho. Ao contrário da maioria das coisas, não planejamos abrir o código dessa parte, simplesmente porque ela expõe um subconjunto muito pequeno das APIs que precisamos usar. Tenho certeza de que tornar público isso compensa a perda e só vai confundir os desenvolvedores. Usamos elastic:/search nesses lugares para calcular perguntas relacionadas e dar sugestões ao fazer perguntas.

Cada cluster Elastic (um para cada data center) contém 3 nós, cada um com seu próprio índice. O site de Carreiras também possui alguns índices adicionais. Uma parte um pouco menos padrão da nossa configuração em círculos elásticos é que nosso cluster de 3 servidores é um pouco mais poderoso do que a configuração usual: cada servidor usa armazenamento SSD, 192GB de memória, rede dupla com largura de banda de 10Gbps.

O mesmo domínio de aplicação do Stack Server (sim, fomos explorados pelo .Net Core neste lugar) também hospeda um motor de tags, que também usa o Elasticsearch para indexação contínua. Aqui usamos um pequeno truque, como usar o ROWVERSION no SQL Server (a fonte de dados) para comparar com o documento de "último lugar" no Elastic. Como aparentemente é sequencial, é fácil rastrear e indexar conteúdo se for modificado após a última visita.

A principal razão pela qual usamos o Elasticsearch em vez de tecnologias como busca em texto completo em SQL é sua escalabilidade e custo-benefício. SQL é relativamente caro para CPUs, enquanto Elastic é muito mais barato e tem muitos recursos novos ultimamente. Por que não usar o Solr? Precisamos buscar pela rede (com múltiplos índices ao mesmo tempo), e o Solr não suporta esse cenário na época das nossas decisões. O motivo de ainda não termos usado o 2.x é porque os tipos mudaram muito no 2.x, o que significa que precisamos reindexar tudo se quisermos atualizar. Simplesmente não tenho tempo suficiente para planejar mudanças de requisitos e migrações.

Banco de Dados (SQL Server)

Usamos o SQL Server como uma única fonte de verdade. Todos os dados no Elastic e Redis vêm do SQL Server. Temos dois clusters SQL Server e estamos configurados com grupos de disponibilidade AlwaysOn. Cada cluster tem um servidor principal em Nova York (que assume quase toda a carga) e um servidor réplica, além de um servidor réplica no Colorado (nosso data center de recuperação de desastres). Todas as operações de cópia são assíncronas.

O primeiro cluster é um conjunto de servidores Dell R720xd, cada um com 384GB de memória, um SSD PCIe com 4TB de espaço e dois CPUs de 12 núcleos. Inclui Stack Overflow, Sites (que nome ruim, explicarei depois), PRIZM e o banco de dados do Mobile.

O segundo cluster é um conjunto de servidores Dell R730xd, cada um com 768GB de memória, um SSD PCIe com 6TB de espaço e dois processadores de 8 núcleos. Este cluster contém todos os outros bancos de dados, incluindo Carreiras, Open ID, Chat, registros de exceções e outros sites de perguntas e respostas (por exemplo, Super Usuário, Falha de Servidor, etc.).

Na camada de banco de dados, queremos manter a utilização da CPU em um nível muito baixo, embora na prática o uso da CPU seja um pouco maior quando alguns problemas planejados de cache estiverem ocorrendo (que estamos resolvendo). Atualmente, NY-SQL02 e 04 são os servidores principais e 01 e 03 são os servidores réplica, e nós os reiniciamos hoje por causa da atualização do SSD. Veja como eles se saíram nas últimas 24 horas:



Nosso uso de SQL é muito simples. Simples significa rápido. Embora algumas instruções de consulta possam ser, nossa interação com o SQL em si é feita de forma bastante nativa. Temos alguns Linq2SQL legados, mas todos os nossos novos desenvolvimentos usam Dapper, nosso framework micro-ORM open-source que usa POCO. Deixe-me explicar de outra forma: o Stack Overflow tem apenas um procedimento armazenado em seu banco de informações, e vou acabar com esse último procedimento armazenado e substituí-lo por código.

Biblioteca

Bem, vamos mudar de ideia, aqui estão coisas que podem te ajudar de forma mais direta. Já mencionei algumas delas antes, mas vou te dar uma lista das muitas bibliotecas .Net open-source que mantemos e que todos usam. Nós os disponibilizamos como código aberto porque não envolvem valor central para o negócio, mas podem ajudar desenvolvedores ao redor do mundo. Espero que você possa usá-los agora:

  • Dapper (.Net Core) – Uma framework micro-ORM de alto desempenho para ADO.Net
  • StackExchange.Redis – Um cliente Redis de alto desempenho
  • MiniProfiler – um perfilador leve que usamos em todas as páginas (também suporta Ruby, Go e Node)
  • Excepcional – Para registro de erros em SQL, JSON, MySQL, etc
  • Jil – Serialização e desserializador JSON de alto desempenho
  • Sigil – .Net CIL Generation Helper (usado quando C# não é rápido o suficiente)
  • NetGain – Servidor websocket de alto desempenho
  • Opserver – Painel de monitoramento que consulta a maioria dos sistemas diretamente e pode obter informações de Orion, Bosun ou WMI
  • Bosun – Sistema de monitoramento em segundo plano, escrito em Go






Anterior:enum Enum verifica se um valor está incluído em um Enum
Próximo:Como posso encontrar MB rapidamente
Disclaimer:
Todo software, material de programação ou artigos publicados pela Code Farmer Network são apenas para fins de aprendizado e pesquisa; O conteúdo acima não deve ser usado para fins comerciais ou ilegais, caso contrário, os usuários terão todas as consequências. As informações deste site vêm da Internet, e disputas de direitos autorais não têm nada a ver com este site. Você deve deletar completamente o conteúdo acima do seu computador em até 24 horas após o download. Se você gosta do programa, por favor, apoie um software genuíno, compre o registro e obtenha serviços genuínos melhores. Se houver qualquer infração, por favor, entre em contato conosco por e-mail.

Mail To:help@itsvse.com