Big Data

MapReduce sem Hadoop usando o Pipeline do ASP.NET

Doug Duerner
Yeon-Chang Wang

Baixar o código de exemplo(VB)

Você nunca desejou adicionar o poder do MapReduce em Big Data (Grandes Volumes de Dados) aos aplicativos do seu smartphone ou análises de dados avançadas em seu tablet ou outro dispositivo pequeno, mas pensou que seria muito difícil?

Você já quis transformar o seu aplicativo de nó único em um sistema distribuído com rapidez e facilidade, sem a necessidade de reformular todo o aplicativo?

Essas perguntas foram as que nos fizeram embarcar em uma aventura de criar um componente MapReduce Restful extremamente fácil de configurar e usar.

Produtos como o Hadoop se destacam nos desafios do Big Data. Criamos uma solução que sacrifica algumas dessas funcionalidades para simplicidade e agilidade para facilitar o desenvolvimento de aplicativos para Big Data. Dessa forma, você não precisa ser um especialista para obter um sistema funcionando e em execução em um curto período de tempo. A simplicidade da malha em comparação com a complexidade da configuração de Hadoop e a agilidade da nossa solução contra “a enormidade” de um cluster Hadoop a tornam uma proposta atraente.

Em resumo, criamos uma infraestrutura muito simples que pode usar o MapReduce para fazer o processamento de computação intensivo em nós de “malha” ou, alternativamente, fazer a coleta de dados fora desses nós, com os resultados sendo correlacionados e agregados em um resultado final que é retornado ao cliente.

Segundo plano

O servidor Web do IIS (com o pipeline do ASP.NET) provou ser um servidor de Web de nível corporativo altamente escalonável. Mas essas tecnologias não estão limitadas a simplesmente servir as páginas da Web e sites da Web de hospedagem. Realmente, não há nenhuma razão técnica, que você não pode usá-las como um mecanismo de pipeline de finalidade geral acessado via HTTP. As etapas do pipeline do ASP.NET são executadas na sequência (não mover para a próxima etapa até que tenha concluído a etapa anterior), mas cada etapa pode ser executada de forma assíncrona em paralelo. O servidor Web do IIS pode ser configurado para executar vários pipelines ASP.NET (várias w3wp.exes) preparando solicitações HTTP.

Usando o pipeline do ASP.NET como um pipeline de finalidade geral (que por acaso sejam acessados por meio de HTTP), no lugar de servir as páginas da Web e hospedagem de sites, pode parecer um pouco incomum, mas um pipeline do ASP.NET (com etapas de pipeline assíncrono) é, na verdade, bastante semelhante ao pipelining de instruções de CPU em microprocessadores (bit.ly/1DifFvO), e a capacidade de ter vários arquivos de w3wp.exe (com um pipeline do ASP.NET em cada w3wp.exe) é muito similar ao design superescalar em microprocessadores (bit.ly/1zMr6KD). Essas semelhanças, juntamente com a escalabilidade comprovada, é o que está fazendo do uso do Servidor Web do IIS e do pipeline do ASP.NET uma solução para tudo que precisa da funcionalidade do pipeline, uma proposta atraente.

Há muitos produtos que já fazem MapReduce RESTful (Hadoop, Infinispan, Riak, CouchDB, MongoDB e muito mais), mas nossa pesquisa sugere que eles são difíceis de configurar ou exigem conhecimento especializado.

Gostaríamos de simplesmente usar nossos servidores IIS do Windows existentes que já estão em funcionamento; usar nossos métodos de API que já foram gravados; obter os dados para nossas telas da interface do usuário por demanda e ter todo o sistema MapReduce distribuído instalado e em funcionamento em minutos (tudo com conhecimento limitado de sistemas distribuídos ou design e arquitetura de sistema do MapReduce). Dessa forma, você pode rápida e facilmente transformar um aplicativo de pequena escala existente em um sistema distribuído maior com um mínimo de esforço ou conhecimento, em seus próprios servidores ou na nuvem. Ou, se você quisesse adicionar análises de dados avançadas para seu aplicativo de smartphone existente, você poderia fazer isso com um mínimo de esforço.

O componente MapReduce RESTful é um adicional que não exige que o aplicativo existente seja reescrito e é um dos principais candidatos quando seu objetivo é simplesmente adicionar funcionalidade básica distribuída a um aplicativo existente que já tem um extensa API de dados públicos. É possível rápida e facilmente emular padrões de computação distribuída como “dispersão e coleta”, conforme mostrado na Figura 1.

Emular padrões de computação distribuída como “Dispersão e coleta”
Figura 1 Emular padrões de computação distribuída como “Dispersão e coleta”

O projeto de exemplo que acompanha este artigo fornece o ponto de partida de uma infraestrutura básica simples que demonstra essa filosofia de design e pode ser expandido no futuro. O fator de atração desse design não é que é melhor do que outros produtos, mas que é mais fácil. É simplesmente uma alternativa de design fácil de usar para os grandes sistemas MapReduce corporativos em uso atualmente. O design não é uma substituição para os produtos desenvolvidos da empresa MapReduce como Hadoop e não está indicando que é muito menor para conter toda a funcionalidade dos principais produtos.

MapReduce

Em termos simples, o MapReduce é uma maneira de agregar grandes repositórios de dados. A etapa de Mapeamento é executada em vários nós de servidores de processamento distribuído. Normalmente, ela executa uma tarefa em cada nó de servidor distribuído para recuperar os dados dos nós de dados e pode, opcionalmente, transformar ou pré-processar os dados enquanto ela ainda está no nó de servidor distribuído. A etapa de Redução é executada em um ou mais nós de servidor de processamento final e consolida todos os resultados das etapas de Mapeamento em um conjunto de resultados final usando muitos algoritmos de combinação diferentes.

No contexto de uma API de objeto comercial, a etapa de Mapeamento executa um método de API de objeto comercial para obter dados e a etapa de Redução combina todos os conjuntos de resultados da etapa de Mapeamento em um único conjunto de resultados final (fazendo uma união por chave primária ou uma agregação como uma soma por grupo, por exemplo) que é retornado ao cliente que fez a solicitação.

Um dos principais benefícios do MapReduce é permitir o “expansão horizontal” em vez da “expansão vertical”. Em outras palavras, você simplesmente continua adicionando mais nós de servidor normais em vez de comprar hardware melhor para o único nó do servidor principal para dimensionar. A expansão horizontal geralmente é a escolha mais barata e mais flexível porque ela usa hardware comum, enquanto a expansão vertical é normalmente muito mais cara porque o custo do hardware tende a aumentar exponencialmente à medida que se torna mais sofisticado.

Como uma observação interessante, o MapReduce se destaca quando se trata de volumes de dados extremamente grandes (escala de Internet) e os dados são parcialmente estruturados ou não estruturados, como arquivos de log e blobs binários. Por outro lado, bancos de dados relacionais do SQL se destacam quando você tiver dados estruturados normalizados com esquemas, pelo menos até um determinado limite quando a sobrecarga do banco de dados relacional é incapaz de lidar com a grande quantidade de dados.

Figura 2 mostra uma visão geral do processo do MapReduce e compara uma consulta simples de banco de dados relacional do SQL com a consulta correspondente em um processo do MapReduce em Big Data.

Consulta simples de banco de dados relacional SQL em comparação com a mesma consulta com MapReduce
Figura 2 Consulta simples de banco de dados relacional SQL em comparação com a mesma consulta com MapReduce

REST

A Representational State Transfer (Transferência de Estado Representacional - REST) define uma API pública em execução em HTTP que usa um paradigma para criar, ler, atualizar, excluir (CRUD), respectivamente baseado nos verbos HTTP Post, Get, Put e Delete, para retornar uma representação de um objeto do servidor para o cliente que fez a solicitação. O REST busca permitir acesso público ao próprio objeto como uma entidade, não apenas operações funcionais no objeto. Ele não é uma especificação ou uma RFC; é simplesmente uma recomendação de design. Você pode seguir à risca o design REST puro e exigir que a URL seja formatada para tratar o objeto como uma entidade, como esta:

http://server/MapReducePortal/BookSales/Book A

Ou você pode optar por um design mais no estilo RPC e exigir que a URL seja formatada com os nomes de classe e método para executar, da seguinte maneira:

http://server/MapReducePortal/BookSales/GetTotalBookSalesByBookName/Book A
http://server/MapReducePortal/BookSales/GetTotalBookSalesByBookName?bookName=Book A

MapReduce RESTful

O MapReduce RESTful significa fazer operações MapReduce por meio de HTTP para a API e o mecanismo de transporte entre os nós de servidor distribuído.

O REST sobre HTTP para o transporte e a API tem várias vantagens que o tornam atraente:

  • O protocolo HTTP pela porta 80 é compatível com firewall.
  • Os aplicativos cliente de praticamente qualquer plataforma facilmente podem consumir recursos sem a necessidade de dependências de plataforma específica.
  • Os verbos HTTP (Get, Post, Put, Delete) são um paradigma simples e elegante para solicitar recursos.
  • A compactação gzip pode ajudar a reduzir os tamanhos de carga.
  • O próprio protocolo HTTP tem vantagens adicionais, como armazenamento em cache interno.

Atualmente, o projeto de exemplo usa o REST sobre HTTP para a API e transporte e suporta apenas Get e Post, nenhuma operação de gravação e comunica-se totalmente em JSON. Ele é semelhante a algumas das metodologias conhecidas para acessar o Hadoop Distributed File System (HDFS) externamente (como Hadoop YARN e Hadoop WebHDFS), mas dá apenas o suporte minimamente necessário para operar o sistema. Não tentamos substituir o Hadoop ou corresponder a toda sua ampla funcionalidade. Estamos apenas tentando fornecer uma alternativa muito rudimentar, fácil de usar, às custas da funcionalidade.

Configuração do MapReduce

Para o projeto de exemplo, basta copiar o MapReduceModule.dll no diretório \bin do diretório virtual em cada nó do servidor de IIS para usar como um nó de servidor distribuído em seu sistema Map-Reduce e, em seguida, colocar uma entrada na seção de módulos do web.config, desta forma:

<modules>
  <add name="MapReduceModule" type="MapReduce.MapReduceModule" />
</modules>

Você terminou. Simples assim.

Se não houver nenhum diretório virtual no nó do servidor de IIS, crie um novo diretório virtual com um diretório \bin, faça dele um aplicativo e verifique se está usando um Pool de aplicativos do Microsoft .NET Framework 4. Aumente a contagem de processo de trabalho w3wp.exe no Pool de aplicativos que atendem o diretório virtual MapReducePortal para fornecer mais pipelines de processamento para as solicitações do MapReduce. As outras opções de configuração avançadas usadas para ajustar o servidor de IIS geralmente já foram definidas pelo departamento de TI que gerencia o servidor e estão além do escopo deste artigo, mas se esse não for o caso, elas estão disponíveis para consulta imediata no site da Microsoft.

Configuração do REST

Para o projeto de exemplo, coloque o PathInfoAttribute em qualquer um dos seus métodos de API de dados de objeto comercial existentes e especifique a cadeia de caracteres do PathInfo que será usada para mapear a URL para o método e argumentos do método. É só isso.

Um dos recursos interessantes do código de exemplo é que quaisquer tipos de dados que os métodos da API de dados de objeto comercial existente esteja retornando atualmente poderão permanecer os mesmos e não precisam ser alterados. A infraestrutura pode manipular praticamente qualquer tipo automaticamente porque ela usa um DynamicObject .NET para representar dinamicamente os tipos de dados retornados. Por exemplo, se o método existente retorna uma coleção de objetos Customer, o DynamicObject representa um tipo de dados Customer.

A cadeia de caracteres PathInfo do PathInfoAttribute usa a mesma classe de UriTemplate .NET que o Windows Communication Foundation (WCF) usa e permite que você faça as mesmas coisas sofisticadas que você pode fazer em um projeto do WCF Web HTTP REST ou um projeto ASP.NET Web API 2, como substituição de nome de variável de argumento, curingas e assim por diante. Você seleciona qual URL é mapeada para quais métodos. Você tem controle total e está livre para implementar sua API REST como desejar. Você pode ficar mais próximo para uma API REST pura e fazer com que os segmentos de URL representem os objetos como entidades de primeira classe:

http://server/MapReducePortal/BookSales/Book A
[PathInfoAttribute(PathInfo="/BookSales/{bookName}", ReturnItemType="Book")]
public BookSales GetTotalBookSalesByBookName(string bookName)
{
}

Ou, se preferir, você pode seguir livremente o REST e fazer com que os segmentos de URL especifiquem o nome da classe e o nome do método que você deseja executar em segmentos da URL:

http://server/MapReducePortal/BookSales/GetTotalBookSalesByBookName/Book A
[PathInfoAttribute(PathInfo="/BookSales/GetTotalBookSalesByBookName/{bookName}",
  ReturnItemType="Book")]
public BookSales GetTotalBookSalesByBookName(string bookName)
{
}

Você decide.

Fatores atraentes

Um dos fatores atraentes do design do projeto de exemplo é a escalabilidade obtida usando o pipeline do ASP.NET como um pipeline MapReduce para executar o processo MapReduce. Como o pipeline do ASP.NET opera em sequência, é adequado para executar as etapas de Mapeamento e de Redução. E o que é interessante é que, embora o pipeline seja sequencial e não será movido para a próxima etapa até que a etapa anterior tenha sido concluída, cada etapa pode ainda ser executada assincronamente. Isso permite ao pipeline continue a receber e processar novas solicitações de MapReduce mesmo quando o pipeline estiver bloqueado aguardando o retorno das chamadas de Mapeamento a partir de outros nós de servidor distribuído.

Como a Figura 3 mostra, cada w3wp.exe hospeda um pipeline do ASP.NET atuando como um pipeline de MapReduce. O w3wp.exe (processo de trabalho do IIS) é gerenciado pelo pool de aplicativos atribuído ao diretório virtual do Map­ReducePortal. Por padrão, o pool de aplicativos tem um w3wp.exe processando novas solicitações de entrada para o diretório virtual, mas pode facilmente ser configurado para ter quantos w3wp.exes você quiser. Isso permite ter vários pipelines de MapReduce em um único nó de servidor autônomo, todos trabalhando em grupo para processar solicitações de entrada MapReduce para o diretório virtual do MapReducePortal. A natureza assíncrona do pipeline do ASP.NET individual permite que várias solicitações sejam processadas em paralelo. A capacidade de ter vários w3wp.exes facilitando que vários pipelines ASP.NET levem você até o próximo nível.

Aumentar a contagem de processo de trabalho do IIS para o Pool de aplicativos ter mais Pipelines MapReduce atendendo às solicitações de MapReduce enviadas para o diretório Virtual MapReducePortal deste servidor do IIS
Figura 3 Aumentar a contagem de processo de trabalho do IIS para o Pool de aplicativos ter mais Pipelines MapReduce atendendo às solicitações de MapReduce enviadas para o diretório Virtual MapReducePortal deste servidor do IIS

O design do projeto de exemplo também permite que você continue adicionando quantos servidores do IIS desejar para formar uma "malha" cada vez maior de nós de servidor, conforme mostrado na Figura 4. Quanto mais a malha cresce, potencialmente maior o problema que pode ser tratado dividindo-o em partes cada vez menores para ser resolvido e o maior nível de paralelismo que potencialmente pode ser obtido. O pipeline do ASP.NET assíncrono, combinado com vários canais por servidor, permite o paralelismo em núcleos de CPU de um único servidor. A malha de servidores fornece outro nível de paralelismo em várias máquinas de servidor. É fácil adicionar mais servidores do IIS para a malha; tudo o que você precisa fazer é copiar o MapReduceModule.dll para a pasta \bin sob o diretório virtual e adicionar uma entrada ao arquivo web.config. Como os servidores IIS são todos simplesmente servidores autônomos, nenhuma configuração adicional é necessária. Produtos como o Hadoop, por outro lado, geralmente exigem mais esforço, planejamento e conhecimento porque os servidores normalmente devem ser configurados como um "cluster" real de servidores.

Qualquer nó de servidor pode iniciar a solicitação de MapReduce e qualquer número de outros nós de servidor distribuído listados na URL do AJAX pode executar as partes da etapa de Mapeamento dessa solicitação em paralelo
Figura 4 Qualquer nó de servidor pode iniciar a solicitação de MapReduce e qualquer número de outros nós de servidor distribuído listados na URL do AJAX pode executar as partes da etapa de Mapeamento dessa solicitação em paralelo

Não é necessário criar especialmente servidores do IIS. Você pode simplesmente usar quaisquer servidores do IIS disponíveis simplesmente copiando o MapReduceModule.dll em qualquer diretório virtual que já está no servidor. Isso é tudo. A próxima chamada AJAX agora pode incluir o novo servidor do IIS no parâmetro de lista distributednodes na sequência de consulta do URL.

Outro benefício do design de malha de servidor é que ele não se baseia em um Nó mestre para a função. Em produtos como o Hadoop, o nó mestre gerencia o cluster de servidor e o local dos dados através desse cluster do servidor. E é o nó mestre que tem sido origem da falha quando Hadoop foi dimensionado para seu limite em produção, em vez da quantidade de dados ou a infraestrutura.

Nesse design de malha de servidor, não há nenhum nó mestre. Qualquer nó de servidor pode iniciar a solicitação de MapReduce e os dados residem no nó que o coleta. Como a Figura 4 mostra, qualquer nó do servidor pode ser o solicitante de dados e o provedor de dados ao mesmo tempo em paralelo. Um servidor pode solicitar dados de outros nós na malha de servidor que estão executando a função de Mapeamento e podem receber os resultados, combinando-os em um conjunto final de resultados na etapa de Redução. Ao mesmo tempo, esse mesmo nó do servidor também pode estar atuando como um nó de servidor de borda manipulando uma etapa de Mapeamento, retornando os resultados parciais para uma solicitação de MapReduce originada em outro nó do servidor e que vai ser reduzida novamente nesse nó.

Atualmente, os clientes que estão fazendo as solicitações identificam o local dos dados (por meio da lista distributednodes na sequência de consulta da URL). Em vez disso, você poderia modificar o design para armazenar esta lista (ou apenas deste nó mais próximo dos nós vizinhos ou dos nós que hospedam dados divididos em vários nós) em uma tabela de banco de dados em cada nó individual e adicioná-los por meio de programação para a URL em tempo de execução. De certa forma, isso seria ativar a noção de Nó mestre único em um conceito de Nó mestre distribuído, onde cada nó sabe onde obter seus dados. Seria como se o Nó mestre fosse espalhado pela malha, permitindo que ele seja dimensionado com a malha.

Como esse design de malha usa uma variedade de produtos da Microsoft testadas e aprovadas — bancos de dados do Windows Server, o servidor Web do IIS e o SQL Server, você obtém os recursos redundância e tolerância a falhas poderosos (como Network Load Balancing [NLB] no Windows Server para o IIS, grupos de disponibilidade do AlwaysOn com o reparo automático de página ou espelhamento com reparo automático de página para o SQL Server) que já estão incorporados nesses produtos comerciais. Detalhes para esses recursos estão disponíveis nos sites da Microsoft.

O design do projeto de exemplo também permite que várias solicitações MapReduce sejam "encadeadas" juntas para formar fluxos de trabalho onde a entrada inicial para uma solicitação de MapReduce é o resultado da solicitação MapReduce anterior. Isso é feito alterando a solicitação MapReduce a um Post em vez de um Get e incluindo os resultados da solicitação MapReduce anteriores no corpo da solicitação Post. Figura 5 mostra um exemplo da saída resultante na página de teste.

Exibindo a saída resultante de encadeamento em uma página de teste
Figura 5 Exibindo a saída resultante de encadeamento em uma página de teste

Visão geral do projeto de exemplo

Em essência, o MapReduceModule.dll transforma o pipeline do ASP.NET em um pipeline de MapReduce. Ele usa um HttpModule para implementar a funcionalidade de Mapeamento e Redução. Como uma observação interessante, algumas das operações de combinação (como união) que são executadas durante a etapa de Redução contam com uma interface IEqualityComparer<T>, onde T é um DynamicObject que, de certa forma, permite fazer comparações de igualdade com base em um nome de propriedade como um valor de cadeia de caracteres em tempo de execução, embora IEqualityComparer<T> requer que um tipo concreto seja definido em tempo de compilação. Muito legal.

A Figura 6 é uma visão geral do design do MapReduce­Module.dll, mostrando o fluxo de processamento conforme ele passa através do MapReduceModule.dll. O MapReduceModule é a única dll necessária e ela precisa estar em cada nó do servidor que você deseja que participe da infraestrutura de MapReduce. A adição do MapReduce­Module.dll ao servidor é muito fácil e é obtida simplesmente copiando o MapReduceModule.dll para a pasta \bin sob o diretório virtual e adicionando uma entrada ao arquivo web.config.

Visão geral do design de alto nível do fluxo de processamento para MapReduceModule.dll
Figura 6 Visão geral do design de alto nível do fluxo de processamento para MapReduceModule.dll

Na Figura 6, o IHttpModule usa a primeira etapa no pipeline do ASP.NET para a funcionalidade MAP inscrevendo ao evento AddOnBeginRequestProcessingAsync que é acionado durante a etapa Iniciar processamento de solicitações no pipeline do ASP.NET. O IHttpModule usa a última etapa no pipeline do ASP.NET para a funcionalidade REDUCE inscrevendo ao evento AddOnEndRequestProcessingAsync que é acionado durante a etapa de Terminar processamento de solicitação no pipeline do ASP.NET.

Em resumo, você inscrever somente os eventos Iniciar processamento de solicitação e Terminar processamento de solicitação no pipeline do ASP.NET. Elas são executadas em sequência e não se movem para a próxima etapa até que a etapa anterior seja concluída.

Durante a etapa Iniciar processamento da solicitação, o IHttpModule inicia todas as solicitações MAP consultando o nó local e enviando uma solicitação HTTP da Web para cada um dos nós de servidor distribuídos presentes no parâmetro de lista distributednodes na sequência de consulta da URL. A solicitação da Web HTTP enviada para cada um dos nós de servidor distribuídos usa a mesma URL que iniciou a solicitação, mas sem nenhum parâmetro distributednodes em sua URL.

Fora os nós de servidor distribuídos que recebem a solicitação MAP, as mesmas duas etapas de pipeline do ASP.NET são executadas sequencialmente, mas porque não há nenhum parâmetro distributednodes em sua URL, as etapas Iniciar processamento da solicitação e Terminar processamento de solicitação apenas consultam esse nó. O método de recuperação de dados do MAP especificado com o PathInfoAttribute é executado nesse nó de servidor distribuído de borda para obter os dados locais do nó. Os dados retornados no fluxo de resposta de cada nó de servidor distribuído de borda para o nó do servidor que iniciou a solicitação original, em seguida, são armazenados no HttpContext usando a URL como a chave para que ela pode ser recuperada posteriormente durante a etapa REDUCE final.

No nó do servidor local que iniciou a solicitação original, o método de recuperação de dados MAP especificado com o PathInfoAttribute é executado para obter os dados locais que estão no nó do servidor local que iniciou a solicitação original. Os dados do nó de servidor local, em seguida, são armazenados no HttpContext usando a URL como a chave para que ela possa ser recuperada na etapa REDUCE final.

Durante a etapa Terminar processamento de solicitação, o IHttpModule executará a etapa REDUCE examinando o HttpContext para todos os dados e os parâmetros REDUCE que foram fornecidos na sequência de consulta de URL (que pode consistir em opções predefinidas, como sum= e union=, sort=, ou opções de função personalizada como reduce=CustomReduceFunction). Em seguida, ele mescla/reduz todos os conjuntos de dados de todos os nós em um conjunto de resultados final usando o parâmetro REDUCE especificado. Por fim, ele serializa o conjunto de resultados final para JSON e retorna o conjunto de resultados no fluxo de resposta ao cliente que iniciou a solicitação AJAX MapReduce original. Se nenhum parâmetro de redução for especificado, todos os dados brutos de todos os nós é retornado. Figura 7 mostra um exemplo da saída resultante na página de teste.

A saída resultante em uma página de teste
Figura 7 A saída resultante em uma página de teste

Comparando o projeto de exemplo com o Hadoop

Figura 8 compara a funcionalidade básica do MapReduce em Hadoop e o projeto de exemplo.

Figura 8 Uma comparação da funcionalidade básica do MapReduce

Hadoop Projeto de exemplo
Função de trabalho MAP em Java que conta palavras Qualquer método decorado com PathInfoAttribute é como uma função de trabalho MAP
Função de trabalho Reduce em Java que soma as contagens de palavra Os parâmetros Reduce na sequência de consulta da URL (como sum=) é como uma função de trabalho REDUCE que faz a operação sum
Interface gravável (serialização) Atributo [Serializable()] (serialização)
Interface WritableComparable (classificação)

IComparer<T> interface (classificação)

IEqualityComparer<T> interface (sum,union)

A entrada para o trabalho MAP é um conjunto de pares <key,value> e a saída do trabalho REDUCE é um conjunto de pares <key,value> Os argumentos para os métodos marcados com PathInfoAttribute são como a entrada para o trabalho MAP, e os parâmetros de redução na sequência de consulta da URL realizam a operação de redução e serializam os resultados como JSON, como a saída do trabalho REDUCE

Um cenário comum em que o MapReduce se destaca é contar o número de vezes que uma palavra específica aparece em milhões de documentos. Figura 9 mostra uma comparação de alguns pseudocódigos básicos que implementa o Big Data equivalente ao famoso programa de exemplo "Hello World" — o "Exemplo de contagem de palavras". A figura mostra a implementação de código Java do Hadoop e o código C# correspondente que poderia ser usado para realizar o equivalente no projeto de exemplo. Tenha em mente que esse código é simplesmente o pseudocódigo e não significa que está correto ou foi concluído. Ele é exibido apenas para ilustrar as possíveis maneiras de realizar uma funcionalidade semelhante em dois designs. Figura 10 mostra a resultante saída na página de teste.

Figura 9 Comparação do pseudocódigo "Exemplo de contagem de palavras"

Hadoop MAP

public void map(LongWritable key, Text value, OutputCollector<Text, IntWritable> output,
  Reporter reporter) throws IOException {
  String line = value.toString();
  StringTokenizer tokenizer = new StringTokenizer(line);
  while (tokenizer.hasMoreTokens()) {
    word.set(tokenizer.nextToken());
    output.collect(word, one);
  }
}

Hadoop REDUCE

public void reduce(Text key, Iterator<IntWritable> values, OutputCollector<Text, IntWritable>
  output, Reporter reporter) throws IOException {
  int sum = 0;
  while(values.hasNext()) {
    sum += values.next().get();
  }
  output.collect(key, new IntWritable(sum));
}

Projeto de exemplo MAP

http://server/.../WordCount/Test.txt?distributednodes=Node1,Node2,Node3&union=Word&sum=Count
[PathInfoAttribute(PathInfo="/WordCount/{fileName}", ReturnItemType="Row")]
public HashSet<Row> GetWordCount(string fileName)
{
  HashSet<Row> rows = new HashSet<Row>();
  byte[] bytes = File.ReadAllBytes(fileName);
  string text = Encoding.ASCII.GetString(bytes);
  string[] words = text.Split(new char[ ]{ ' ', '\r', '\n' });
  foreach(string word in words)
  {
    dynamic row = new Row();
    row["Word"] = word;
    row["Count"] = 1;
  }
  return rows;
}

Projeto de exemplo REDUCE

http://server/.../WordCount/Test.txt?distributednodes=Node1,Node2,Node3&union=Word&sum=Count

A saída resultante em uma página de teste
Figura 10 A saída resultante em uma página de teste

Figura 11 mostra como realizar a funcionalidade básica do MapReduce no projeto de exemplo. Observe como a entidade do objeto na URL é mapeada para o equivalente da função da etapa MAP com o PathInfoAttribute e como as opções de parâmetro de REDUCE na sequência de consulta da URL, como sum= e reduce=, serão iguais à funcionalidade equivalente de etapa REDUCE no Hadoop.

Figura 11 A funcionalidade básica MapReduce no projeto de exemplo

(Como Hadoop MAP)                    (Como Hadoop REDUCE)

http://server/.../BookSales?distributednodes=Node1,Node2,Node3&union=BookName&sum=Sales
[PathInfoAttribute(PathInfo="/BookSales", ReturnItemType="Book")]
public BookSales GetTotalBookSales()
{
}

(Como Hadoop MAP)                    (Como Hadoop REDUCE)

http://server/.../Alarms?distributednodes=Node1,Node2,Node3&reduce=UnionIfNotDeleted
[PathInfoAttribute(PathInfo="/Alarms", ReturnItemType="Alarm")]
public Alarms GetAlarms()
{
}
private static HashSet<Alarm> UnionIfNotDeleted(HashSet<Alarm> originalData,
  HashSet<Alarm> newData)
{
}

Exemplos adicionais

A Figura 12 mostra outras maneiras de realizar a funcionalidade do tipo Map-Reduce e como a URL RESTful é mapeada para os métodos. O código de implementação do método é omitido por questão de brevidade. O código poderia ser implementado de várias maneiras diferentes, variando de: um algoritmo que conta palavras; dados da tabela BookSales no banco de dados em cada loja do livro em uma cadeia de lojas de livro; em um método API de dados de objeto comercial que retorna uma coleção de classes de objetos comerciais; dados do sensor de locais distribuídos em todo o país. É tudo para sua imaginação, divirta-se!

Figura 12 Diversas maneiras para realizar o MapReduce com o projeto de exemplo

Exemplo

http://server/.../BookSales/Book A?distributednodes=Node1,Node2,Node3&union=BookName&sum=Sales
[PathInfoAttribute(PathInfo="/BookSales/{bookName}", ReturnItemType="Book")]
public BookSales GetTotalBookSales(string bookName)
{
}

Exemplo

http://server/.../Alarms?distributednodes=Node1,Node2,Node3&union=AlarmID
[PathInfoAttribute(PathInfo="/Alarms", ReturnItemType="Alarm")]
public Alarms GetAlarms()
{
}

Exemplo

http://server/.../Alarms?distributednodes=Node1,Node2,Node3&reduce=UnionIfNotDeleted
[PathInfoAttribute(PathInfo="/Alarms", ReturnItemType="Alarm")]
public Alarms GetAlarms()
{
}
private static HashSet<Alarm> UnionIfNotDeleted(HashSet<Alarm> originalData,
  HashSet<Alarm> newData)
{
}

Exemplo

http://server/.../SensorMeasurements/2?distributednodes=Node1,Node2,Node3&union=SensorID
[PathInfoAttribute(PathInfo="/SensorMeasurements/{sensorID}",
  ReturnItemType="SensorMeasurement")]
public SensorMeasurements GetSensorMeasurements(int sensorID)
{
}

Exemplo

http://server/.../MP3Songs?distributednodes=Node1,Node2,Node3&union=SongTitle
[PathInfoAttribute(PathInfo="/MP3Songs", ReturnItemType=" MP3Song")]
public MP3Songs GetMP3Songs()
{
}

Conclusão

Neste artigo, apresentamos uma infraestrutura básica e simples para a funcionalidade Map-Reduce que pode ser acessada com REST sobre HTTP e consumida em um dispositivo pequeno, como um smartphone ou tablet. Também abordamos a transformação de um aplicativo de nó único em um sistema distribuído básico.

Há muitas infraestruturas de MapReduce abrangentes que fazem quase tudo sob o sol, mas o foco deste artigo e objetivo era fazer um mecanismo básico de MapReduce que é extremamente fácil de configurar e é simples de usar.

A simplicidade de configuração e expansão da nossa solução oferece a capacidade de testar sua pequena ideia (em vários laptops) e aumentar para uma maior facilmente após sua ideia ter sido comprovada (em quantos servidores conforme necessário.)

O projeto de exemplo permite que você use os métodos da API de dados de objeto comercial existentes na etapa MAP aplicando simplesmente um atributo ao método que mapeia o caminho da URL para esse método. Ele também permite que você controle a etapa de redução adicionando comandos simples à sequência de consulta da URL, como uma operação de combinação (como união) nos dados com base em uma chave primária.

Aplicando o atributo para os métodos da API de dados em um objeto comercial existente e especificando um comando União com base em um campo de chave primária na URL, você obtém um mecanismo simples que pode transformar partes de um aplicativo de nó único em um sistema distribuído básico com muito pouco esforço, fornecendo a capacidade de ter uma visão global centralizada de todo o sistema distribuído em um único lugar. Por exemplo, um objeto de dados comercial que normalmente recupera apenas itens no nó único pode agora recuperar itens em vários nós, mesclados com base em um campo de chave primária no item. Os dados para os escritórios locais poderiam ser correlacionados ou agregados sob demanda e exibidos em uma tela na sede.

Para dispositivos pequenos, o "trabalho pesado" ocorre em servidores do IIS na malha e não no dispositivo pequeno. Assim, por exemplo, um aplicativo de smartphone pode aproveitar o paradigma MapReduce fazendo uma única chamada HTTP, usando os recursos mínimos do telefone.


Doug Duerner é engenheiro sênior de software com mais de 15 anos projetando e implementando sistemas de grande porte com tecnologias da Microsoft. Ele trabalhou para diversas instituições bancárias de empresas da Fortune 500 e em uma empresa de software comercial que projetou e criou o sistema de gerenciamento de rede distribuída em grande escala usado pelo Departamento de Defesa da Defense Information Systems Agency (DISA) para sua "Grade de informações globais" e o Departamento de Estado (DoS). Ele é um geek de coração, concentrando-se em todos os aspectos, mas aproveita as dificuldades técnicas mais complexas e desafiadoras, especialmente aquelas que todos dizem "não podem ser feito." Duerner pode ser contatado pelo coding.innovation@gmail.com.

Yeon-Chang Wang é engenheiro sênior de software com mais de 15 anos projetando e implementando sistemas de grande porte com tecnologias da Microsoft. Ele, também, trabalhou para diversas instituições bancárias de empresas da Fortune 500 e em uma empresa de software comercial que projetou e criou o sistema de gerenciamento de rede distribuída em grande escala usado pelo Departamento de Defesa da Defense Information Systems Agency (DISA) para sua "Grade de informações globais" e o Departamento de Estado (DoS). Ele também projetou e implementou um Sistema de Certificação de Driver em larga escala para um dos maiores fabricantes de chips do mundo. Wang possui mestrado em Ciência da Computação. Ele consome problemas complexos para o jantar e pode ser contatado pelo yeon_wang@yahoo.com.

Agradecemos aos seguintes especialistas técnicos da Microsoft pela revisão deste artigo: Mikael Sitruk e Mark Staveley
Mikael Sitruk é engenheiro sênior de Software com mais de 17 anos em design e implementação do sistema de grande escala em uma ampla gama de tecnologias. Antes da Microsoft, ele trabalhou por um líder de provedor de software de telecomunicações e implementou vários produtos novos. Ele é apaixonado pelo sistema distribuído, big data e aprendizado de máquina. Ele trabalhou vários anos com o ecossistema do Hadoop e tecnologias não-Sql como Cassandra e HBase.  Mikael pode ser contatado pelo Mikael.Sitruk@outlook.com

Mark Staveley é programador sênior com a equipe de computação intensa do Azure. Antes de mudar para o Azure, Mark fazia parte do Microsoft Research (responsáveis por supervisionar seu programa de processamento e o gerenciamento de Big Data) e também fazia parte anteriormente do Xbox One Compilers e Code Gem Team (trabalhando na compatibilidade e desempenho de mecanismo de jogo). Mark tem um bacharelado pela Queen’s University, um MSc da Universidade de Waikato e um PhD em Ciência da Computação / Química computacional pela Memorial University.  Antes da Microsoft, Mark era um pesquisador dos dois maiores centros de computação de alta performance do Canadá.