• Nenhum resultado encontrado

Escalabilidade em Aplicações Web: Estudo de Caso em um Sistema Ruby on Rails

N/A
N/A
Protected

Academic year: 2021

Share "Escalabilidade em Aplicações Web: Estudo de Caso em um Sistema Ruby on Rails"

Copied!
36
0
0

Texto

(1)

Universidade Federal do Rio Grande do Norte Centro de Ciências Exatas e da Terra

Departamento de Informática e Matemática Aplicada Bacharelado em Engenharia de Software

Escalabilidade em Aplicações Web: Estudo de

Caso em um Sistema Ruby on Rails

Luiz Rogério de Carvalho Neto

Natal-RN Maio 2017

(2)

Luiz Rogério de Carvalho Neto

Escalabilidade em Aplicações Web: Estudo de Caso em

um Sistema Ruby on Rails

Monografia de Graduação apresentada ao Departamento de Informática e Matemática Aplicada do Centro de Ciências Exatas e da Terra da Universidade Federal do Rio Grande do Norte como requisito parcial para a ob-tenção do grau de bacharel em Engenharia de Software.

Orientador

Prof. Dr. Fernando Marques Figueira Filho

Universidade Federal do Rio Grande do Norte – UFRN Departamento de Informática e Matemática Aplicada – DIMAp

Natal-RN Maio de 2017

(3)

Catalogação da Publicação na Fonte. UFRN / SISBI / Biblioteca Setorial Centro de Ciências Exatas e da Terra – CCET.

Carvalho Neto, Luiz Rogério de.

Escalabilidade em aplicações web: estudo de caso em um sistema Ruby on Rails / Luiz Rogério de Carvalho Neto. - Natal, 2017.

34 f.: il.

Orientador: Prof. Dr. Fernando Marques Figueira Filho.

Monografia (Graduação) – Universidade Federal do Rio Grande do Norte. Centro de Ciências Exatas e da Terra. Departamento de Informática e Matemática Aplicada.

1. Engenharia de software – Monografia. 2. Escalabilidade – Monografia. 3. Performance – Monografia. 4. Ruby – Monografia. 5. Rails – Monografia. I. Figueira Filho, Fernando Marques. II. Título.

(4)

Monografia de Graduação sob o título Escalabilidade em Aplicações Web: Estudo de Caso em um Sistema Ruby on Rails apresentada por Luiz Rogério de Carvalho Neto e aceita pelo Departamento de Informática e Matemática Aplicada do Centro de Ciências Exatas e da Terra da Universidade Federal do Rio Grande do Norte, sendo aprovada por todos os membros da banca examinadora abaixo especificada:

Prof. Dr. Fernando Marques Figueira Filho

Orientador

Departamento de Informática e Matemática Aplicada UFRN

Prof. Dr. Gibeon Soares De Aquino Junior

Departamento de Informática e Matemática Aplicada UFRN

Prof. Dr. Uirá Kulesza

Departamento de Informática e Matemática Aplicada UFRN

(5)

Ao meu avô, Humberto do Nascimento Dantas, que sonhou com este momento mais do que qualquer pessoa.

(6)

Agradecimentos

Este trabalho não teria sido possível sem a ajuda de muitas pessoas a quem sou profundamente grato.

Em primeiro lugar, gostaria de agradecer aos meus pais por todo o suporte. Muito obrigado por me fazer acreditar e trabalhar duro para chegar até aqui. Agradeço também a minha namorada, Vanessa, por todo o amor e apoio durante essa jornada.

Muito obrigado, Matheus Gadelha, pela amizade incondicional e por se fazer presente, mesmo quando distante, nos momentos mais importantes! Um agradecimento especial para aqueles que tornaram a rotina das aulas menos cansativa e mais divertida: Andreza Medeiros, Bernardo Gurgel, Iago Moreira, Igor Marques, Lucas Bibiano, Matheus Campos e Waldyr Araújo.

Por último, agradeço a esta Universidade e todos os professores que ajudaram a cons-truir quem sou hoje. Em especial, gostaria agradecer ao professor Fernando que, além de orientador, é um amigo que me motivou a buscar o meu melhor enquanto aluno e profissional. Agradeço também aos professores Gibeon e Uirá, que estiveram presentes em diversos momentos da minha graduação, por aceitarem fazer parte da banca avaliadora deste trabalho.

(7)

Remember these 6 rules. Trust yourself, break some rules, don’t be afraid to fail, ingore the naysayers, work like hell, and give something back.

(8)

Escalabilidade em Aplicações Web: Estudo de Caso em

um Sistema Ruby on Rails

Autor: Luiz Rogério de Carvalho Neto Orientador: Prof. Dr. Fernando Marques Figueira Filho

Resumo

Escalabilidade é uma qualidade desejada em sistemas de software, especialmente naqueles disponíveis na web, que podem sofrer picos de demanda a qualquer momento. Este tra-balho apresenta um estudo de caso realizado em uma aplicação construída utilizando o framework Ruby on Rails. Este estudo foi feito especificamente sobre a funcionalidade de sugestão personalizada de vagas via e-mail para os mais de 500.000 usuários cadastrados. É sugerida uma nova abordagem para implementação da funcionalidade, analisando em detalhes as diferentes soluções através de métricas coletadas nos experimentos realizados. Os resultados demonstram uma diminuição expressiva do consumo de recursos na solu-ção proposta, sendo esta uma solusolu-ção escalável. Estudos futuros podem ser feitos com a realização de experimentos em servidores com múltiplos processadores a fim de encontrar mais benefícios para a solução proposta, dada a natureza paralelizável desta.

(9)

Web Applications Scalability: Case Study on a Ruby on

Rails System

Author: Luiz Rogério de Carvalho Neto Advisor: Prof. Dr. Fernando Marques Figueira Filho

Abstract

Scalability is a desirable attribute of software systems, particularly on web applicati-ons, which may deal with large traffic spikes at any time. This work presents a case study on an application built using the Ruby on Rails framework. This study specifically targets a mailing list with customized content feature, which runs for more than 500.000 registe-red users. A new approach is suggested to implement that feature, thoroughly analyzing the nuances of each solution through metrics from performed experiments. Results show a significant decrease in resource consumption for the proposed solution. Future studies may perform experiments on multi-processor servers in order to see if further benefits can be observed on the suggested solution, due to its parallelizable nature.

(10)

Lista de figuras

1 Gráfico do consumo de memória pela quantidade de usuários processados. Linhas em tons de vermelho representam a Solução 1, linhas em tons de azul representam a Solução 2, linhas em tons de verde representam a

Solução 3. . . p. 30 2 Gráfico de barra com a média e variância dos valores máximos de cada

solução. Barra vermelha representa a solução 1, barra azul representa a

(11)

Lista de tabelas

(12)

Lista de códigos

3.1 Worker (Solução padrão) . . . p. 23 3.2 Enqueuer (Solução proposta) . . . p. 25 3.3 Worker (Solução proposta) . . . p. 25 3.4 Worker (Solução proposta com coleta do lixo de memória forçada) . . . p. 26 3.5 Script de linha de comando . . . p. 28

(13)

Sumário

1 Introdução p. 13 1.1 Organização do Trabalho . . . p. 14 2 Referencial Teórico p. 15 2.1 Escalabilidade . . . p. 15 2.1.1 Tipos de escalabilidade . . . p. 15 2.1.2 Exemplo de escalabilidade . . . p. 16 2.1.3 Paralelismo e assincronicidade . . . p. 17 2.2 Teoria das Filas . . . p. 17 2.2.1 Atrasos de comunicação . . . p. 18 2.2.2 Classificação de um Sistema de Filas . . . p. 18 2.2.3 Teorema de Little . . . p. 20 2.3 Ruby on Rails e Sidekiq . . . p. 20

3 Metodologia p. 22

3.1 Sistema . . . p. 22 3.1.1 Walljobs . . . p. 22 3.1.2 Sugestão Personalizada de Vagas via Email . . . p. 23 3.2 Solução Proposta . . . p. 24 3.2.1 Utilização de Múltiplos Processos . . . p. 25 3.2.2 Coletor de lixo de memória . . . p. 26 3.3 Configuração dos Experimentos . . . p. 27

(14)

3.3.1 Hardware . . . p. 27 3.3.2 Software . . . p. 27 3.4 Métricas . . . p. 28 4 Resultados e Discussão p. 29 4.1 Resultados . . . p. 29 4.2 Discussão . . . p. 31 5 Conclusão p. 33 Referências p. 34

(15)

13

1

Introdução

Escalabilidade é um ponto crítico para o sucesso de muitas empresas que têm seus negócios disponíveis na web e que podem, a qualquer momento, sofrer um pico de demanda de seus recursos. Podemos definir escalabilidade como a habilidade de uma aplicação se adaptar e se expandir de acordo com suas necessidades de negócio (CHIEU et al., 2009). Escalabilidade é um atributo desejável em sistemas de software. Sistemas não escaláveis podem ter a performance comprometida, necessitando passar por uma reestruturação do software (BONDI, 2000).

O contexto de desenvolvimento Ruby on Rails (HANSSON, 2008) é conhecido por promover o conceito de convention over configuration1, ou seja, é preferível a adoção de

padrões que dispensarão o desenvolvedor de ter que configurar determinadas partes do software a ser desenvolvido. Desta forma, são adotadas soluções genéricas e simplistas com o intuito de agilizar o processo de desenvolvimento, mas que podem ser de alto custo computacional ou não se adequar a situações específicas. Esses padrões são benéficos para a prototipagem de soluções mas podem se tornar um gargalo de performance de acordo com o crescimento da aplicação.

Envio de email em massa é um exemplo claro em que a situação descrita acima ocorre. Ao buscar propostas na web de como implementar tal funcionalidade, facilmente encontramos formas2 3 4 convencionais de fácil implementação porém possivelmente não

escaláveis.

Essas soluções envolvem carregar uma lista de usuários para então iterá-la sequenci-almente, processando um usuário e suas dependências de cada vez. A natureza sequencial desse tipo de solução torna difícil a utilização da paralelização dessa rotina e impossibi-lita otimizações em sistemas multithreaded. Também é possível observar que, conforme a

1https://en.wikipedia.org/wiki/Convention_over_configuration

2http://stackoverflow.com/questions/20072939/send-email-to-all-users-in-rails 3http://stackoverflow.com/questions/22786202/send-mail-to-all-users

(16)

14

utilização do sistema aumentar, o mesmo passará a ter um crescimento no uso de memó-ria, haja vista que haverá um número crescente de usuários que, por sua vez, terão mais dependências. Todos esses pontos corroboram com a noção de que tais soluções não são escaláveis.

Assim sendo, o estudo de melhorias para esta situação se mostra válido e necessário para desenvolvedores que lidam com aplicações web em larga escala. Este trabalho pro-põe soluções utilizando filas de prioridade e paralelização da rotina de processamento dos usuários. Foi realizado um estudo de caso comparando as diferentes soluções e seus im-pactos no consumo de memória, tempo de execução e, consequentemente, escalabilidade do sistema.

1.1

Organização do Trabalho

No Capítulo 2 serão apresentadas noções sobre assuntos relevantes para o entendi-mento deste trabalho como: escalabilidade, Teoria das Filas, Ruby on Rails e Sidekiq (PERHAM, 2012). O Capítulo 3 traz informações sobre o Walljobs e a Sugestão Personali-zada de Vagas via Email, sistema e funcionalidade utilizados neste estudo respectivamente. Ainda no Capítulo 3 serão apresentadas em detalhes as soluções propostas para o pro-blema de escalabilidade, bem como as métricas para comparação e avaliação das diferentes abordagens.

No Capítulo 4 são mostradas as especificações de Hardware e Software utilizados nos experimentos deste trabalho. Também no Capítulo 4 serão discutidos minuciosamente os resultados obtidos. O Capítulo 5 traz a conclusão deste estudo e ideias para trabalhos futuros.

(17)

15

2

Referencial Teórico

Neste capítulo, serão apresentadas informações sobre escalabilidade na seção 2.1, Te-oria das Filas na Seção 2.2, e Ruby On Rails e Sidekiq Seção 2.3.

2.1

Escalabilidade

Escalabilidade é um atributo desejável em um sistema. O conceito de escalabilidade conota a habilidade de um sistema acomodar um volume maior de carga sem que haja degradação de performance, ou seja, um sistema que é susceptível ao crescimento. Por outro lado, um sistema é considerado não escalável quando existe um custo adicional excessivo para lidar com uma demanda maior, ou o sistema simplesmente não consegue lidar com essa demanda. O custo pode ser quantificado através do uso de memória, tempo de resposta, espaço em disco, ou até mesmo dinheiro. A escalabilidade de um sistema é crucial para seu sucesso, visto que falhar em escalar pode prejudicar a qualidade do serviço oferecido.

2.1.1

Tipos de escalabilidade

No presente trabalho, foram considerados três tipos diferentes de escalabilidade: es-calabilidade de carga, eses-calabilidade de espaço, e eses-calabilidade estrutural. Um sistema pode ter mais de um desses atributos. Além disso, um ou mais tipos de escalabilidade podem interagir entre si.

• Escalabilidade de carga. Um sistema possui escalabilidade de carga quando con-segue funcionar normalmente, ou seja, sem atrasos ou consumo excessivo de recursos em situações de demanda leve, moderada, ou pesada.

• Escalabilidade de espaço. Um sistema possui escalabilidade de espaço quando seu uso de memória não ultrapassa os limites desejados mesmo que haja um aumento

(18)

16

na quantidade de itens a serem suportados.

• Escalabilidade estrutural. Um sistema é dito estruturalmente escalável quando é possível expandí-lo em uma dimensão escolhida sem a necessidade de fazer grandes mudanças em sua arquitetura, isto é, nas definições de seus componentes, proprie-dades e relacionamentos externos.

2.1.2

Exemplo de escalabilidade

Na Biblioteca Central Zila Mamede, visitantes precisam enfrentar uma fila para guar-dar e retirar suas mochilas. Os recursos escassos são os armários e os atendentes. Mochilas são guardadas nos armários e existem algumas estantes de armários. Cada estante é man-tida exclusivamente por um atendente. O atendente faz uma busca em uma estante para encontrar um armário vazio ou para encontrar a mochila de um visitante. Nosso objetivo é minimizar o tempo de espera dos visitantes na fila dos armários.

A performance desse sistema decai bruscamente em horários de pico. Primeiro, o tempo de serviço aumenta de acordo com a ocupação dos armários, pois se torna mais demorado encontrar os armários vazios. Segundo, o sistema é propenso a deadlock, por exemplo, no início da tarde muitos visitantes que chegaram pela manhã estarão de saída, ao passo que novos visitantes estarão chegando para passar a tarde na biblioteca. O deadlock ocorre quando não existem mais armários vazios, e os visitantes que querem coletar suas mochilas estão no fim da fila, enquanto os visitantes que querem guardar as mochilas estão no começo da fila. Os atendentes podem resolver o deadlock dando preferência aos visitantes que desejam retirar as mochilas, porém essa é uma solução ineficiente.

Seguindo as regras supracitadas, os armários da biblioteca vão funcionar adequada-mente em demandas leves e com baixa ocupação dos armários, mas esse sistema vai certa-mente sofrer em situações de demanda intensa. Independente da quantidade de atendentes e de armários, esse sistema vai entrar em deadlock quando a quantidade de visitantes for suficientemente grande. Aumentar a quantidade de armários só vai protelar o início do deadlock, não elimina sua possibilidade. A escalabilidade de carga é ainda mais compro-metida porque o tempo de espera de um armário ocupado é aumentado de duas formas:

1. O tempo de busca por armários vazios aumenta de acordo com a ocupação.

(19)

ar-17

mários) precisa esperar por todos os outros que desejam guardar suas mochilas. Aumentando assim o tempo para liberar um armário.

Perceba que os impedimentos para a escalabilidade deste sistema variam de acordo com a hora do dia. Pela manhã, quando todos os visitantes precisam guardar suas mochi-las, o maior impedimento é a quantidade de atendentes. O mesmo vale para o fim do dia, quando todos os visitantes querem buscar suas mochilas. No início da tarde, o principal impedimento são as regras da fila, que levam a um deadlock.

Para este sistema, a escalabilidade de carga pode ser aprimorada com as seguintes modificações.

1. Deveriam existir filas separadas para os visitantes que vão guardar suas mochilas e para aqueles que vão retirá-las, com a prioridade sendo destes com o intuito de liberar os armários mais rapidamente.

2. Uma lista ordenada dos armários vazios poderia ser mantida para cada estante, assim reduzindo o tempo de procura.

A primeira modificação é fácil de ser implementada e, sozinha, traria um benefício substancial porque elimina o risco de deadlock e reduz a ocupação dos armários. A segunda modificação reduz o custo das buscas, porém traz consigo o custo de manter as listas atualizadas.

2.1.3

Paralelismo e assincronicidade

Paralelismo é uma forma de computação em que vários cálculos são realizados simul-taneamente. Sob a ótica de que grandes problemas podem ser divididos em problemas menores, que poderão ser resolvidos paralelamente. Um sistema é considerado paralela-mente escalável quando este está apto a tomar proveito da presença de múltiplos proces-sadores. Em contrapartida, argumenta-se que sistemas que não possuem escalabilidade de carga podem não estar fazendo uso adequado de paralelismo e de técnicas assíncronas, que trazem oportunidades de otimização no uso de recursos.

2.2

Teoria das Filas

A Teoria das Filas é o campo matemático que estuda as filas. São construídos mo-delos para que o tamanho e tempo de espera de uma fila possa ser previsto. O início da

(20)

18

Teoria das Filas se deu a partir de pesquisas feitas pelo matemático dinamarquês Agner Krarup Erlang, quando ele criou modelos para descrever a rede de ligações telefônicas de Copenhague.

2.2.1

Atrasos de comunicação

Ao passo em que um sistema se torna congestionado, o atraso no serviço do sistema aumenta. É essencial que haja um bom entendimento sobre o relacionamente entre con-gestionamento e atraso para que se possa criar algoritmos de controle de carga eficientes. Existem diferentes aspectos de atrasos em um sistema (de mensagens), que podem ser classificados da seguinte forma:

• Atraso de Processamento. É o tempo entre a chegada de um pacote para trans-missão até o ponto em que este entra na fila. No lado do receptor é o tempo entre o recebimento do pacote na fila e o ponto em que a mensagem começa a ser processada. Este atraso depende da velocidade da CPU e a carga do sistema.

• Atraso de Enfileiramento. É o tempo entre a entrada do pacote na fila de trans-missão e o ponto em que a mensagem é transmitida. Este atraso depende da carga no link de comunicação.

• Atraso de Transmissão. É o tempo entre a transmissão do primeiro bit do pacote até a transmissão do último bit. Este atraso depende da velocidade do link de comunicação.

• Atraso de Propagação. É o tempo entre a transmissão do último bit do pacote até o ponto em que este último bit chega no receptor. Este atraso depende das características físicas do link de comunicação.

• Atraso de Retransmissão. É o resultado de quando um pacote é perdido e precisa ser retransmitido. Este atraso depende da taxa de erro no link de comunicação e do protocolo utilizado para retransmissão.

2.2.2

Classificação de um Sistema de Filas

Os pontos abaixo correspondem às características mais importantes em um sistema de filas:

(21)

19

• Processo de chegada. Indica qual o padrão de chegada dos clientes no sistema. • Tempo de Serviço. Indica o tempo em que um cliente permance no sistema. • Número de Atendentes. Número de atendentes disponíveis para atender os

cli-entes.

• Capacidade do Sistema. Número máximo de clientes que o sistema suporta, incluindo os que estão em espera e os que estão sendo atendidos.

• População de Usuários. Número potencial de clientes que podem chegar a um sistema.

• Disciplina de Atendimento. Forma como os clientes saem da fila de espera para serem atendidos. Algumas disciplinas de atendimento são:

– FIFO. (First In, First Out): Primeiro a Entrar, Primeiro a Sair. – LIFO. (Last In, First Out): Último a Chegar, Primeiro a Sair.

– Fila de Prioridade. Uma prioridade é atribuída a cada cliente; clientes com maior prioridade têm preferência no atendimento.

De acordo com as características acima, um sistema de filas pode ser classificado através da notação de Kendal:

A/S/n/K/N/Q (2.1)

Em que A é o processo de chegada, S é o tempo de serviço, n é o número de atendentes, K é a capacidade do sistema, N é a população de usuários e Q é a disciplina de atendimento. Em muitos casos, as três últimas características são omitidas. Assim, assume-se capacidade ilimitada, população infinita e disciplina de atendimento FIFO. A notação então fica:

A/S/n (2.2)

Esses valores podem ser qualquer um dos seguintes:

• Markov. Densidade de probabilidade exponencial. • Determinístico. Todos os clientes tem o mesmo valor. • Geral. Qualquer distribuição de probabilidade arbitrária.

(22)

20

2.2.3

Teorema de Little

O teorema de Little diz que o número médio de clientes em um sistema estável L é igual a média de chegada dos clientes, λ, multiplicada pelo tempo médio que um cliente permanece no sistema, W. Ou expressado algebricamente:

L = λW (2.3)

Apesar de parecer intuitivamente razoável, este é um resultado importante, já que a relação não é influenciada pela distribuição do processo de chegada, pela distribuição do serviço, pela ordem do serviço, por praticamente nada. O resultado deste teorema se aplica para qualquer sistema(LITTLE, 2011).

2.3

Ruby on Rails e Sidekiq

Em 2005 um framework chamado Ruby on Rails foi desenvolvido. Criado por Da-vid Heinemeier Hansson durante seu trabalho na empresa de aplicações web 37signals1, para desenvolver uma ferramenta de gerenciamento de projeto chamada de Basecamp. Posteriormente a 37signals iria adotar o nome da sua ferramenta e se chamar Basecamp2

(GRIMMER, 2006). Rails inclui ferramentas que transformam tarefas comuns da rotina de desenvolvimento em simples comandos, por exemplo, scaffolding é um comando respon-sável por gerar toda a estrutura de um CRUD(MARTIN, 1983) incluindo testes automati-zados. Ele também inclui um simples servidor Ruby3 chamado WEBrick4 e um sistema

de build chamado Rake5. Uma outra característica marcante do Rails é a capacidade

de integração com outras soluções ou pacotes chamados Gems. O framework Rails ainda conta com o ambiente o RubyGems6, responsável por gerenciar as Gems e distribuí-las em um formato padrão facilitando a sua instalação e gerenciamento; e o Bundler responsável por gerenciar as dependências de sua aplicação dispondo as Gems do RubyGems em uma determinada versão com apenas uma linha de código.

Sidekiq é um framework em Ruby que torna possível a execução de código assincro-namente em segundo plano usando Threads. Processos a serem executados seguem uma

1https://37signals.com/ 2https://basecamp.com/ 3https://www.ruby-lang.org/pt/ 4http://ruby-doc.org/stdlib-2.0.0/libdoc/webrick/rdoc/WEBrick.html 5https://github.com/ruby/rake 6https://pt.wikipedia.org/wiki/RubyGems

(23)

21

implementação de conceitos da Teoria das Filas. Por exemplo, os processos são colocados em filas que podem ter prioridades entre si. Este tipo de framework é necessário para que as tarefas mais computacionalmente intensas sejam delegadas para processamento posterior, tirando esta carga dos processos que atendem as requisições de usuários do sistema.

Diversas filas podem ser criadas através do Sidekiq, cada fila pode ter uma prioridade associada a ela. Os processos em uma mesma fila são executados de acordo com a disciplina FIFO, porém os processos de filas com maior prioridade tem mais chances de serem escolhidos em detrimento daqueles em filas de menor prioridade.

(24)

22

3

Metodologia

Neste capítulo será abordado o contexto em que foi feito o estudo de caso. A Seção 3.1 traz detalhes sobre o sistema em questão e suas funcionalidades. A Seção 3.2 mostra qual foi a solução proposta para o problema. Na Seção 3.4 são apresentados detalhes das métricas utilizadas neste estudo. Finalmente, a Seção 3.3 traz as especificações de Hardware e Software dos experimentos realizados.

3.1

Sistema

O sistema e funcionalidade, objeto de estudo deste trabalho, serão descritos detalha-damente nas subseções 3.1.1 e 3.1.2, respectivamente. Em seguida será proposta uma nova abordagem para a implementação dessa funcionalidade. Por fim, na subseção 3.2.2, será apresentada uma otimização que pode ser utilizada para diminuir a variação no consumo de memória do sistema.

3.1.1

Walljobs

Criado em 2015, o Walljobs soluciona uma das principais demandas de quem ainda está estudando: ter acesso a vagas de emprego. Um estudante pode se cadastrar gratuitamente na plataforma para buscar oportunidades de trabalho de acordo com suas aptidões. Ao preencher seu perfil, o estudante pode inserir diversos detalhes, como: nível dos idiomas que domina, faculdade e curso que estudou, áreas de interesse, habilidades técnicas e até mesmo certificações que possui. Todas as informações são então utilizadas pelo sistema para mostrar ao usuário as oportunidades mais relevantes de acordo com seu perfil.

Empresas também podem ingressar no sistema a fim de promover as oportunidades de emprego que estão oferecendo. Em seu perfil, uma empresa pode inserir informações sobre sua localização, quantidade de funcionários, setor de atuação e uma breve descrição sobre suas atividades. A empresa pode publicar vagas apenas como divulgação, informando os

(25)

23

meios de contato para os possíveis interessados, ou pode publicar a vaga e gerenciar todo o processo seletivo diretamente pelo sistema.

Principal funcionalidade do sistema, as vagas são cadastradas pelas empresas. Detalhes sobre modalidade de contratação, local de trabalho, faixa de salário, auxílios oferecidos, e atividades a serem exercidas podem ser preenchidos para que o sistema possa fazer o cru-zamento dessas informações com as informações fornecidas pelos usuários. A localização da vaga é usada para traçar em um mapa a distância entre ela e os usuários interessados. O sistema possui números expressivos: são mais de 500.000 estudantes cadastrados, vindos de mais de 600 universidades diferentes. Cerca de 1.200 empresas já utilizaram o serviço Foi implementado utilizando a linguagem de programação Ruby, o framework para aplicações web RubyOnRails, e está hospedado no Heroku sob o plano standard-2x, que provê 1024MB de memória RAM. Com aproximadamente 55 requisições por minuto, o sistema consome em média 216MB da memória disponível.

3.1.2

Sugestão Personalizada de Vagas via Email

Uma das funcionalidades do sistema é o envio automático de emails semanais com sugestões de vagas personalizadas para seus usuários. Esta funcionalidade requer a análise de informações dos usuários e das vagas disponíveis, para assim determinar quais as oportunidades mais relevantes em cada caso.

Para descobrir as oportunidades relevantes é importante que sejam analisados rela-cionamentos de um usuário com outras entidades, como: universidades, cursos, idiomas, habilidades, áreas de interesse, entre outros. Desta forma, há a necessidade de se fazer diversas consultas ao banco para um único usuário, além de algum processamento sobre os dados retornados. Esta é uma tarefa de intenso esforço computacional para o servidor, que se agrava conforme o número de usuários do sistema aumenta.

O envio desses emails acontece de maneira sequencial, um usuário após o outro, ininterruptamente, até que todos os usuários tenham sido processados e tenham seus emails enviados. Para isso, o seguinte trecho de código é utilizado:

Código 3.1: Worker (Solução padrão)

c l a s s E m a i l S u g g e s t i o n W o r k e r i n c l u d e S i d e k i q :: W o r k e r

(26)

24

def p e r f o r m

User . w h e r e ( s u b s c r i b e d : true). each do | user | C o n t e n t M a i l e r . s u g g e s t _ t o ( user ). d e l i v e r _ n o w

end end end

No trecho de código acima, carrega-se toda a coleção de usuários do sistema para então iterá-la. Dentro do loop, o usuário atual é passado como parâmetro para o método suggest_to da classe ContentMailer, que irá retornar um email com conteúdo direcio-nado ao usuário em questão. Para finalizar, o método deliver_now é chamado para que o email seja enviado imediatamente.

O aumento de usuários afeta diretamente o custo da solução descrita, uma vez que não apenas serão carregados em memória todos os registros de uma única vez, como também haverá a instanciação de um novo objeto para cada usuário, impactando ainda mais a utilização de memória da operação. Além dos pontos citados, é importante notar que iterar sobre a coleção de usuários é uma atividade bloqueante para o servidor, ou seja, o processo encarregado de processar essa tarefa estará indisponível para a realização de outras atividades do sistema enquanto estiver processando a coleção de usuários. Desta forma, ao passo que a lista de usuários crescer, aumentará também o tempo bloqueado do processo.

Para ilustrar como isso pode afetar o desempenho geral do sistema, suponhamos que o servidor dispõe de apenas um processo para lidar com os envios de email. Nesta situação, durante o tempo em que a rotina de sugestão de emails esteja sendo processada, um usuário que esteja se cadastrando no sistema terá que esperar, no mínimo, até que a rotina seja concluída para receber sua confirmação de registro e poder ingressar na plataforma. Outrossim, diversas outras funcionalidades que dependam da disponibilidade do envio de email serão afetadas.

3.2

Solução Proposta

Nesta seção será apresentada uma nova abordagem para a funcionalidade discutida na seção anterior. Denominaremos a solução padrão descrita na listagem 3.1 de Solução 1. Na subseção 3.2.1 será apresentada uma abordagem utilizando múltiplos processos,

(27)

25

denominada de Solução 2. Finalmente, na seção 3.2.2 será apresentada uma modificação da Solução 2 relacionada com a coleta automática do lixo de memória, denominada de Solução 3.

3.2.1

Utilização de Múltiplos Processos

Para resolver o problema de escalabilidade na Sugestão Personalizada de Vagas via Email, ocasionado pelo modo de implementação comumente sugerido, foi utilizada uma abordagem que toma vantagem do paralelismo e assincronicidade disponíveis no servidor da plataforma.

Ao invés de ter apenas uma entidade que lida com o carregamento de todos os usuários e o envio de email para cada um deles, como é mostrado na listagem 3.1, essas resposabi-lidades foram divididas entre duas novas entidades.

O carregamento dos usuários não é mais feito antecipadamente, como foi mostrado na listagem 3.1. A primeira das novas entidades, descrita na listagem 3.2, busca apenas a lista de identificadores dos usuários, e para cada identificador será colocado um processo na fila do Sidekiq, que executará de forma assíncrona em segundo plano.

Código 3.2: Enqueuer (Solução proposta)

c l a s s C o n t e n t S u g g e s t i o n E n q u e u e r

def self. e n q u e u e

User . w h e r e ( s u b s c r i b e d : true). p l u c k (: id ). each do | u s e r _ i d | C o n t e n t S u g g e s t i o n W o r k e r . p e r f o r m _ a s y n c ( u s e r _ i d )

end end end

A segunda nova entidade, descrita na listagem 3.3, recebe um identificador de usuário como parâmetro e o busca no Banco de Dados. Em posse do usuário, o email com con-teúdo personalizado é gerado e então enviado imediatamente, terminando a execução do processo.

Código 3.3: Worker (Solução proposta)

c l a s s C o n t e n t S u g g e s t i o n W o r k e r i n c l u d e S i d e k i q :: W o r k e r

(28)

26

def p e r f o r m ( u s e r _ i d )

user = User . find ( u s e r _ i d )

C o n t e n t M a i l e r . s u g g e s t _ t o ( user ). d e l i v e r _ n o w

end end

Nesta abordagem é desnecessário realizar o carregamento antecipado de todos os usuá-rios para a memória, carregando-se apenas o conjunto de identificadores dos mesmos, di-minuindo assim o consumo de recursos. Além disso, a Sugestão Personalizada de Vagas via Email torna-se uma tarefa paralelizável, uma vez que cada usuário pode ser processado de forma totalmente independete.

3.2.2

Coletor de lixo de memória

A coleta de lixo de memória tem um papel importante no comportamento do consumo de memória da aplicação. A abordagem proposta toma vantagem disto, tendo em vista que os objetos instanciados não são mais referenciados após o término da execução do processo que os criou. Uma vez que cada processo é executado rapidamente, pois lida com apenas um usuário, seus objetos são, da mesma forma, rapidamente liberados.

Com isto em mente, uma modificação da abordagem anterior foi proposta. Esta ter-ceira abordagem adiciona apenas mais um passo à abordagem anterior, forçando a coleta do lixo de memória ao fim de cada processo. Esta modificação está descrita na listagem 3.4.

Código 3.4: Worker (Solução proposta com coleta do lixo de memória forçada)

c l a s s C o n t e n t S u g g e s t i o n W o r k e r i n c l u d e S i d e k i q :: W o r k e r

def p e r f o r m ( u s e r _ i d )

user = User . find ( u s e r _ i d )

C o n t e n t M a i l e r . s u g g e s t _ t o ( user ). d e l i v e r _ n o w GC . s t a r t

end end

(29)

27

coletor do lixo de memória estará liberando espaço com mais freqência, evitando assim o acúmulo de memória não utilizada. Entretanto, existe uma relação de compromisso ao adotar este recurso, uma vez que a execução deste aumentará o tempo de execução de cada processo, consequentemente afetando a performance geral da solução.

3.3

Configuração dos Experimentos

A subseção 3.3.1 traz informações detalhadas sobre o hardware utilizado para execução dos experimentos, enquanto a subseção 3.3.2 especifica as versões dos softwares necessários para a realização destes.

3.3.1

Hardware

Os experimentos foram realizados em um computador da marca Apple, modelo Mac-Book Pro (Retina, 13 polegadas, Mid 2014), equipado com 8GB de memória RAM do tipo DDR3L SDRAM operando na frequência de 1600MHz. A máquina utiliza o OSX El Capitan em sua versão 10.11.6 (15G31) como sistema operacional.

Este modelo é alimentado por um processador de 22 nm, 64-bit da “Quarta Geração” Intel Mobile Core i5 Haswell (I5-4278U), que inclui dois núcleos independentes em um único chip. Cada núcleo tem um cache dedicado de 256kB de nível 2, compartilha 3 MB de cache de nível 3 e possui um controlador de memória integrado (canal duplo).

Este sistema também suporta o Turbo Boost 2.0 - que aumenta automaticamente a velocidade dos núcleos ativos para melhorar o desempenho quando necessário (até 3.1 GHz para este modelo) - e Hyper Threading - que permite ao sistema reconhecer quatro núcleos ou threads totais (dois reais e dois virtuais).

3.3.2

Software

Para a execução dos experimentos foi utilizada a linguagem de programação Ruby na versão 2.2.2p95. Ruby é uma linguagem interpretada multiparadigma, de tipagem dinâmica e forte, com gerenciamento de memória automático, desenvolvida no Japão em 1995, por Yukihiro Matsumoto. Também foi utilizado o framework web Rails em sua versão 4.2.3, que suporta o desenvolvimento de sistemas com base no padrão de arquitetura MVC (Model-View-Controller). Rails foi criado por David Heinemeier Hansson e tornou-se público no ano de 2003.

(30)

28

Foi utilizado PostgreSQL em sua versão 9.4.2, um sistema gerenciador de banco de dados objeto relacional (SGBDOR), que é desenvolvido como projeto de código aberto. Para gerenciamento de filas e processamento em segundo plano em Ruby, foi utilizado Sidekiq na versão 4.1.2, criado por Mike Perham, também de código aberto. Finalmente, para o armazenamento das filas foi utilizado Redis, um banco de dados de chave-valor em memória, na versão 3.0.1 (build bf58331b4c8133f5).

3.4

Métricas

As métricas utilizadas foram o consumo máximo de memória RAM e o quantidade de memória utilizada durante cada instante dos experimentos. O consumo máximo de me-mória é importante para estabelecermos um valor mínimo necessário para que a execução da solução seja possível. Já a análise do consumo de memória ao longo do experimento é necessária para entender as nuances de cada solução.

Para coletar as métricas, utilizou-se um script de linha de comando que faz uso de programas comumente disponíveis em distribuições de sistemas UNIX como: echo, ps, grep e awk. Este script está descrito na listagem 3.5

Código 3.5: Script de linha de comando

echo $ (( ‘ ps aux | grep - v ’ grep ’ | grep ’ sidekiq ’ | head - n 1 | awk ’{ p r i n t $6 } ’ ‘/1024))

(31)

29

4

Resultados e Discussão

Este capítulo apresenta os resultados dos experimentos, na Seção 4.1. Posteriormente, na Seção 4.2, os resultados obtidos serão discutidos a fim de esclarecer as vantagens apresentadas pela solução proposta.

4.1

Resultados

A Figura 1 mostra o consumo de memória em MB pela quantidade de usuários pro-cessados. Foram feitos experimentos com grupos de 100, 500 e 2000 usuários para cada solução. A Solução 1 está apresentada em tons de vermelho, enquanto a Solução 2 está em tons de azul e a Solução 3 está em tons de verde. A tonalidade das cores indica a quan-tidade de usuários do experimento, tendo as tonalidades mais escuras um maior número de usuários.

A Figura 2 foi gerada a partir da média dos valores máximos de consumo de memória em cada execução dos experimentos. Assim, a barra vermelha indica a média dos valores máximos encontrados nos experimentos feitos utilizando a Solução 1. Da mesma forma, as barras azul e verde representam, respectivamente, as Soluções 2 e 3. Ainda existe uma linha vertical preta no topo de cada barra, denotando a variância dos valores máximos de cada execução.

(32)

30

Figura 1: Gráfico do consumo de memória pela quantidade de usuários processados. Linhas em tons de vermelho representam a Solução 1, linhas em tons de azul representam a Solução 2, linhas em tons de verde representam a Solução 3.

Figura 2: Gráfico de barra com a média e variância dos valores máximos de cada solução. Barra vermelha representa a solução 1, barra azul representa a solução 2, barra verde representa a solução 3.

(33)

31

A Tabela 1 mostra o desvio padrão do consumo de memória durante a execução. Para gerar a tabela, foram utilizados quatro experimentos variando a quantidade total de usuários: 100, 500, 1000 e 2000. Como podemos observar, a Solução 3 apresenta o menor desvio padrão em todos os experimentos. Além disso, na Solução 3 o desvio padrão tem valores similares independentemente da quantidade de usuários, ao contrário das Soluções 1 e 2. A Solução 1 apresenta um maior desvio padrão a medida que a quantidade de usuários aumenta, enquanto a Solução 2 apresenta comportamento oposto.

Tabela 1: Desvio padrão do consumo de memória

100 usuários 500 usuários 1000 usuários 2000 usuários Solução 1 16.67 32.06 32.61 53.4

Solução 2 15.87 11.93 10.74 7.13 Solução 3 4.65 5.94 3.86 5.3

4.2

Discussão

Os resultados da bateria de testes realizada com a Soluçao 1 mostram uma relação di-retamente proporcional entre o tamanho do conjunto de usuários e o consumo de memória, comportamento que pode ser observado na Figura 1. Ou seja, quanto maior a quantidade de usuários, mais memória é utilizada pelo sistema. Este comportamento está de acordo com o esperado, uma vez que nesta solução é necessário carregar todos os objetos na memória para então processá-los.

A Solução 1 tem alguns aspectos interessantes. Podemos observar que existem saltos repentinos no consumo de memória (como o que acontece nos pontos de 100 e 280 usuários processados - Figura 1). Provavelmente isso ocorre devido a alocação antecipada de mais memória que o necessário naquele momento, visando diminuir o número de vezes em que o interpretador da linguagem precisa requisitar mais memória para o sistema operacional, visto que essa é um operação custosa. Em contrapartida por volta dos 1500 usuários processados, há uma queda no consumo de memória. Isso ocorre devido ao fato do coletor automático de lixo de memória observar que a memória previamente alocada não foi completamente utilizada.

De acordo com as definições de escalabilidade de espaco e de escalabilidade de carga, apresentadas no Capítulo 2, a Solução 1 não pode ser considerada escalável, pois apresenta um consumo de recursos crescente conforme o número de usuários a serem processados

(34)

32

pelo sistema aumenta.

Os resultados apresentados pela Solução 2, como visto na Figura 1, mostram um com-portamento constante com relação ao consumo de memória. A maior vantagem observada nesta solução, em comparação a Solução 1, está no fato de não haver relação entre o ta-manho do conjunto de usuários e a quantidade de memória utilizada. Isso ocorre durante a execução de um mesmo procecimento (em que o uso de memória tem poucas variações -Figura 1) e entre diferentes execuções onde variamos a quantidade de usuários. Isso pode ser verificado através da pequena variância apresentada pela Solução 2 na Figura 2.

A Solução 3, por ser similar a Solução 2, apresenta características semelhantes. Porém, a maior frequência da coleta do lixo de memória, faz com que esta solução evidencie os benefícios obtidos através da Solução 2. Por exemplo, na Figura 1 é possível observar que a solução 3 não apresenta picos no consumo de memória, fato que é apoiado pela baixa variância do consumo máximo de memória apresentada na Figura 2.

Finalmente, a Solução 3 é a mais estável, pois independentemente da quantidade total de usuários no experimento não apresenta oscilações no consumo de memória durante sua execução, como demonstrado pelo baixo desvio padrão apresentado na Tabela 1.

As características da Solução 2 e 3 estão de acordo com a definição de escalabilidade de espaço e escalabilidade de carga apresentadas no Capítulo 2, não apresentado con-sumo excessivo de memória e recursos computacionais mesmo havendo um aumento na quantidade de objetos a serem suportados.

(35)

33

5

Conclusão

Neste trabalho foi apresentada uma solução para o problema de escalabilidade en-frentado no sistema Walljobs. As soluções foram testadas variando a quantidade total de usuários. Nos testes realizados, a solução proposta chegou a consumir um máximo 230MB de memória RAM, enquanto a solução padrão atingiu um máximo de 459MB. Além disso, a solução proposta tem consumo de memória constante com relação a quantidade total de usuários, ao passo que na solução padrão o consumo de memória é função monotoni-camente crescente da quantidade total de usuários. Assim, este estudo de caso demonstra claramente a superioridade da solução proposta com relação ao consumo de recursos e, consequentemente, escalabilidade do sistema.

Uma extensão natural deste estudo consiste em realizar uma análise detalhada da performance das soluções em servidores com múltiplos processadores. Tendo em vista que as solução proposta é naturalmente paralelizável, sua utilização também deve reduzir significativamente o tempo de execução.

(36)

34

Referências

BONDI, A. B. Characteristics of scalability and their impact on performance. In: Proceedings of the 2Nd International Workshop on Software and Performance. New York, NY, USA: ACM, 2000. (WOSP ’00), p. 195–203. ISBN 1-58113-195-X. Disponível em: <http://doi.acm.org/10.1145/350391.350432>.

CHIEU, T. C. et al. Dynamic scaling of web applications in a virtualized cloud computing environment. In: 2009 IEEE International Conference on e-Business Engineering, ICEBE 2009, Macau, China, 21-23 October 2009. [s.n.], 2009. p. 281–286. Disponível em: <http://dx.doi.org/10.1109/ICEBE.2009.45>.

GRIMMER, L. Interview with David Heinemeier Hansson from Ruby on Rails. 2006. Online; accessed 05/29/2017. Disponível em: <https://web.archive.org/ web/20130225091835/http://dev.mysql.com/tech-resources/interviews/ david-heinemeier-hansson-rails.html>.

HANSSON, D. H. Ruby on Rails. 2008. http://rubyonrails.org/. Accessed: 2017-05-29.

LITTLE, J. D. C. Or forum—little’s law as viewed on its 50th anniversary. Oper. Res., INFORMS, Institute for Operations Research and the Management Sciences (INFORMS), Linthicum, Maryland, USA, v. 59, n. 3, p. 536–549, maio 2011. ISSN 0030-364X. Disponível em: <http://dx.doi.org/10.1287/opre.1110.0940>.

MARTIN, J. Managing the Data Base Environment. Pearson Education, Li-mited, 1983. (A James Martin book). ISBN 9780135505823. Disponível em: <https://books.google.com.br/books?id=ymy4AAAAIAAJ>.

Referências

Documentos relacionados

As principais constatações identificam ações e práticas no âmbito da gestão logística e da cadeia do abastecimento que contribuem para o equilíbrio entre os

Com base nos resultados da pesquisa referente à questão sobre a internacionalização de processos de negócios habilitados pela TI com o apoio do BPM para a geração de ganhos para

Este trabalho traz uma contribuição conceitual sobre a utilização do sistema de gestão de produtividade que poderá motivar futuras pesquisas sobre o tema, bem

Figura A53 - Produção e consumo de resinas termoplásticas 2000 - 2009 Fonte: Perfil da Indústria de Transformação de Material Plástico - Edição de 2009.. A Figura A54 exibe

2 – Fundamentando a sua pretensão, os embargantes começam por dizer que dão por reproduzida a matéria alegada pelo também fiador, seu filho, C…. Dizem que a

4634 MANUEL ALEXANDRE LOPES GOMES FERREIRA 7099 MANUEL AUGUSTO DE SANCHO FONTES RODRIGUES 2290 MANUEL LUDGERO SOUSA LOUREIRO HORTA E MELO 4114 MANUEL MARIA PINA DA SILVA GARCIA

Considera-se COMISSÃO DE COMPRA a comissão paga pelo comprador do(s) animal(is) produto(s) à EMPRESA LEILOEIRA / LEILOEIROS / OUTROS, que o valor será anunciado

Análise temporal e comparação de gravações do excerto orquestral para flauta do Prélude à l´après-midi d´un faune. de Claude Debussy