aguarde...

30 de dezembro de 2019

Três maneiras de reduzir os custos da sua API HTTP (S) na AWS

Três maneiras de reduzir os custos da sua API HTTP (S) na AWS

Como você pode economizar dinheiro com a execução da API HTTP (S) na Amazon Web Services (AWS)? Neste post, nossa equipe de back-end mergulha exatamente nisso.

qui na GameAnalytics, recebemos, armazenamos e processamos eventos de 1,2 bilhão de jogadores mensais em quase 90.000 jogos. Todos esses eventos passam por um sistema que chamamos de API de coleta de dados, que encaminha os eventos para outros sistemas internos, para que eventualmente terminemos com estatísticas e gráficos em um painel, exibindo a atividade do usuário, a receita do jogo e muito mais.

A API de coleta de dados é bastante simples em princípio: os jogos nos enviam eventos como objetos JSON por meio de solicitações HTTP POST, e enviamos uma resposta curta e fazemos o evento a partir daí. Os clientes usam um de nossos SDKs ou invocam nossa API REST diretamente.

Recebemos aproximadamente cinco bilhões de solicitações por dia, cada uma contendo normalmente dois ou três eventos para um total de alguns kilobytes. A resposta é uma resposta HTTP 200 “OK” simples com um pouco de JSON, confirmando que os eventos foram recebidos. O padrão geral de tráfego é um número alto de conexões com vida útil relativamente curta: os clientes enviam, em média, pouco mais de duas solicitações HTTP por conexão. 

Então, qual você acha que é o maior custo associado à execução desse sistema na AWS, com uma frota de instâncias do EC2 atrás de um balanceador de carga?

Nós não teria imaginado que a maior parte do custo é para transferir dados para fora . A transferência de dados na partir da Internet é livre, enquanto que a transferência de dados para a Internet é cobrado entre 5 e 9 cêntimos por gigabytes .

Então, decidimos fazer algo sobre isso e ver se poderíamos economizar algum dinheiro aqui. Ficamos um pouco surpresos por não termos encontrado nada escrito sobre o que fazer nesse cenário – certamente nosso caso de uso não é totalmente incomum? – espero que isso seja útil para alguém em situação semelhante.

1. Reduza os cabeçalhos HTTP

Antes dessas alterações, uma resposta desse sistema seria assim, para um total de 333 bytes:

HTTP / 1.1 200 OK 
Conexão: Manter ativo 
Conteúdo do comprimento: 15 
Tipo 
de conteúdo: application / json accept-codificação: gzip 
Acesso-controle-permissão-origem: * 
Serviço X-GA: coletar 
métodos de acesso-controle-permissão : GET, POST, OPTIONS 
Acesso-controle-permitir-cabeçalhos: autorização, X-solicitado com, tipo de conteúdo, codificação de conteúdo

{"status": "ok"}

(Lembre-se de que as quebras de linha são CRLF e, portanto, contam como dois bytes cada.)

Como enviaríamos cinco bilhões de vezes por dia, cada byte que pudéssemos economizar economizaria cinco gigabytes de dados enviados, economizando 25 centavos por dia por byte removido.

Muito disso poderia ser simplesmente removido:

  • Os cabeçalhos de resposta Access-Control-Allow-Methods e Access-Control-Allow-Headers são cabeçalhos CORS, mas são necessários apenas em respostas a solicitações de comprovação usando o método OPTIONS, portanto, são supérfluas em respostas a solicitações POST.
  • O Access-Control-Allow-Origin ainda é necessário, mas apenas quando a solicitação é uma solicitação CORS, que podemos determinar verificando o cabeçalho da solicitação Origin . Para qualquer solicitação não enviada por um navegador da Web, podemos simplesmente omiti-la.
  • O cabeçalho Accept-Encoding é na verdade um cabeçalho de solicitação; incluí-lo na resposta não tem significado.
  • Por fim, o cabeçalho X-GA-Service já foi usado para depuração, mas não o usamos mais, portanto pode ser descartado também.

Portanto, para a grande maioria dos pedidos, a resposta seria assim:

HTTP / 1.1 200 OK 
Conexão: Keep-Alive 
Comprimento do 
conteúdo : 15 Tipo de conteúdo: application / json

{"status": "ok"}

Enviar 109 bytes em vez de 333 significa economizar US $ 56 por dia ou um pouco mais de US $ 1.500 por mês.

Portanto, é lógico que, ao reduzir os dados enviados para um terço, os custos de transferência de dados devem cair 66%, certo? Bem, os custos caíram, mas apenas 12%. Isso foi um pouco abaixo do esperado.

2. Reduza também os handshakes TLS

Obviamente, antes que possamos enviar esses 109 bytes de resposta HTTP, precisamos estabelecer uma sessão TLS, trocando várias mensagens coletivamente conhecidas como “TLS handshake”. Fizemos uma solicitação ao nosso serviço enquanto capturávamos o tráfego de rede com o Wireshark e descobrimos que ele envia 5433 bytes durante esse processo, a maior parte composta pela cadeia de certificados, ocupando 4920 bytes.

Portanto, reduzir a resposta HTTP, embora importante, não tem tanto impacto quanto reduzir o tamanho da transferência de handshake TLS. Mas como faríamos isso?

Uma coisa que reduz o tamanho da transferência de handshake é a retomada da sessão TLS. Basicamente, quando um cliente se conecta ao serviço pela segunda vez, ele pode solicitar ao servidor que retome a sessão TLS anterior em vez de iniciar uma nova, o que significa que ele não precisa enviar o certificado novamente. Observando os logs de acesso, descobrimos que 11% das solicitações estavam usando uma sessão TLS reutilizada. No entanto, temos um conjunto muito diversificado de clientes sobre os quais não temos muito controle e também não conseguimos encontrar nenhuma configuração para o AWS Application Load Balancer para tamanho de cache de sessão ou similar, portanto, não há realmente nada que pode fazer para afetar isso.

Isso reduz o número de handshakes necessários, reduzindo o número de conexões que os clientes precisam estabelecer. A configuração padrão para os balanceadores de carga da AWS é fechar as conexões inativas após 60 segundos, mas parece benéfico aumentá-lo para 10 minutos. Isso reduziu os custos de transferência de dados em 8% adicionais.

3. Verifique seus certificados

Uma cadeia de certificados realmente precisa ocupar 4920 bytes?

Inicialmente, usamos um certificado do AWS Certificate Manager. É muito conveniente: não há necessidade de copiar arquivos em qualquer lugar, o certificado se renova automaticamente e é gratuito. A desvantagem é que vários certificados intermediários são necessários para estabelecer uma cadeia confiável para um certificado raiz:

  • O certificado gameanalytics.com, 1488 bytes
  • Um certificado intermediário para “Amazon Server CA 1B”, 1101 bytes
  • Um certificado intermediário para “Amazon Root CA 1”, 1174 bytes
  • “Autoridade de certificação raiz Starfield Services”, 1145 bytes (apesar do nome, este é um certificado intermediário, não um certificado raiz)

Isso equivale a 4908 bytes, mas cada certificado possui um campo de 3 bytes de comprimento. Portanto, a mensagem do certificado de handshake TLS contém 4920 bytes de dados do certificado.

Portanto, para reduzir a quantidade de dados que precisamos enviar em cada handshake, compramos um certificado da Digicert. A corrente é muito mais curta:

  • O próprio certificado gameanalytics.com, 1585 bytes
  • “CA do servidor seguro Digicert SHA2”, 1176 bytes

Ao todo, 2767 bytes.

Portanto, considerando que os clientes estabelecem aproximadamente dois bilhões de conexões por dia, esperamos economizar quatro terabytes de dados de saída todos os dias. As economias reais foram próximas a três terabytes, mas isso ainda reduziu os custos de transferência de dados em um dia típico em quase US $ 200.

E mais oportunidades de redução de custos

Provavelmente já estamos em território de retornos decrescentes, mas há algumas coisas que não mencionamos acima:

  • Se os clientes usam HTTP / 2, a transferência de dados diminui ainda mais, à medida que os cabeçalhos de resposta são compactados. Aproximadamente 4% de nossas solicitações recebidas são feitas usando HTTP / 2, mas na verdade não temos como aumentar esse percentual. Na AWS, os Application Load Balancers (ALBs) suportam HTTP / 2 sem nenhuma configuração necessária, enquanto os balanceadores de carga “clássicos” não oferecem suporte a ele.
  • No momento, estamos usando um certificado RSA com uma chave pública de 2048 bits. Poderíamos tentar mudar para um certificado ECC com uma chave de 256 bits – presumivelmente a maioria ou todos os clientes já são compatíveis.
  • Há espaço para diminuir ainda mais o tamanho do certificado. Atualmente, usamos um certificado curinga com dois nomes alternativos de assunto; podemos economizar alguns bytes usando um certificado dedicado para o nome de domínio que este serviço usa.
  • Alguns dos clientes usam mais de uma de nossas APIs. Atualmente, eles são servidos sob nomes de domínio diferentes, mas, servindo-os sob o mesmo nome de domínio e usando regras do ouvinte ALB para rotear solicitações, o cliente precisaria apenas estabelecer uma conexão TCP e sessão TLS em vez de duas, reduzindo assim o número de Apertos de mão TLS necessários.
  • Se estivermos preparados para introduzir uma alteração incompatível da API, poderemos começar a retornar respostas “204 sem conteúdo” aos clientes. Uma resposta “204 sem conteúdo”, por definição, não possui um corpo de resposta; portanto, podemos eliminar a resposta {“status”: “ok”} , bem como os cabeçalhos de tipo e comprimento de conteúdo , economizando 70 bytes adicionais por resposta, ou aproximadamente US $ 17 por dia.

Além disso, o certificado contém URLs longos para locais de download de CRL e respondedores de OCSP, 164 bytes no total. Embora esses sejam recursos de segurança necessários, pode ser um ponto de venda para uma Autoridade de Certificação usar URLs tão curtos quanto possível. A Starfield Technologies está dando um bom exemplo aqui: usa os nomes de host x.ss2.us e o.ss2.us para esses fins.

Aqui estão algumas extensões TLS futuras que também reduziriam o tamanho dos handshakes:

  • Há um rascunho RFC para compactação de certificado TLS .
  • Há também a RFC 7924 , “Extensão de Informações em Cache”, o que significa que o servidor não precisa enviar sua cadeia de certificados se o cliente a tiver visto anteriormente. No entanto, isso não parece ter sido implementado em nenhuma biblioteca-cliente TLS e provavelmente não é suportado pelos balanceadores de carga da AWS.

Então foi isso que aprendemos até agora. Lembre-se de verificar seus certificados, pois eles podem ser maiores que o necessário, aumente o tempo limite da conexão inativa , pois é mais barato manter aberta uma conexão estabelecida e apare os cabeçalhos de resposta HTTP .

Posted in Blog
Write a comment