Conteúdo


Desenvolvimento de um aplicativo WebSocket para visualizar volumes de tweet em um mapa

Este tutorial mostra como usar o WebSocket, a busca no Twitter e o Google Maps API para criar um aplicativo da web Node.js que exibe a origem dos tweets em um mapa em tempo real. Também mostrarei como implementar o aplicativo à IBM® Cloud ™ (e, em geral, para qualquer plataforma como serviço baseada na Cloud Foundry).

Escrevi o aplicativo porque queria uma visão rápida da origem geográfica de tweets que mencionam um tópico específico. Por exemplo: em que lugar do mundo as pessoas estão tweetando sobre inovação?

O aplicativo TOTEM (Tweets On ThE Map) tem um identificador exclusivo simples, no qual você digita uma dúvida (como é possível fazer com o Twitter) e começar a ver os tweets correspondentes aparecerem no mapa no seu lugar de origem, anunciado por um ícone saltitante do tweet :

Screenshot of map with located tweets
Screenshot of map with located tweets

Estou interessado no volume de tweets originados de um lugar — em vez do conteúdo das mensagens — então o aplicativo TOTEM marca a localização com um círculo que aumenta de tamanho quando o número de tweets daquele lugar aumenta. Ao clicar no círculo, aparece o nome do lugar, o número de tweets desde que você iniciou a busca e o último tweet vindo de lá:

Ao invés de usar uma lógica de votação complexa no HTTP, o aplicativo abre um único canal full duplex do WebSocket, no qual o cliente e o servidor podem trocar mensagens seguindo seus próprios protocolos.

O que será necessário para desenvolver o aplicativo

  • Node.js, um ambiente de desenvolvimento do Node.js e a familiaridade em escrever aplicativos da web com o Node usando Express. (Eu usei Eclipse com a diretiva Nodeclipse plugin para desenvolver o aplicativo.)
  • Uma conta do Twitter.
  • Familiaridade com o Google Maps JavaScript API.
  • Para implementação no IBM Cloud:

É possível usar este tutorial como orientação para o desenvolvimento de aplicativos semelhantes. Se você quiser testar o aplicativo depois de conseguir o código no Passo 1, é possível testar suas alterações localmente e, então, implementar o aplicativo no IBM Cloud (como mencionado no Passo 5). Veja o arquivo README.md em meu projeto DevOps Services para instruções de como executar o aplicativo localmente.

Execute o aplicativoObtenha o código

Ao invés de usar uma lógica de votação complexa no HTTP, o aplicativo abre um único canal full duplex do WebSocket, no qual o cliente e o servidor podem trocar mensagens seguindo seus próprios protocolos.

Passo 1. Obter o código fonte e configurar o ambiente

  1. Use seu cliente Git preferido para obter o código clonando o repositório Git do projeto TOTEM:
    git clone https://hub.jazz.net/git/mcrudele/totem
  2. Executenpm install da pasta raiz. Este comando instala as dependências listadas no pacote .json no diretório node_modules. As dependências incluem a ws biblioteca WebSocket e o ntwitter assíncrono Twitter client API, junto com o Express e Jade.
  3. Altere para a pasta node_modules/ntwitter no seu projeto local e aplique uma correção que é necessária no pacote ntwitter :
    • No lib/keys.js, substitua search_base: 'http://search.twitter.com' por search_base: 'https://api.twitter.com/1.1/search'
    • No lib/twitter.js, substitua var url = this.options.search_base + '/search.json'; por var url = this.options.search_base + '/tweets.json';
  4. Gere um token de acesso ao Twitter e uma chave de API. (Leia Como obter minha chave de API se você não está familiarizado com o uso da Twitter API ou vá diretamente para Gerenciamento de aplicativos do Twitter.)

Passo 2. Incluir o endpoint WebSocket endpoint ao servidor Node.js

Eu criei a estrutura básica do meu aplicativo da web com o Nodeclipse alternando para a perspectiva Node e clicando em New > Node.js Express. É possível obter a mesma estrutura básica executando o comando express que vem com o pacote NPM express .

A estrutura básica consiste no arquivo app.js, que é o servidor Node.js, junto com outros diretórios contendo recursos estáticos, como imagens, CSS e arquivos HTML (público, por exemplo), arquivos de modelo Jade (visualizações) e arquivos de manipulador REST (rotas).

O aplicativo TOTEM define um endpoint WebSocket que aceita uma cadeia de caracteres de consulta no evento message , inicia uma busca periódica de tweets que correspondem à consulta e envia esses tweets ao cliente.

Eu escrevi a aula TwitterFinder (no módulo tfinder.js) para controlar a busca periódica usando o módulo ntwitter Node.js. TwitterFinder é um Node.js EventEmitter que emite o evento data quando ele recebe novos tweets que correspondem à consulta.

Esse código cria o endpoint WebSocket Retorno de chamada /search :

var WebSocket = require('ws');
var WebSocketServer = WebSocket.Server;

var app = express();

var server = http.createServer(app).listen(app.get('port'), function() {
  console.log('TOTEM server listening on port ' + app.get('port'));
});

// Crie o endpoint WebSocket que faz a tarefa
//
var searchServer = new WebSocketServer( {server: server, path: '/search'});

WebSocketServer , é definida na tabela ws módulo Node.js WebSocket. WebSocketServer é um Node.js EventEmitter que implementa o lado do servidor do protocolo WebSocket. Então, eu incluí o manipulador do evento connection , que é acionado quando uma nova conexão WebSocket com o cliente é criada. O evento traz como um parâmetro o cliente WebSocket que deve ser usado para a duração da conexão. É naquele soquete que você deve controlar o evento message para aceitar a cadeia de caracteres de consulta:

// One connection manages one twitter search.
// To stop current search simply close this connection.
//
searchServer.on('connection', function(ws) {

  // Create a twitter finder for the duration of the connection//
  var finder = new TwitterFinder(TwitterKeys);

  ws.on('message', function(searchstring, flags) {

    // Ignore the message if the search is already running
    // The client must close the connection to stop searching
    //
    if ( finder.isRunning() ) {
      console.log('Finder already running.');
      return;
    }

    // Set the data handler on the twitter finder
    //
    finder.on('data', function(tweets) {

      // This is the format of the object that should be sent: // // {
      //   address: "user.location attribute of the tweet",
      //   text: "text attribute of the tweet formatted as html"
      // }
      //
      // Get tweets from the bottom of the array to start
      // from the oldest.
      //
      for (var i=tweets.length-1; i>=0; i--) {
        var data = { address: tweets[i].user.location,
                     text:     tweetToText(tweets[i]) };
        ws.send(JSON.stringify(data));
      }

    });

    console.log('Start a new search for: ', searchstring);
    finder.start(searchstring);
  });

});

Passo 3. Criar a visualização HTML

Em seguida, eu escrevi um arquivo HTML para interagir com o usuário (public/index.html). O layout da web consiste na página mostrada na introdução deste tutorial, com a inclusão de um painel com algumas estatísticas:

  • O número total de tweets postados no mapa.
  • O número de tweets com erro. Esses são os tweets que não podem ser postados no mapa porque o atributo user.location está vazio ou está definido com um valor que o serviço Google Geocoder não consegue resolver. (Os usuários do Twitter às vezes fornecem localizações inconstantes em seus perfis.)
  • O número de tweets não postados no mapa porque o serviço Google Geocoder está repetidamente retornando o erro do limite de consulta , que pode ocorrer quando o cliente recebe um grande volume de tweets.

Aqui está o código para o layout da web TOTEM:

  <body>
    <div id="panel">
      <input id="search" type="search" title="Write a search, click the Search button,
      and see what's going on on Twitter! Tips: use OR, AND logical operators for multiple-words query">
      <input type="button" value="Search" onclick="startSearch()"
title="Click to start searching"> </div>
    <div id="map-canvas"></div>
    <div id="panel-monitor">
      <div>Tweets on the Map</div>
      <input id="num-tweets" type="text" readonly title="Number of tweets posted to the map"><br>
      <div>Tweets in error</div>
      <input id="num-tweets-in-error" type="text" readonly
      title="Number of tweets not posted to the map because of invalid location provided"><br>
      <div>Tweets discarded</div>
      <input id="num-tweets-discarded" type="text" readonly title="Number of tweets discarded
      because of errors with the Geocoder service (query limit)"><br>
      <div>Tweets/secs</div>
      <input id="tweets-per-sec" type="text" readonly title="Tweets per seconds posted to the map"> </div>
  </body>

Passo 4. Criar o código do cliente JavaScript

Eu escrevi a lógica do cliente para aceitar uma consulta do usuário, conectar a um endpoint WebSocket e processar o recebimento de tweets.

Tudo começa com o manipulador onclick do botão Search, startSearch():

função startSearch() {
  var search = document.getElementById('search').value;
  if ( search.trim().length===0 ) {
    alert('Cannot submit an empty search.');
    return;
  }

  // Create the connection for the new search
  // Use secure WebSocket (wss) when running on IBM Cloud
  //
  if ( window.document.location.host.match(/localhost/) ) {
    var wsUri = 'ws://' + window.document.location.host + '/search';
  } else {
    var wsUri = 'wss://' + window.document.location.host + '/search';
  }
  wssearch = new WebSocket(wsUri);
  wssearch.onopen = function(evt) { onOpen(evt) };
  wssearch.onclose = function(evt) { onClose(evt) };
  wssearch.onmessage = function(evt) { onMessage(evt) };
  wssearch.onerror = function(evt) { onError(evt) };
}

function onOpen(e) {
  // Submit the search to the server
  //
  var search = document.getElementById('search').value.trim();
  wssearch.send(search);
}

O terminal startSearch() função cria um novo objeto WebSocket e define os manipuladores para abertas, fechados, message e erro . Assim que o handshake de abertura do WebSocket estiver concluído, o manipuladoronOpen() é chamado, o que pega a consulta do usuário e a envia para iniciar a busca.

Finalmente, a tarefa divertida: o manipulador onMessage() , que analisa os dados e os grava no mapa. Eu usei o serviço Google Geocoder para obter as coordenadas do msg.address. O serviço retorna o erro OVER_QUERY_LIMIT quando você o liga frequentemente, então usei dois truques para minimizar a situação. Primeiro, eu armazenei os endereços resolvidos na memória para reduzir o número de chamadas. Segundo, eu processei as mensagens em um intervalo de tempo variável, aumentando quando OVER_QUERY_LIMIT é atingido.

Visualize public/index.html no projeto para ver o manipulador onMessage() e as outras funções que implementam a lógica de gravar resultados no mapa.

Passo 5. Implementar o aplicativo no IBM Cloud

  1. Renomeie o arquivo manifest.yml.change_me (localizado no diretório raiz) para manifest.yml.
  2. Em manifest.yml, defina o host valor para um nome exclusivo para seu aplicativo e preencha os valores TWITTER_KEYS :
     ---
      applications:
      - name: totem
        memory: 512M
        domain: mybluemix.net
        instances: 1
        host: CHANGE_ME
        path: .
        env:
          TWITTER_KEYS: '{"consumer_key":"CHANGE_ME","consumer_secret":
          "CHANGE_ME","access_token_key":"CHANGE_ME","access_token_secret":"CHANGE_ME"}'

    O terminal consumer_key e as variáveis do consumer_secret valores são a chave de API e a variável API Secret tomada na página Configurações dos aplicativos do Twitter.
  3. Edite o arquivo package.json, localizado no diretório-raiz, e remova o módulo ntwitter das dependências, para que o módulo seja transferido por upload do diretório local quando você realiza o push no IBM Cloud. (Esta etapa é necessária para manter a correção que você fez no pacote ntwitter ; caso contrário, o IBM Cloud substitui o módulo corrigido ao organizar o aplicativo.)
  4. Execute a atualização cf push comando do diretório-raiz do aplicativo e conecte seu navegador em https://yourappname.mybluemix.net/ para ver seu aplicativo em ação.

Conclusão

O aplicativo TOTEM demonstra o poder do WebSocket para escrever aplicativos da web quando a comunicação em tempo real é necessária. O WebSocket direciona as principais deficiências que fazem o HTTP impróprio para esses tipos de aplicativos. Comparado à pesquisa HTTP, o WebSocket melhora o desempenho e aprimora a simplicidade, e isso é uma peça padrão da especificação da conectividade HTML5.


Recursos para download


Temas relacionados

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Cloud computing
ArticleID=991440
ArticleTitle=Desenvolvimento de um aplicativo WebSocket para visualizar volumes de tweet em um mapa
publish-date=12052014