DevSecOpsARTIGO

Analise e otimize o desempenho do Cluster na nuvem

O Hadoop é um software livre, um framework flexível para
processamento de dados em larga escala em redes de hardware commodity.
Foi inspirado no MapReduce e na tecnologia Global File System (GFS). O Hadoop foi desenvolvida originalmente pela Google Labs, e se tornou cada vez mais
popular devido à sua eficiência, confiabilidade e escalabilidade.

Agora,
como um projeto de alto nível da Apache, o Hadoop é suportado e usado
por muitas empresas – como IBM, Google, Yahoo! e Facebook – e se tornou o
framework de fato do segmento de mercado para o processamento de
grandes quantidades de dados.

O que o Hadoop significa para a computação em nuvem?

Um dos objetivos
da computação em nuvem é fornecer alta disponibilidade aos recursos de
computação com a sobrecarga mais baixa possível. O Hadoop é uma
ferramenta perfeita para alcançar esse objetivo, devido à sua capacidade
de trabalhar com milhares de nós e petabytes de dados, além de tratar
automaticamente o planejamento de tarefas, falhas parciais e
balanceamento de carga.

Para aproveitar totalmente os recursos de computação, é importante
otimizar o desempenho, incluindo a CPU, memória e E/S (tanto de disco
quanto de rede).

O Hadoop pode trabalhar para melhorar automaticamente o
desempenho e deixar a interface, para que os usuários ajustem o
desempenho de acordo com os seus aplicativos específicos. Este artigo
apresenta os parâmetros configuráveis importantes do Hadoop e o método
para analisar e ajustar o desempenho.

Configure o ambiente

Etapas para implementar o ambiente do Hadoop

Para que possa ajustar o desempenho, primeiro é preciso desenvolver um ambiente em cluster do Hadoop. Basta seguir estas etapas:

  1. Prepare os nós do cluster com o SO Linux, JDK 1.6 e ssh instalados. Certifique-se de que o sshd esteja executando em cada nó.
  2. Acesse o site da Apache Software Foundation e faça o download de uma distribuição estável do Hadoop.
  3. Escolha o NameNode (NN), JobTracker (JT) e secondary NameNode (SNN). Os outros nós são DataNode (DN) e TaskTracker (TT). Este artigo
    pressupõe que você escolheu host001 como NN, host002 como JT e host003
    como SNN.
  4. Habilite o NN, JT e SNN para ssh de forma sem passphrase para todos os DN e TTs.
  5. Descompacte a distribuição do Hadoop transferida por download em cada nó. $HADOOP_HOME é usado abaixo para representar a posição de descompactação.
  6. Insira o diretório $HADOOP_HOME e modifique os arquivos de configuração em NN.
  • Inclua host003 em $HADOOP_HOME/conf/masters.
  • Inclua os endereços IP/nomes de host de todos os nós DD/TT em $HADOOP_HOME/conf/slaves, um host por linha.
    Nota: se você usar o nome do host, será necessário configurar o arquivo /etc/hosts para se certificar de que cada nome de host seja conhecido por todos os nós do cluster.
  • Inclua a seguinte propriedade em $HADOOP_HOME/conf/core-site.xml para configurar o IP/porta de NN:

<property>
<name>fs.default.name</name>
<value>hdfs://host001:9000</value>
</property>
  • Inclua a seguinte propriedade em $HADOOP_HOME/conf/mapred-site.xml para configurar a porta/IP de JT.
<property>
<name> mapred.job.tracker </name>
<value>host002:9001</value>
</property>

Nota: se você está usando o release 0.21.0 do Hadoop, o nome dessa propriedade deve ser mapreduce.jobtracker.address.

  • Inclua a seguinte propriedade em $HADOOP_HOME/conf/hdfs-site.xml, caso você tenha mais de uma interface de rede no NN:
<property>
<name>dfs.datanode.dns.nameserver</name>
<value>eth1</value>
<description>The name of the Network Interface from which a data node
should report its IP address.
</description>
</property>

07.  Copie todos os arquivos de configuração mencionados acima do NN para todos os outros nós do cluster no diretório $HADOOP_HOME/conf/.

08.  Insira o diretório $HADOOP_HOME/bin no NN.

  • Formate o NN por meio do comando: $./hadoop namenode -format.
  • Ative o script start-all.sh para iniciar os daemons do Hadoop.

09.  Para obter informações mais detalhadas, consulte o Hadoop Common. Nota: se você optar por usar o release 0.21.0 do Hadoop, deverá usar o JDK atual, controlado por JIRA HADOOP-6941. 

Instale e configure a ferramenta de monitoramento de desempenho nmon

A ferramenta nmon atua como um administrador do sistema, ajustador e
referência, que pode monitorar uma grande quantidade de informações
importantes de desempenho simultaneamente.

É possível usar o nmon como
ferramenta de monitoramento durante todo o processo de ajuste de
desempenho. Siga as etapas abaixo para configurar o nmon e o seu sistema
de monitoramento de desempenho:

  1. Faça o download do pacote binário do nmon a partir do site nmon for Linux
    . Encontre a versão correta do SO Linux e distribua para todos os nós do cluster do Hadoop. $NMON_HOME é usado abaixo para representar o local onde o binário deve ser colocado.
  2. Já que NN, JT, e SNN foram habilitados a usar ssh sem passphrase
    para todos os outros nós, e todas as tarefas de mapeamento/redução serão
    enviadas no JT, escolha o JT como o nó central para coletar todos os
    dados do nmon. Faça logon no nó JT e, em seguida, realize as seguintes
    etapas.
  3. Crie um diretório no JT (host002), por exemplo, /home/hadoop/perf_share, e compartilhe-o por meio de NFS usando os seguintes comandos:
  • Crie o diretório: $mkdir /home/hadoop/perf_share
  • Modifique o arquivo /etc/exports para incluir a linha a seguir:
    /home/hadoop/perf_share *(rw,sync)
  • Reinicie o serviço de NFS:
    $/etc/rc.d/init.d/nfs restart
  • Crie o diretório em todos os outros nós e monte-os no diretório perf_share no JT:
$mkdir/home/hadoop/perf_share
$mount host002: /home/hadoop/perf_share /home/hadoop/perf_share

04.  Crie o seguinte script para iniciar o nmon em todos os nós:

hosts=( shihc008 shihc009 shihc010 shihc011 shihc012 shihc013 shihc014 shihc015 
shihc016 shihc017)
# Remove all data in /home/hadoop/perf_share
for host in ${hosts[@]}
do
ssh $host "cd /home/hadoop/perf_share;rm -rf *"
done
# Start nmon on all nodes
for host in ${hosts[@]}
do
ssh $host " /usr/bin/nmon -f -m /home/hadoop/perf_share -s 30 -c 360"
done

No último comando do nmon, -f significa que você deseja salvar os dados em um arquivo, e não exibi-los na tela. -m indica onde salvar os dados, -s 30 significa que você deseja capturar os dados a cada 30 segundos e -c 360 significa que você deseja 30 pontos de dados ou capturas instantâneas
(o tempo total de coleção de dados seria 30×360 segundos ou 3 horas).

05.  Faça o download do nmonanalyser (uma planilha Excel que toma um arquivo
de saída do nmon e produz gráficos precisos para ajudar na análise) a
partir do wiki do nmonanalyser para analisar os dados de monitoramento a serem coletados.

Detalhando os parâmetros configuráveis do Hadoop

O Hadoop fornece diversas opções de configuração para usuários e
administradores em relação à configuração e ajuste do cluster. Há um
grande número de variáveis em core/hdfs/mapred-default.xml que podem ser substituídas em core/hdfs/mapred-site.xml. Algumas especificam caminhos de arquivo no sistema, mas outras ajustam alavancas e botões giratórios no interior do Hadoop.

Para o ajuste de desempenho, há quatro aspectos principais: CPU, memória, E/S de disco e rede.
Este artigo descreve os parâmetros que estão mais relacionados a esses quatro aspectos, e deixa os outros em *-defalt.xml para que você possa explorar usando um método apresentado anteriormente.

  • Parâmetros relacionados à CPU:
    mapred.tasktracker.map e
    reduce.tasks.maximum

Decida o número máximo de tarefas de mapeamento/redução que serão
executadas simultaneamente por um rastreador de tarefa. Esses dois
parâmetros estão mais relacionados à utilização da CPU.

O valor padrão
de ambos os parâmetros é 2. O aumento adequado dos seus valores, de
acordo com a condição do cluster, aumenta a utilização da CPU e,
portanto, melhora o desempenho.

Por exemplo: suponha que cada nó do
cluster possui 4 CPUs que suportam multiencadeamento simultâneo, e cada
CPU possui dois núcleos. Portanto, o número total de daemons não deve
ser superior a 4x2x2=16.

Considerando que DN e TT tomariam 2 slots, há
no máximo 14 slots para tarefas de mapeamento/redução, portanto, o
melhor valor para os dois parâmetros é 7. Configure esse parâmetro em mapred-site.xml.

  • Parâmetro relacionado à memória:
    mapred.child.java.opts

Este é o parâmetro principal para o ajuste da JVM. O valor padrão é
-Xmx200m, que dá a cada
encadeamento de tarefa-filha 200 MB de memória, no máximo. É possível
aumentar esse valor caso a tarefa seja grande, mas você deve se
certificar de que isso não cause uma troca, que reduz o desempenho
significativamente.

Vamos examinar como esse parâmetro pode afetar o uso total da
memória. Suponha que o número máximo de tarefas de mapeamento/redução
esteja configurado como 7 e mapred.child.java.opts seja
deixado com o valor padrão.

O custo de memória das tarefas em execução
será 2x7x200 MB = 2800 MB. Se cada nó trabalhador possui daemons DN e
TT, e cada daemon custa 1 GB de memória por padrão, a memória total
alocada seria por volta de 4,8 GB. Configure esse parâmetro em mapred-site.xml.

  • Parâmetros relacionados à E/S de disco:
    mapred.compress.map.output, mapred.output.compress e mapred.map.output.compression.codec

Estes são os parâmetros que controlam se a saída deve ser compactada ou não, em que mapred.compress.map.output é usado para compactar a saída de mapa, mapred.output.compress é usado para compactar a saída de tarefa e mapred.map.output.compression.codec é usado para o código de compactação. Por padrão, todas essas opções estão desativadas.

A ativação da compactação de saída pode acelerar a gravação em disco
(local/Distributed File System (HDFS) do Hadoop), e reduzir o tempo
total de transferência de dados (na ordem aleatória e na fase de
gravação de HDFS), mas, por outro lado, isso tem uma sobrecarga
adicional durante o processo de compactação/descompactação.

De acordo com minha experiência pessoal, a ativação da compactação
não é efetiva para preenchimento em sequência com chaves/valores
aleatórios. Uma sugestão é ativar a compactação somente quando a
quantidade dos dados com os quais você está trabalhando é grande, e os
dados estão organizados (principalmente dados em idioma natural). Configure esses parâmetros em mapred-site.xml.

  • io.sort.mb parameter

Esse parâmetro configura o tamanho do buffer para a classificação no
lado do mapa, em unidades de MB – 100 por padrão. Quanto maior o valor,
ocorrem menos “derramamentos” para o disco, reduzindo os tempos de E/S
no lado do mapa.

Observe que o aumento desse valor aumenta a memória
necessária para cada tarefa de mapeamento. De acordo com a experiência, quando a saída de mapa é grande e a E/S
no lado do mapa é frequente, deve-se tentar aumentar esse valor. Configure esse parâmetro em mapred-site.xml.

  • io.sort.factor parameter

Esse parâmetro configura o número de fluxos de entrada (arquivos) que
serão mesclados simultaneamente nas tarefas de mapeamento e redução.
Quanto maior for esse valor, ocorrem menos “derramamentos” para o disco,
reduzindo os tempos de E/S nos lados de mapeamento e redução.

Observe
que o aumento desse valor pode provocar mais atividades de coleta de
lixo se a memória alocada para cada tarefa não for suficiente.

De acordo com a experiência, quando ocorrem muitos “derramamentos”
para o disco e os tempos de E/S da fase de classificação e ordenação
aleatória são altos, deve-se tentar aumentar esse valor. Configure esse parâmetro em mapred-site.xml.

  • mapred.job.reduce.input.buffer.percent parameter

Esse parâmetro configura a porcentagem de memória (em relação ao tamanho
máximo de heap) para reter as saídas de mapa durante a fase de redução.

Quando a ordenação aleatória termina, as saídas de mapa restantes na
memória devem consumir menos do que o limite, para que a fase de redução
possa começar, 0 por padrão. Quanto maior esse valor, menos mesclagens
ocorrem no disco, reduzindo os tempos de E/S no disco local durante a
fase de redução.

Observe que o aumento desse valor pode provocar mais
atividades de coleta de lixo se a memória alocada para cada tarefa não
for suficiente. De acordo com a experiência, quando a saída de mapa é grande e a E/S
do disco local é frequente durante a fase de redução até a fase de
classificação, deve-se tentar aumentar esse valor.

Configure esse parâmetro nos parâmetros mapred-site.xml.

  • mapred.local.dir e dfs.data.dir

Esses dois parâmetros decidem onde colocar os dados no Hadoop. Em que mapred.local.dir decide onde os dados intermediários de MapReduce (dados de saída de mapa) são armazenados, e dfs.data.dir decide onde os dados de HDFS são armazenados.

De acordo com a experiência, a distribuição desses locais em todos os
discos em cada nó pode equilibrar a E/S de disco e, portanto, melhorar
muito o desempenho da E/S de disco.

Configure mapred.local.dir em mapred-site.xml e
dfs.data.dir em hdfs-site.xml.

  • Parâmetros relacionados à rede:
    topology.script.file.name

Este é o parâmetro que aponta para o script definido pelo usuário para
determinar a correspondência entre racks e hosts, e para configurar o
conhecimento em relação aos racks. Configure esse parâmetro no arquivo core-site.xml.

O conhecimento em relação aos racks é a configuração mais importante
para melhorar o desempenho da rede – recomenda-se enfaticamente que você
o configure seguindo as instruções contidas em Cluster Setup e em Hadoop Wiki.

  • mapred.reduce.parallel.copies parameter

Esse parâmetro determina o número de encadeamentos usados para copiar
saídas de mapa para o redutor, 5 por padrão. O aumento desse valor pode
aumentar a taxa de fluxo da rede e acelerar o processo de cópia das
saídas de mapa, mas, por outro lado, provoca um uso mais intenso da CPU.

De acordo com a experiência, o efeito do aumento desse valor é
discreto, e a sugestão é aumentar esse valor somente se a saída de mapa é
muito grande na sua tarefa.

Observações: os nomes de parâmetros listados acima
estão no release 0.20.x do Hadoop. Se você usa o release 0.21.0, alguns
nomes podem mudar. Além dos parâmetros do Hadoop, também há alguns
parâmetros do sistema, como largura da banda entre os racks, que afetam o
desempenho geral.

Como ajustar e melhorar o desempenho

Depois dos longos, porém necessários, preparativos acima, você
finalmente chegou a este ponto, para ver como ajustar e melhorar o
desempenho. O processo todo pode ser separado em etapas.

Etapa 1: escolha a referência de teste

O desempenho de todo o cluster do Hadoop é decidido por dois
aspectos: desempenho de E/S de HDFS e desempenho de MapReduce no tempo
de execução.

O próprio Hadoop fornece diversas referências como TestDFSIO e dfsthroughput para o teste de E/S de HDFS, que estão contidas em hadoop-*-test.jar, Sort para o teste geral de hardware, que está contido em hadoop-*-examples.jar e Gridmix, o qual imita a carga de trabalho combinada em um ambiente de grade e está sob o diretório $HADOOP_HOME/src/benchmarks. É possível escolher qualquer uma dessas referências, de acordo com a demanda de teste.

Entre todas as referências, Sort pode refletir o
desempenho de MapReduce no tempo de execução (durante o procedimento de
“classificar”) e desempenho de E/S de HDFS (durante o procedimento de
“gravar os resultados da classificação no HDFS”) quando os dados de
entrada são grandes.

E mais, Sort é a referência de hardware recomendada pela Apache. (Você encontra informações no wiki do Hadoop.) Portanto, Sort é usado como exemplo de referência de teste para mostrar o método de ajuste de desempenho.

Etapa 2: construa a linha de base

  1. Ambiente de teste:
  • Referência: Sort
  • Escala dos dados de entrada: 500 GB
  • Escala do cluster do Hadoop: 10 nós DN/TT
  • Todos os nós são homogêneos
  • Informações do nó:

– SO Linux

– Dois processadores de 4 núcleos, suportam multiencadeamento simultâneo

– 32 GB de memória

– Cinco discos de 500 GB

02.  Scripts de teste: estes são os scripts usados para teste (consulte o wiki do Hadoop para obter mais informações sobre a execução da referência Sort). Todos os scripts devem ser executados no nó JT.

Nota: coloque o script start_nmon.sh citado acima e os scripts a seguir no mesmo diretório que foi escolhido para armazenar o resultado do teste.

  • baseline_test.sh
#!/bin/sh
# since there are 10 nodes, should write 50 GB file on each
fSize=5368709120
$HADOOP_HOME/bin/hadoop jar $HADOOP_HOME/hadoop-0.20.1-examples.jar
randomwriter -D
test.randomwrite.bytes_per_map=$fSize /rand_$fSize 2>&1 | tee
./testRes/randomwriter_$fSize.out
mkdir -p ./testRes/nmonFiles
# run three cycles to get a more precise result
for runtimes in {a,b,c}
do
./ run_sort_baseline.sh $fSize $runtimes
done
  • run_sort_baseline.sh
#!/bin/sh
$HADOOP_HOME/bin/hadoop dfs -rmr /rand_$1-sorted
./start_nmon.sh
$HADOOP_HOME/bin/hadoop jar $HADOOP_HOME/hadoop-0.20.1-examples.jar sort
-r 70 /rand_$1
/rand_$1-sorted 2>&1 |tee ./testRes/sort_baseline_$2.out
cp -r /home/hadoop/perf_share ./testRes/nmonFiles/mb$4_$2

03.  Valores de parâmetro para o teste de linha de base:

Valor de parâmetro do Hadoop:

  • mapred.tasktracker.map.tasks.maximum = 2 (valor padrão)
  • mapred.tasktracker.reduce.tasks.maximum = 2 (valor padrão)
  • mapred.reduce.parallel.copies = 5 (valor padrão)
  • mapred.child.java.opts = -Xmx200m (valor padrão)
  • mapred.job.reduce.input.buffer.percent = 0 (valor padrão)
  • io.sort.mb = 100 (valor padrão)
  • io.sort.factor = 10 (valor padrão)
  • mapred.local.dir = /hadoop/sdb
  • dfs.data.dir = /hadoop/sdc, /hadoop/sdd, /hadoop/sde

Valor do parâmetro do sistema:

  • Largura da banda entre os racks = 1 Gb

04.  Resultado do teste de linha de base:

  • tempo de execução: 10051 segundos
  • Resumo do uso de recursos:

  • Gráfico detalhado:

Depois de obter todos os dados do nmon, é possível usar o nmonanalyser
para gerar gráficos. Como o nmonanalyser é uma planilha Excel, basta
abri-la, clicar em analyse nmon data e escolher os arquivos do nmon. Em seguida, é possível obter os gráficos analisados. 

Figura 1. Analise os dados do nmon usando o nmonanalyser

Os gráficos detalhados gerados pelo nmonanalyser para o teste de linha de base são os seguintes:

Figura 2. Gráficos de NameNode

Figura 3. Gráficos de JobTracker

Figura 4. Gráficos de DataNode/TaskTracker

Etapa 3: localize o gargalo

É necessário explorar atentamente o gargalo do sistema a partir dos
dados e gráficos de monitoramento. Como a carga de trabalho principal
foi designada para os nós DN/TT, primeiro é necessário se concentrar no
uso de recursos dos nós DN/TT (somente os gráficos de nmon referentes a
DN/TT são mostrados abaixo, para poupar espaço).

A partir dos dados de monitoramento e gráficos da linha de base, é
possível ver os gargalos do sistema: a CPU não foi totalmente utilizada
na fase de mapeamento (na maior parte do tempo, abaixo de 40%), e a E/S
de disco foi muito frequente.

Etapa 4: elimine o gargalo

Primeiramente, tente aumentar a utilização da CPU na fase de
mapeamento. A partir da introdução dos parâmetros do Hadoop, é possível
perceber que, para aumentar a utilização da CPU, é necessário aumentar o
valor dos parâmetros mapred.tasktracker.map e
reduce.tasks.maximum.

No ambiente de teste, cada nó tem dois processadores de quatro
núcleos que suportam multiencadeamento simultâneo. Portanto, você tem um
total de 16 slots disponíveis e pode configurar os dois parâmetros como
7.

Para fazer a mudança, é necessário configurar as propriedades mapred.tasktracker.map e
reduce.tasks.maximum em mapred-site.xml. Reinicie o cluster e ative baseline_test.sh novamente (já que a configuração é feita no arquivo mapred-site.xml, não há necessidade de modificar o script aqui). O mapred-site.xml modificado ficará assim:

<configuration>
<property>
<name>mapred.tasktracker.map.tasks.maximum</name>
<value>7</value>
</property>
<property>
<name>mapred.tasktracker.map.tasks.maximum</name>
<value>7</value>
</property>
</configuration>

Estes são os resultados do teste depois do ajuste:

  • Tempo de execução: 8599 segundos
  • Resumo do uso de recursos:

Figura 5. Gráficos de DataNode/TaskTracker depois do ajuste

Etapa 5: nova rodada de ajuste – repita as etapas 3 e 4

A partir dos dados e gráficos que você obtém após ajustar o número
máximo de tarefas de mapeamento/redução em cada TaskTracker, pode-se ver
que a CPU é totalmente utilizada durante a fase de mapeamento.

Entretanto, enquanto isso, a frequência de E/S de disco continua alta;
portanto, uma nova rodada do processo de ajuste, monitoramento e análise
é necessária.

É preciso repetir essas etapas até que não haja mais gargalos no sistema, e cada recurso seja utilizado totalmente.

Observe que cada ajuste não proporcionará necessariamente uma melhoria
de desempenho. Se houver uma redução de desempenho, é necessário voltar
para as configurações anteriores e tentar outro ajuste para eliminar o
gargalo.

Nesse teste, os seguintes resultados otimizados foram obtidos no final:

  • Tempo de execução: 5670 segundos
  • Valores de parâmetro do Hadoop:
  • Valores de parâmetro do sistema: largura da banda entre racks = 1 Gb
  • Resumo do uso de recursos:

Figura 6. Gráficos de DataNode/TaskTracker – Segunda rodada de ajuste

Etapa 6: teste e melhoria da escalabilidade

Para verificar mais adiante o resultado do ajuste, é necessário
aumentar a escala do cluster e a escala de dados de entrada, usando a
configuração otimizada que você obteve para testar a escalabilidade da
configuração.

Mais precisamente, aumentar a escala para 30 nós e a
escala de dados de entrada para 1,5 TB e, em seguida, refazer todo o
procedimento de teste acima. Como o espaço é limitado, o procedimento detalhado de ajuste não será
descrito aqui.

O método de monitoramento e análise é exatamente igual ao
mencionado acima, e o gargalo principal que você viu ocorreu na rede. A
largura da banda entre os racks se tornou insuficiente quando os dados
de entrada passaram à escala de TB.

Quando a largura da banda entre
racks foi aumentada para 4 GB e todos os outros parâmetros otimizados
para dez nós não foram alterados, o tempo final de execução foi de 5916
segundos – algo muito próximo ao resultado otimizado para dez nós (5670
segundos).

Conclusão

Agora, você sabe como monitorar um cluster do Hadoop, analisar o
gargalo do sistema usando os dados de monitoramento e ajustar o
desempenho. Esperamos que esse conhecimento possa ajudá-lo a aproveitar
ao máximo o cluster do Hadoop, e tenha como resultado o aumento na
eficiência das suas tarefas.

É possível usar o método descrito neste
artigo para explorar mais adiante os parâmetros configuráveis do Hadoop, além de encontrar a relação entre a configuração dos parâmetros e as diversas
características das tarefas.

E mais: esse ajuste baseado em parâmetros é “estático”, pois um conjunto
de configurações de parâmetros só é otimizado para um tipo de tarefa.
Para obter mais flexibilidade, talvez você esteja interessado em estudar
o algoritmo de programação do Hadoop e compartilhar seus novos métodos
para melhorar o desempenho do Hadoop.

Recursos

Aprender

Discutir

***

artigo publicado originalmente no developerWorks Brasil, por Yu Li

Yu Li é engenheiro de software na China. Faz parte da
equipe IBM InfoSphere BigInsight, cujo objetivo é construir uma
plataforma de analítica tendo como base o Apache Hadoop. Suas áreas de
conhecimento englobam computação em nuvem, ajuste de desempenho,
mineração de dados, tecnologia de banco de dados e tecnologias de
middleware.

é o portal de tecnologia da IBM para profissionais de TI de todo o mundo. Colabore, aprenda a criar aplicativos inovadores e compreenda tecnologias avançadas. Acesse mais artigos e tutoriais em www.ibm.com/developerworks/br

Ver perfil