Archive for the ‘Arquitetura’ Tag

Otimizando um Servidor Web para Download Progressivo

No dia 27 do mês passado estive no Congresso SET, da Sociedade Brasileira de Engenharia de Televisão e Telecomunicações, onde apresentei junto com o Marcello Azambuja um artigo sobre Arquiteturas de Distribuição de Vídeos na Internet. Porém, devido ao tempo bastante limitado da apresentação, não pude entrar em detalhes mais específicos de como otimizar as arquiteturas para obter um melhor desempenho. Por isso, resolvi colocar um pouco do que está no artigo aqui no blog, já que nem todo mundo que tem interesse neste assunto estava na apresentação. (O slides podem ser vistos aqui)

Assim, resolvi começar falando especificamente sobre o Download Progressivo, ou seja, como podemos otimizar um Web Server para que ele seja capaz de servir a maior quantidade de usuários possível. No caso de distribuição de vídeos via Progressive Download temos algumas características básicas que devem ser consideradas para otimização:

  • As conexões com o WebServer são persistentes, ou seja, o usuário fica conectado ao servidor por uma quantidade razoável de tempo, até que o conteúdo seja completamente copiado;
  • Dependendo da quantidade de vídeos diferentes podemos ter diversos conteúdos diferentes sendo acessados simultaneamente;
  • Os arquivos de mídia não estão necessariamente nos discos do servidor. Eles podem estar em um repositório central sendo acessados pelo servidor Web através de NFS/CIFS;
  • A quantidade de acessos ao serviço pode aumentar rapidamente, variando de acordo com o conteúdo disponibilizado;

Com estas características já podemos identificar alguns gargalos principais:

  • Tráfego de rede: muitos usuários conectados realizando download irão rapidamente ocupar a banda disponível. Além disso, existe a ocupação de banda pela cópia dos arquivos do repositório central para o servidor;
  • IO (Repositório Central e Disco Local): muitos usuários realizando download significa muito IO de leitura, que é maior a medida que temos mais usuários acessando arquivos diferentes;
  • Memória: quanto mais usuários, mais memória o Apache irá precisar para atendê-los, e quanto mais arquivos, mais memória o kernel irá utilizar para cache;
  • CPU: quanto mais usuários mais processamento para organizar e servir as requisições;

Na verdade, se queremos obter o máximo de uma arquitetura não podemos nos limitar a olhar apenas um componente. O primeiro ponto, e mais simples, consiste em alterar as configurações do Apache, escolher a versão correta (2.X), e compilar uma versão do Web Server que tenha apenas aquilo que é relevante para a distribuição de arquivos, ou seja, devemos evitar incluir na configuração de compilação módulos que não serão utilizados, como mod ssl, por exemplo.

Uma vez compilado cabe a nós decidir qual arquitetura interna de funcionamento possui uma performance maior: se é a multi-process (Apache Prefork) ou a multi-process/multi-thread (Apache Worker).

Basicamente, no prefork, para cada conexão será criado um processo específico para atendê-la, de modo que a quantidade de processos httpd será sempre maior ou igual a quantidade de clientes. Nesta arquitetura, temos uma quantidade inicial que processos que são pré-criados para atender as conexões, sendo que a medida que os clientes se conectam, eles são atendidos por estes processos. No prefork, temos basicamente dois problemas em termos de performance:

  • Memória: cada processo criado ocupa uma porção da memória, de modo que a memória disponível para cache do kernel é cada vez menor, ao ponto que toda ela é preenchida pelos processos httpd, momento onde o servidor começa a fazer swap em disco;
  • Load: a criação de novos processos (fork) gera um overhead no sistema, de modo que quanto maior a taxa de conexão, maior será o overhead no fork para atender as novas conexões;

A outra opção de MPM, o worker, trabalha com o conceito multi-thread, onde as requisições são atendidas por threads e não por processos. Assim, teríamos apenas um ou poucos processos com múltiplas threads em cada um deles, atendendo cada uma das conexões. Com o worker, minimizamos a utlização de memória, já que as threads compartilham a mesma área de memória do processo pai, e reduzimos o overhead na criação de processos. Na verdade, no caso do worker, definimos a quantidade de threads por processo de modo que só realizamos um fork quando este limite é atingido.

A escolha entre prefork ou worker deve ser realizada caso a caso, ou seja, dependendo da situação de uso e da configuração de hardware uma escolha poderá ser melhor que a outra. No caso específico de servidores para utilização em distribuição de vídeos em Progressive Download, a escolha do MPM Worker é recomendada, com o objetivo de reduzir a ocupação de memória pelo servidor Web, deixando-a livre para que o kernel possa utilizá-la para cache de conteúdo, reduzindo o IO no disco e conseqüentemente o tempo de resposta da requisição. Uma configuração interessante seria neste caso seria forçar a criação de todos os processos filho (children) e suas threads no momento que o servidor Web for iniciado, reduzindo assim o overhead de controle de threads.

Além de configurar o MPM, o Apache permite diversas outras configurações capazes de aumentar o desempenho do mesmo, como a possibilidade de desabilitar Hostname Lookups e DNS resolving, e outras que podem ser encontradas na documentação do Apache.

Entretanto, quando falamos de arquiteturas de distribuição de vídeo que recebem grandes volumes de acesso, devemos nos preocupar um pouco mais em como otimizar o processo de distribuição. Uma das maneiras mais eficientes de aumentar a capacidade de uma infra-estrutura de Download Progressivo consiste em realizar um processo de cache intensivo. Ter uma estratégia de cache de conteúdo é fundamental quando falamos de alta performance com um grande volume de acessos. Quando temos um volume muito grande, temos também diversos requests ao mesmo conteúdo. Processar todo o request para cada cliente consistiria em repetir as mesmas operações diversas vezes, sem aproveitar para um request os dados processados por outro. Assim, fica claro que podemos otimizar este processamento, simplesmente aproveitando aquilo que serve para mais de uma requisição.

Em situações onde temos uma arquitetura onde o Apache funciona como uma espécie de “proxy” entre o usuário e um repositório central de vídeos, essa necessidade é ainda mais latente, uma vez que não faz sentido obter um mesmo arquivo diversas vezes neste repositório para servir para os clientes a cada request semelhante. Uma vez copiado do storage para o Apache, para servir uma requisição, o arquivo pode ser mantido no servidor Web para as requisições posteriores realizadas ao mesmo vídeo. Com essa abordagem, reduzimos a carga no storage e o tráfego de rede e de operações de cópia.

Para realizar o cache de conteúdo existem diversas alternativas, como o Squid e o mod_cache. O mod_cache, por estar integrado ao Apache e por ser bastante conhecido e utilizado, além de apresentar uma ótima performance, é uma excelente opção para cache em arquiteturas de distribuição em Download Progressivo.

O mod_cache permite duas abordagens principais de cache: disco ou memória. O mod_disk_cache é o módulo responsável pelo cache em disco, e funciona da seguinte forma: ao receber um request, o mod_disk_cache verifica se o conteúdo solicitado já está no cache em disco. Se estiver, o módulo valida se o conteúdo não está expirado e serve o mesmo a partir do cache, sem que a requisição seja passada aos demais módulos do Apache. Caso, não esteja cacheado, o request passa pelo path normal de atendimento ao request, sendo que, ao finalizar o processamento, o mod_cache escreve a resposta no cache antes de servir a mesma. Assim, ele cria dois arquivos em disco: o .data, com o conteúdo do request, e o .header com os headers da resposta. O nome dos arquivos criados é criado a partir de um hash, para evitar que o conteúdo seja sobre-escrito.

O mod_mem_cache, é o módulo responsável pelo cache em memória, e funciona de forma semelhante ao mod_disk_cache, porém armazenando o conteúdo em memória.

A escolha de um dos módulos para utilização em um servidor de Download Progressivo também depende da configuração do hardware. Entretanto, em servidores que rodam em Linux, o uso do mod_disk_cache é recomendada, uma vez que o gerenciamento do cache em memória feito pelo kernel é bastante eficiente.

Com um web server bem otimizado e configurado, e com uma estratégia de cache eficiente, temos uma arquitetura de distribuição muito mais robusta e capaz de atender um volume até 70% maior que o volume que atenderíamos utilizando apenas um servidor com as configurações “default”, o que reduz de forma significativa os custos de investimento para expansão da capacidade, tornando esta uma opção bastante interessante para distribuição de vídeos na internet.

Advertisements

Escolhendo a melhor arquitetura para delivery de vídeos

Apesar de parecer algo simples, a escolha correta de uma arquitetura de delivery de mídia é tão fundamental para o serviço quanto a definição dos parâmetros de codificação, isto porque ela também pode impactar de forma decisiva na qualidade da experiência do usuário. Distribuir um vídeo que demora muito para carregar, ou que “trava” constantemente, muitas vezes é pior do que ter uma experiência contínua porém com pior qualidade. Por isso, a escolha da arquitetura de delivery está fortemente atrelada ao target bitrate escolhido.

Existem duas formas básicas de se distribuir vídeos na internet: via Streaming ou Progressive Download. O streaming é a tecnologia que permite o envio de informação através de pacotes, que são solicitados sob demanda pelo cliente, ou seja, o cliente solicita ao servidor apenas os pacotes necessários para exibir o conteúdo em um determinado momento. Uma vez consumidos, os pacotes deixam de existir na máquina do cliente. O progressive download, nada mais é que um simples download, porém com a vantagem de que o usuário pode começar a reproduzir o conteúdo sem que seja necessário que o download esteja finalizado. No progressive download, o conteúdo é efetivamente copiado para a máquina do usuário, e, de acordo com as diretivas de cache, pode ficar armazenado lá por um determinado período de tempo.

Vocês agora podem se perguntar qual dessas duas opções é melhor, e a resposta é simples: depende dos seus objetivos e das características do cenário em que a solução será utilizada. Simplificando, você deve se fazer as seguintes perguntas antes de escolher qual modelo de distribuição será utilizado:

  • Qual o target bitrate total dos vídeos que serão distribuídos?
  • Qual é a largura de banda disponível média dos usuários que irão acessar o conteúdo?
  • A segurança do conteúdo é uma prioridade? (Pirataria)
  • Os custos da infra-estrutura são um fator limitante?

Se suas respostas indicam que a largura de banda disponível média dos usuários é pelo menos 20% maior que o target bitrate total dos vídeos que serão distribuídos, então a situação é bastante confortável, e a escolha entre streaming e progressive download será decidida nas outras duas questões. Caso a segurança seja uma preocupação primordial, ou seja, o conteúdo oferecido é altamente sensível à pirataria, então recomendo fortemente a utilização do streaming. Isto porque o conteúdo distribuído via streaming não fica armazenado no cliente após a reprodução, o que dificulta um pouco as coisas para o lado de quem quer copiar. Além disso, não é possível fazer o download do arquivo através de um simples GET HTTP, já que a maioria dos servidores de streaming utilizam protocolos diferentes (rtmp para Flash e rtsp para WMV). Entretanto, não pense que será impossível copiar o conteúdo, já que existem alguns programas que “tocam” o vídeo como se fossem um player, mas na verdade estão copiando o bitstream para um arquivo local (WMRecoder, etc).

Por outro lado, ainda no cenário ideal onde a disponibilidade de banda é maior que o target bitrate, caso os custos sejam uma preocupação crítica, então a melhor opção seria o progressive download, já que podemos montar um servidor utilizando apenas tecnologias open-source (Apache), e podemos compartilhar os recursos com outras aplicações, como um web-server. Desta forma, não é necessário um hardware dedicado ao serviço, o que pode acontecer utilizando streaming, principalmente em Windows Media. Além disso, um servidor de progressive download suporta, naturalmente, mais conexões que um servidor de streaming, já que ele dispensa uma série de controles do fluxo de bits de cada conexão.

Voltando ao mundo real, o que acontece quando a disponibilidade de banda dos usuários não é lá grandes coisas? Neste caso, a melhor opção é sem dúvidas o progressive download. Caso o usuário não tenha uma banda disponível suficiente e a distribuição seja feita via streaming, a reprodução do conteúdo será interrompida constantemente para rebufferings, já que o fluxo de bits de saída é maior que o de entrada, e não há nada mais irritante para o usuário do que interrupções na reprodução. O mesmo problema acontece para o progressive download, com um porém: o YouTube ensinou uma valiosa lição para o usuário, “aperte o pause e espere carregar”! Assim, a diferença, neste caso, entre o streaming e o progressive download, está na possibilidade de pausar a reprodução até que o conteúdo esteja suficientemente carregado para uma reprodução contínua. Esta é uma “feature” que definitivamente faz toda diferença quando falamos na satisfação do usuário.

Um bom argumento daqueles que defendem o uso do streaming é a possibilidade de seek para qualquer posição do vídeo, independente da necessidade de carregamento. Entretanto, se estivermos falando de Flash Vídeo, este argumento é totalmente inválido. Com Flash Vídeo podemos implementar de forma relativamente simples uma solução de seek para qualquer posição, sem que seja necessário que a posição desejada já tenha sido recebida (mod_flv_streaming).

Resumindo, a escolha entre progressive download e streaming deve ser feita baseada nas características específicas de cada situação, e não podemos dizer para usar sempre um ou outro, pois ambos possuem vantagens e desvantagens. Na verdade, a chave para escolha da melhor arquitetura consiste na avaliação correta de todos os pontos envolvidos no processo de delivery, considerando assim os trade-offs de cada solução.

Scaling Down

Quando falamos de arquiteturas escaláveis, a primeira coisa que nos vem a mente é ter um projeto de hardware/software que permita o aumento da capacidade de processamento de acordo com o aumento da demanda. Isto significa que o esforço para atender uma demanda crescente deve ser o mínimo possível, limitando-se, na maioria das vezes, apenas em investimento em infra-estrutura de hardware. Entretanto, o conceito de escalabilidade é muito mais amplo que isto, e não deve ser limitado às condições de crescimento da demanda, mas também deve considerar uma redução significativa desta. O problema é que a grande maioria das pessoas que projetam arquiteturas estão preocupadas apenas com o scaling up e se esquecem totalmente do scaling down, e isto pode ser bastante arriscado também. Theo Schlossnagle, no seu livro Scalable Internet Architectures, nos dá inúmeros exemplos de empresas que, por não possuirem uma arquitetura totalmente escalável, simplesmente faliram durante o estouro da bolha da Internet por não serem capazes de cortar seus custos operacionais.

O problema de scaling down geralmente é mais comum em empresas de médio e grande porte, uma vez que a demanda inicial já é bastante grande, o que exige uma arquitetura inicial mais robusta e complexa. Porém, todo software tem um ciclo de vida, e existe uma probabilidade grande de que após alguns anos a demanda torne-se cada vez menor. Neste ponto, quando a demanda atual passa a ser menor que a inicial, muitas vezes a estrutura torna-se super dimensionada, e os custos de operação não são mais justificáveis. Além disso, nem sempre é interessante, do ponto de vista de posicionamento de negócios, tirar o software do ar. É aí que a necessidade de reduzir a estrutura vira uma questão de sobrevivência (sendo um pouco radical).

Você pode estar se perguntando agora: Se eu tenho uma arquitetura que é facilmente “escalável para cima”, porque ela não seria “escalável para baixo”? Quais são as características necessárias para um scaling down? Na verdade, em qualquer arquitetura existe um limite de quão simples e barata ela pode ser. O princípio geral da escalabilidade nos diz que quanto melhor for o isolamento entre as diferentes camadas do software, mais fácil é expandir a capacidade dos gargalos existentes nele. Para um scale down eficiente, também é fundamental que os componentes que foram isolados sejam construídos de forma uniforme, e rodem em plataformas compatíveis, de modo que todos os componentes possam coexistir em um único ambiente, que, no caso mais extremo, seria uma máquina Google Like. Esta é a visão ideal de escalabilidade: ter um software (inclusive com suas dependências), que seja capaz de rodar em uma máquina de supermercado, e que também possa funcionar perfeitamente em 50 servidores Dual Quad Core em cluster, e que a quantidade de informação processada varie linearmente de acordo com o aumento da capacidade.

Para que isto seja viável, é fundamental considerar os requisitos mínimos de cada componente, avaliando a compatibilidade destes requisitos com os demais componentes. Por exemplo, se um determinado componente só roda em Solaris, e o outro só em Windows, a coexistência dos dois em um único hardware fica comprometida. Atualmente, ainda temos a saída da virtualização, mas pode ser que nem todos os componentes rodem sem problemas em ambientes virtualizados. Além disso, requisitos mínimos de memória, disco e processamento irão delimitar o quão simples poderá ser a estrutura.

Resumindo, temos todos que nos preocupar não somente com o que fazer quando as coisas estão se expandindo, mas também é muito importante ter um plano claro de o que pode ser feito para enxugar a estrutura, direcionando os recursos para pontos mais prioritários. Quando você estiver projetando uma arquitetura, lembre-se de que em algum momento o scaling down pode ser a única alternativa para manter um produto no ar.