Dezembro 2015

Volume 30 – Número 13

Execução de teste – Introdução ao Spark para desenvolvedores .NET

Por James McCaffrey | Dezembro de 2015

James McCaffreySpark é uma estrutura de computação de software livre para Big Data que está se tornando cada vez mais popular, especialmente em cenários de aprendizado de máquina. Neste artigo vou descrever como instalar o Spark em um computador executando um sistema operacional Windows e explicar funcionalidades básicas do Spark do ponto de vista de um desenvolvedor .NET.

A melhor maneira de demonstrar o rumo que este artigo tomará é examinar a sessão interativa de demonstração na Figura 1. A partir de um shell de comando do Widows em execução no modo administrativo, eu iniciei um ambiente Spark emitindo um comando spark-shell.

O Spark em ação
Figura 1 - Spark em ação

O comando spark-shell gera um interpretador Scala que executa no shell, que por sua vez emite um prompt Scala (scala>). Scala é uma linguagem de script baseada em Java. Existem outras maneiras de interagir com o Spark, mas usar um interpretador Scala é a abordagem mais comum, em parte porque a estrutura do Spark é escrita principalmente em Scala. Você também pode interagir com o Spark usando comandos de linguagem Python ou escrevendo programas em Java.

Veja várias mensagens de aviso na Figura 1. Essas mensagens são muito comuns durante a execução do Spark, pois ele possui vários componentes opcionais que, se não encontrados, geram avisos. Em geral, as mensagens de aviso podem ser ignoradas para cenários simples.

O primeiro comando inserido na sessão de demonstração é:

scala> val f = sc.textFile("README.md")

Ele pode ser vagamente interpretado com o significado “Armazenar em um objeto RDD imutável chamado f os conteúdos do arquivo README.md”. Os objetos Scala podem ser declarados como val ou var. Os objetos declarados como val são imutáveis e não podem ser alterados.

O interpretador Scala tem um objeto de contexto Spark integrado chamado sc, que é usado para acessar a funcionalidade Spark. A função textFile carrega os conteúdos de um arquivo de texto em uma estrutura de dados Spark chamada de conjunto de dados distribuído resiliente (RDD). Os RDDs são as principais abstrações de programação usadas no Spark. Você pode pensar no RDD como algo parecido com uma coleção .NET armazenada em RAM através de vários computadores.

O arquivo de texto README.md (a extensão .md significa documento de redução) está localizado no diretório raiz do Spark C:\spark_1_4_1. Se o seu arquivo de destino estiver localizado em outro lugar, você pode fornecer um caminho completo como C:\\Data\\ReadMeToo.txt.

O segundo comando na sessão de demonstração é:

scala> val ff = f.filter(line => line.contains("Spark"))

Isto significa, “Armazenar em um objeto RDD imutável chamado ff somente as linhas de um objeto f que possuírem a palavra “Spark” nelas”. A função filtro aceita aquilo que chamamos de um fechamento. Você pode imaginar um fechamento como uma espécie de função anônima. Aqui, o fechamento aceita uma parâmetro de entrada de cadeia de caracteres fictícia chamado linha e retorna verdadeiro se, caso contrário, a linha contiver “Spark,” falso.

Uma vez que “linha” é apenas um nome de parâmetro, eu poderia ter usado qualquer outro nome no fechamento, por exemplo:

ln => ln.contains("Spark")

Spark diferencia maiúsculas de minúsculas, por isso, a seguinte ação geraria um erro:

ln => ln.Contains("Spark")

A Scala possui algumas características de linguagem de programação funcionais, possibilitando compor vários comandos. Por exemplo, os dois primeiros comandos poderiam ser combinados em um só da seguinte forma:

val ff = sc.textFile("README.md").filter(line => lne.contains("Spark"))

Os três comandos finais na sessão de demonstração são:

scala> val ct = ff.count()
scala> println(ct)
scala> :q

A função de contagem retorna o número de itens em um RDD, o que neste caso é o número de linhas no arquivo README.md que contém a palavra Spark. Existem 19 linhas deste tipo. Para sair de uma sessão Scala do Spark, você pode digitar o comando :q.

Instalação do Spark em um computador Windows

Existem quatro etapas principais para instalar o Spark em um computador Windows. Primeiro, você instala um Kit de Desenvolvimento do Java (JDK) e o Ambiente de Tempo de Execução do Java (JRE). Segundo, você instala a linguagem Scala. Terceiro, você instala a estrutura do Spark. E em quarto, você configura as variáveis do sistema do computador host.

A distribuição do Spark vem em um formato .tar comprimido, por isso, você precisará de um utilitário para extrair os arquivos Spark. Eu recomendo a instalação do programa de software livre 7-Zip antes de começar.

Embora o Spark e seus componentes são sejam formalmente suportados por uma grande variedade de versões do sistema operacional Windows, eu consegui instalar o Spark com êxito em computadores executando Windows 7, 8, 10 e os Server 2008 e 2012. A demonstração mostrada na Figura 1 está em execução em um computador com Windows 8.1.

Você instala o JDK através de um executável de extração automática que pode encontrar pesquisando na Internet. Eu usei a versão jdk-8u60-windows-x64.exe.

Ao instalar a versão de 64 bits do JDK, o diretório de instalação padrão é C:\Arquivos de Programas\Java\jdkx.x.x_xx\, como mostrado na Figura 2. Recomendo que você não altere a localização padrão.

A localização padrão do JDK
Figura 2 - A localização padrão do JDK

A instalação do JDK também instala um JRE associado. Após a conclusão da instalação, o diretório pai padrão do Java conterá o diretório JDK e um diretório JRE associado, como mostrado na Figura 3.

![JDK e JRE do Java instalados em C:\Arquivos de Programas\Java\](images/mt595756.McCaffrey_Figure3-JavaInstallDirectories_hires(pt-br,MSDN.10).png "JDK e JRE do Java instalados em C:\Arquivos de Programas\Java\")
Figura 3 - JDK e JRE do Java instalados em C:\Arquivos de Programas\Java\

Observe que seu computador terá provavelmente um diretório Java com um ou mais diretórios JRE de 32 bits em C:\Arquivos de Programas (x86). Não há problema em ter as duas versões de 32 e 64 bits do JRE em seu computador, mas eu recomendo usar somente a versão de 64 bits do JDK do Java.

Instalação do Scala

A próxima etapa é instalar a linguagem Scala, mas antes de começar, você deve ir até o site de download do Spark (descrito na próxima seção deste artigo) e determinar qual versão do Scala você deseja instalar. A versão do Scala deve ser compatível com a versão do Spark que você instalará na próxima etapa.

Infelizmente, as informações sobre a compatibilidade das versões do Scala e do Spark são escassas. Quando eu estava instalando os componentes do Spark (quando você ler este artigo já terá passado bastante tempo), a versão atual do Spark era 1.5.0, mas eu não consegui encontrar nenhuma informação sobre qual versão do Scala era compatível com esta do Spark. Portanto, eu olhei para a versão anterior do Spark, que era a 1.4.1, e encontrei algumas informações nos sites de discussão de desenvolvedores na Internet que sugeriam que a versão 2.10.4 do Scala era provavelmente compatível com a versão 1.4.1 do Spark.

A instalação do Scala é fácil. O processo de instalação envolve simplesmente a execução de um arquivo de instalação .msi.

O assistente de instalação do Scala guia você ao longo do processo. Curiosamente, o diretório de instalação padrão do Scala está no diretório de 32 bits C:\Arquivos de Programas (x86)\ em vez de estar no diretório de 64 bits C:\Arquivos de Programas\ (veja a Figura 4).

![Instalações do Scala em C:\Arquivos de Programas (x86)\scala\](images/mt595756.McCaffrey_Figure5-ScalaInstallDirectories_hires(pt-br,MSDN.10).png "Instalações do Scala em C:\Arquivos de Programas (x86)\scala\")
Figura 4 - Instalações do Scala em C:\Arquivos de Programas (x86)\scala\

Se você pretende interagir com o Spark escrevendo programas em Java em vez de usar comandos Scala, precisará instalar uma ferramenta adicional chamada Scala Simple Build Tool (SBT). Interagir com o Spark através de programas Java é muito mais difícil do que usar o Scala interativo.

Instalação do Spark

A próxima etapa é instalar a estrutura Spark. Primeiro, verifique se você tem um programa utilitário como o 7-Zip, que pode extrair arquivos de formato .tar. O processo de instalação do Spark é manual: você baixa uma pasta comprimida para seu computador local, extrai os arquivos comprimidos e, em seguida, copia os arquivos para um diretório raiz. Isso significa que se você deseja desinstalar o Spark, basta excluir os arquivos do Spark.

Você pode encontrar o site do Spark em spark.apache.org. A página de download permite que você selecione uma versão e um tipo de pacote. O Spark é uma estrutura de computação e requer um sistema de arquivos distribuído (DFS). O DFS mais comum usado com a estrutura Spark é, de longe, o sistema de arquivos distribuído Hadoop (HDFS). Para fins de teste e experimentação, como a sessão de demonstração na Figura 1, você pode instalar o Spark em um sistema que não tem um DFS. Neste cenário, o Spark usará o sistema de arquivos local.

Se você nunca extraiu arquivos .tar antes, poderá achar o processo um pouco confuso, pois normalmente é necessário extrair duas vezes. Primeiro, baixe o arquivo .tar (o meu foi nomeado spark-1.4.1-bin-hadoop2.6.tar) para qualquer diretório temporário (Eu usei C:\Temp). Em seguida, clique com o botão direito do mouse no arquivo .tar, selecione “Extrair arquivos” no menu de contexto e extraia para um novo diretório dentro do diretório temporário.

O primeiro processo de extração cria um novo arquivo comprimido sem qualquer extensão de arquivo (no meu caso spark-1.4.1-bin-hadoop2.6). Em seguida, clique com o botão direito do mouse nesse arquivo novo, selecione “Extrair arquivos” novamente no menu de contexto e extraia para um diretório diferente. Esta segunda extração produzirá os arquivos de estrutura do Spark.

Crie um diretório para os arquivos de estrutura do Spark. Uma convenção comum é criar um diretório chamado C:\spark_x_x_x, em que os valores x indicam a versão. Usando esta convenção, eu criei um diretório C:\spark_1_4_1 e copiei os arquivos extraídos para esse diretório, como mostrado na Figura 5.

![Copie manualmente os arquivos Spark extraídos para C:\spark_x_x_x\](images/mt595756.McCaffrey_Figure6-SparkFilesInstalled_hires(pt-br,MSDN.10).png "Copie manualmente os arquivos Spark extraídos para C:\spark_x_x_x\")
Figura 5 - Copie manualmente os arquivos Spark extraídos para C:\spark_x_x_x\

Configuração de seu computador

Depois de instalar o Java, o Scala e o Spark, a última etapa é configurar o computador host. Isso envolve baixar um arquivo utilitário especial necessário para Windows, configurar três variáveis de ambiente de sistema definidas pelo usuário, configurar a variável do caminho do sistema e, opcionalmente, modificar um arquivo de configuração do Spark.

Executar o Spark no Windows exige que um arquivo utilitário especial chamado winutils.exe esteja em um diretório local chamado C:\hadoop. Você pode encontrar o arquivo em vários lugares através de uma pesquisa na Internet. Eu criei um diretório C:\hadoop, encontrei uma cópia do winutils.exe em http://public-repo-1.hortonworks.com/hdp-win-alpha/winutils.exe e baixei o arquivo no diretório.

Em seguida, crie e defina três variáveis de ambiente de sistema definidas pelo usuário e modifique a variável de Caminho do sistema. Vá para Painel de Controle | Sistema | Configurações Avançadas do Sistema | Avançado | Variáveis de Ambiente. Na seção Variáveis do Usuário, crie três variáveis novas com estes nomes e valores:

JAVA_HOME     C:\Program Files\Java\jdk1.8.0_60
SCALA_HOME    C:\Program Files (x86)\scala
HADOOP_HOME   C:\hadoop

Em seguida, nas Variáveis do Sistema, edite a variável de Caminho adicionando a localização do binários do Spark, C:\spark_1_4_1\bin. Tenha cuidado; você realmente não vai querer perder quaisquer valores na variável de Caminho. Observe que o processo de instalação do Scala já terá adicionado a localização dos binários do Scala para você (veja a Figura 6).

Configuração de seu sistema
Figura 6 - Configuração de seu sistema

Depois de você ter configurado suas variáveis do sistema, eu recomento modificar o arquivo de configuração do Spark. Vá para o diretório raiz C:\spark_1_4_1\config e faça uma cópia do arquivo log4j.properties.template. Renomeie essa cópia removendo a extensão .template. Edite a primeira entrada de configuração do log4j.rootCategory=INFO para log4j.rootCategory=WARN.

A ideia é que, por padrão, o Spark despeja todos os tipos de mensagens informativas. Alterar o nível de registro de INFO para WARN reduz muito o número de mensagens e torna a interação com o Spark menos confusa.

O Hello World do Spark

O exemplo Hello World de computação distribuída é para calcular o número de palavras diferente em uma fonte de dados. A Figura 7 mostra o exemplo de contagem de palavras usando o Spark.

Exemplo de contagem de palavras usando o Spark
Figura 7 - Exemplo de contagem de palavras usando o Spark

O shell do Scala às vezes é chamado de read, evaluate, print loop (REPL) shell. Você pode limpar o Scala REPL digitando CTRL+L. O primeiro comando na Figura 7 carrega o conteúdo do arquivo README.md em um RDD chamado f, como explicado anteriormente. Em um cenário realista, sua fonte de dados poderia ser um enorme arquivo espalhado por centenas de computadores ou poderia estar em um banco de dados distribuído como o Cassandra.

O próximo comando é:

scala> val fm = f.flatMap(line => line.split(" "))

A chamada de função flatMap divide cada linha no objeto RDD f nos caracteres de espaço em branco, de forma que o objeto RDD resultante manterá uma coleção de todas as palavras no arquivo. Do ponto de vista de um desenvolvedor, você pode imaginar um fm como algo semelhante a uma coleção .NET List<string>.

O próximo comando é:

scala> val m = fm.map(word => (word, 1))

A função de mapa cria um objeto RDD que mantém pares de itens, em que cada par consiste em uma palavra e o valor inteiro 1. Você pode ver isto com mais clareza se emitir um comando m.take(5). Você verá as primeiras cinco palavras no arquivo README.md e um valor 1 ao lado de cada palavra. Do ponto de vista de um desenvolvedor, m é basicamente uma coleção List<Pair> em que cada objeto Pair consiste em uma cadeia de caracteres e um inteiro. A cadeia de caracteres (uma palavra em README.md) é uma chave e o inteiro é um valor, mas ao contrário de muitos pares chave-valor no Microsoft .NET Framework, os valores-chave duplicados são permitidos no Spark. Os objetos RDD que mantêm pares chave-valor são às vezes chamados de RDDs pares para distingui-los de RDDs comuns.

O próximo comando é:

scala> val cts = m.reduceByKey((a,b) => a + b)

A função reduceByKey combina os itens em objeto m adicionando os valores inteiros associados a valores-chave iguais. Se você fez um cts.take(10), verá 10 das palavras em README.md seguidas do número de vezes que cada palavra ocorre no arquivo. Você também poderá observar que as palavras no objeto cts não estão necessariamente em nenhuma ordem específica.

A função reduceByKey aceita um fechamento. Você pode usar uma notação de atalho do Scala alternada:

scala> val cts = m.reduceByKey(_ + _)

O sublinhado é um curinga de parâmetro para que a sintaxe possa ser interpretada como “adicionar sempre que dois valores forem recebidos”.

Observe que este exemplo de contagem de palavras usa uma função de mapa permitida pela função reduceByKey. Este é um exemplo do paradigma MapReduce.

O próximo comando é:

scala> val sorted =
     cts.sortBy(item => item._2, false)

Este comando classifica o item no RDD cts, baseado no segundo valor (a contagem inteira) dos itens. O argumento falso significa classificar em ordem descendente, em outras palavras, da contagem mais alta para a mais baixa. O formulário de sintaxe do atalho do Scala do comando de classificação seria:

scala> val sorted = cts.sortBy(_._2, false)

Como o Scala possui muitas características de linguagem funcional e usa muitos símbolos em vez de palavras-chave, é possível escrever o código do Scala, pois ele é muito intuitivo.

O comando final no exemplo Hello World serve para exibir os resultados:

scala> sorted.take(5).foreach(println)

Isso significa, “Buscar os cinco primeiros objetos no objeto RDD nomeados e classificados, iterar sobre essa coleção, aplicar a função println em cada item”. Os resultados são:

(,66)
(the,21)
(Spark,14)
(to,14)
(for,11)

Isso significa que existem 66 ocorrências da palavra vazia/nula em README.md, 21 ocorrências da palavra “the,”, 14 ocorrências de “Spark” e assim por diante.

Conclusão

As informações apresentadas neste artigo devem preparar você para instalar e executar o Spark, se desejar experimentá-lo em um computador Windows. O Spark é uma tecnologia relativamente nova (criada na Universidade de Berkeley em 2009), mas o interesse por ela aumentou drasticamente ao longo dos últimos meses, pelo menos entre os meus colegas.

Em uma competição de 2014 entre estruturas de processamento de Big Data, o Spark estabeleceu um novo recorde de desempenho, ganhando facilmente do último recorde estabelecido por um sistema Hadoop no ano anterior. Com suas características de desempenho excepcionais, o Spark é especialmente muito adequado para uso em sistemas de aprendizado de máquina. O Spark suporta uma biblioteca de software livre de algoritmos de aprendizado de máquina chamada M Lib.


Dr. James McCaffreytrabalha para a Microsoft Research em Redmond, Washington. Ele trabalhou em vários produtos da Microsoft, incluindo Internet Explorer e Bing. Entre em contato com o Dr. McCaffrey pelo email jammc@microsoft.com.

Agradecemos aos seguintes especialistas técnicos da Microsoft pela revisão deste artigo: Gaz Iqbal e Umesh Madan