Widget do Submarino com YQL

4/6/2009 | Tags:, , , , , , | Escrito por: Dirceu Pauka Jr.

Meu último post foi uma tentativa de explicar o funcionamento da Yahoo Query Language. No final falei da possibilidade de se fazer um Widget do Submarino com a YQL e agora volto para implementar o prometido.

YQL

Como eu disse existe um post explicando a YQL, porém aqui vai uma explicação rápida:

A YQL pode ser usada para buscar dados nas APIs do Yahoo (Flickr, Delicious, Placemaker, Maps, …), nas APIs do DataTables (Twitter, Amazon, GeoLocation, …) ou em qualquer outro documento da web (página HTML).

Para mostrar como a busca em um documento qualquer da web é feita, escrevi o seguinte código:

Para testar use o console da YQL ou veja teste dessa busca. O YQL vai retornar o conteúdo pedido em XML ou JSON.

JSONP

“JSON with Padding” é uma técnica que permite o carregamento assíncrono de informações de um domínio externo.

Toda biblioteca de JavaScript que se preze possui um método para esse procedimento.
jQuery: $.getJSON(url, function(data) { // callback });
MooTools: Request.JSON([options]);

A implementação desse tipo de requisição é bem simples.

Sabemos que o retorno de um arquivo chamado pela tag script é imediatamente executado pelo interpretador JavaScript.

Também sabemos que um objeto em JS pode ser representado por {} e pode conter pares de chave/valor. Ex: var Objeto = { chave: ‘valor’ };

Assim sendo, o que precisamos agora é que o conteúdo de um arquivo que vai ser requisitado utilizando a tag script seja uma chamada a nossa função previamente declarada.

Atente que ao contrario do xhttprequest o elemento HTML script não possui limitações quanto ao domínio que a requisição pode ser feita. Porém, para que a técnica funcione é necessário que o servidor requisitado retorne (em texto puro) uma chamada para nossa função.

Requisição:
http://pomoti.com/arquivo.php?callback=recebe_json
PHP simples para retorno:
echo $_GET['callback'] . ‘( {chave: “valor”} )’;
Retorno:
recebe_json( {chave: ‘valor’} );

Todos webservices que queiram trabalhar com JSON devem, então, aceitar um parâmetro para envio de callback.

Widget do Submarino

Fundamentadas as técnicas necessárias para a implementação vamos ao código.

// tentando duas querys diferentes. a estrutura do submarino pode ser alterada e isso evita quebrar
getJSON(query1);
}
function getJSON(query) {
// monta a requisição REST
var url = ‘http://query.yahooapis.com/v1/public/yql?q=’+
encodeURIComponent(query)+
‘&format=json’+
‘&callback=Submarino.recebe’;

// se tag head não existir, cria uma para evitar problemas
if (!document.getElementsByTagName(’head’)[0]) {
document.getElementsByTagName(’body’)[0].appendChild( document.createElement(’head’) );
}

// adiciona o elemento



YQL – Yahoo Query Language

27/5/2009 | Tags:, , , , , | Escrito por: Dirceu Pauka Jr.

YQL é uma linguagem que permite buscas nas bases de dados do Yahoo ou em documentos especificados pelo desenvolvedor.

Uma boa maneira de entender o uso dessa linguagem é ver um pouco de código.

JS-Placemaker – geolocate texts in JavaScript é uma demonstração de como utilizar a YQL para buscar informações em uma API do Yahoo chamada Placemaker.
Dê uma olhada no código.

O exemplo usou o Placemaker, porém as outras APIs do Yahoo também podem ser buscadas.

Vamos a outro exemplo, dessa vez utilizando dados de outra API.

API do Twitter pelo Yahoo! YQL

Para quem nunca usou a API do Twitter existe um guia sobre a API.

Twitts de Usuário

Para ver alguns twitts de um certo usuário existe o método statuses/user_timeline. A entrada para ele é o ID do usuário.

Como especificado, uma requisição HTTP para esse método se parece com:
http://twitter.com/statuses/user_timeline.xml?user_id=12345

No YQL é tão simples quanto:

select * from twitter.user.timeline where id=12345

Faça o teste YQL Console

twitter

Esse exemplo utilizou a tabela do Twitter que faz parte do projeto DataTables. Projeto que reúne várias tabelas criadas por outros desenvolvedores.

Então é possivel criar novas tabelas!?

É sim! É possivel até mandar o Yahoo fazer screen scraping e retornar o resultado em JSON!

Mais um exemplo!

Digamos que eu quero fazer um widget para retornar alguns produtos do Submarino.

Cumprindo seu papel de maior e-commerce brasileiro o Submarino não dá a minima para os desenvolvedores e não possui até hoje uma API. Vamos “hackear” uma ;D

A busca pelo site é feito pelo seguinte HTTP GET: http://www.submarino.com.br/busca?q=ipod
Usaremos essa URL para buscar dados no Submarino utilizando a YQL.

A primeira parte para fazer o YQL ler documentos é simples:

select * from html where url=”http://www.submarino.com.br/busca?q=ipod”

A segunda é um pouco complicada. Para entender é necessário o minimo de conhecimento sobre XPath.

Para extrair o XPath da parte desejada do conteúdo usei o Firebug, porém é possivel usar o XPather também.

No Firebug use o “Inspect” para encontrar o elemento desejado:
Firebug

Copie o XPath:
xpath

Com o XPath do elemento HTML “pai” do produto temos a chamada YQL completa.

select * from html where url=”http://www.submarino.com.br/busca?q=ipod” and xpath=’/html/body/div/div[2]/div/div/div/div/div/div/div[2]/div/div/div/ul/li/div/a[3]‘

Abra o Console do YQL e teste. Legal! Temos uma busca de produtos no Submarino retornando XML ou JSON e aprendemos a usar o YQL com APIs do Yahoo, com APIs do DataTables e com qualquer documento da Web ;D

Como fazer um widget de ofertas do Submarino com YQL

Outros artigos relacionados ao Yahoo Open Strategy:

Yahoo! Web Services – apresentação das APIs
SearchMonkey Monkey
Yahoo! SearchMonkey



Yahoo! Web Services – apresentação das APIs

23/5/2009 | Tags:, , , , , , , , , | Escrito por: Dirceu Pauka Jr.

No #brhackday08 o foco dos desenvolvedores do Y! já parecia ser o de abrir a sua API.

Parte do Yahoo Open Strategy, SearchMonkey e BOSS foram os destaques das apresentações, mas uma serie de outros serviços da empresa foram apresentados para os desenvolvedores.

API do Yahoo!

O termo é realmente vago para quem nunca olhou a lista de serviços (web services) que a empresa oferece.

Pipes e SearchMonkey são possivelmente os mais conhecidos. Mas outros produtos do Y! para desenvolvedores são interessantes.

Pipes

O serviço permite a criação de mashups entre outras fontes de dados. O fluxo de dados extraídos das paginas pode ser construído usando uma interface visual bem legalzinha.

yahoo pipes screenshot

SearchMonkey

SearchMonkey é a ferramenta que permite modificar o visual da busca do Yahoo!
As informações que aparecerão nos resultados da buscas podem ser extraídas das paginas usando XPath.

Exemplo de resultado da busca com SearchMonkey:

Search Monkey funcionando

BOSS

Busca aberta. API que retorna os resultados da busca do Yahoo! em JSON ou XML.
É possivel facilmente “re-rankear” os resultados antes de mostrar para o usuário adicionando novas regras.

Music

Essa API pouco conhecida abre possibilidades interessantes para criação de serviços em cima.
Ela dá acesso a informações sobre artistas, álbuns, músicas e vídeos e permite buscas com diversos parâmetros.

Weather

API usada pela Apple em sua aplicação “Weather” do iPhone OS.
Além é claro de poder ser usada em gadgets como iPods e Wii, mashups (principalmente com geodata) podem ser criados com essa ferramenta disponibilizada pelo Y!

Todas essas “fontes de dados” do Yahoo! podem ser usadas de uma melhor forma com geodata.

O Google Latitude está em funcionamento a algum tempo e sua proposta (geo location) vai ajudar muito a filtrar o que rola na real-time web.

O Yahoo! não quer ficar de fora.

Yahoo! Placemaker™ Beta

Lançado recentemente em conjunto junto com o Y! Geoplanet™ o Placemaker™ não é uma solução de geo location.

O que ele faz na verdade é buscar em um documento por informações sobre “locais”.

O exemplo dado pela pagina do projeto é ruim de se entender por não enviar um texto e sim o nome exato do local.
Mas esse exemplo é interessante. Não deixe de ver.

O documento de resposta do Yahoo! contem o que é chamado de woeids.

Mas e ai, o que fazer com um “woeid”?

O woeid é na verdade uma identificação numérica referente a um local no GeoPlanet™.

GeoPlanet™

GeoPlanet é outro webservice do Y!. Lançado junto com o Placemaker promete ser útil em todo futuro desenvolvimento com geodata.
A busca por um “woeid” retorna dados específicos sobre o lugar. Dados que são organizados e catalogados pelo Y! e não exigem nenhum grande trabalho para quem for utilizar o serviço.

YQL

A linguagem de buscas YQL (Yahoo Query Language) é uma grande idéia e códigos que facilitam o uso dela já comeram a serem criados.

Conheça outras APIs do Y! (sim, existem mais!) e saiba o que é YQL e como utilizar a linguagem do Yahoo!



Espancando um Web Server com Ruby

19/10/2008 | Tags:, , , , , , | Escrito por: Dirceu Pauka Jr.

Eu resolvi fazer alguns testes com Threads e Timeouts no Ruby hoje e para tal resolvi lidar com algo real: chamadas a APIs.

Problema

O problema todo da escalabilidade acontece por um simples motivo: uma chamada que demora para responder gera uma bola de neve no sistema. E esse problema da bola de neve se repete em várias partes do sistema todo. É isso que ocorre com as querys longas no MySQL ou upload de arquivo que trava uma instância do servidor e deixa todas próximas requisições lentas.

Por isso que é muito mais importante diminuir o tempo das querys longas no MySQL do que qualquer outra coisa. Você provavelmente consegue tirar mais tempo das querys mais longas, certo? Pense no lance de proporção, certo?

Problemas em escalar leitura em MySQL são comuns e por isso as soluções são amplamente conhecidas. Apesar de existir lock para leitura e outras características que machucam muito o trabalho de quem tem que escalar base de dados relacionais, caras como o Flickr fazem um bom uso do MySQL até hoje (sempre com ajuda de cache, lembre-se disso).

Mas e se a fonte de dados da aplicação é algo muito mais lento que um MySQL? E se a fonte de dados é digamos um WebService? Ou no pior dos casos: Web Scraping?

Um WebService ou uma API RESTfull não tem latência controlada como uma query rodando no MySQL local (ou em um cluster de) pode ter. Para começar a falar sobre WebServices eu penso logo em 200ms de latência só para atingir o servidor. E acredite, na maioria das vezes isso é pouco e de qualquer forma é o tempo de resposta que uma query comum no MySQL deve levar.

Além da lentidão você tem que pensar que a coisa toda pode não funcionar. Você pode fazer a requisição HTTP e ela não voltar. É da natureza do HTTP não retornar algumas requisições.

Agora você pensa em centenas de requisições entrando na fila a cada segundo em um sistema que busca os dados em uma API de tempo de resposta médio de 2 segundos. Esqueça a moleza do MySQL. 2 segundos aqui (no meu exemplo de API) é o tempo de resposta mínimo! Ferrou.

Eu já passei por isso. A maneira básica de lidar é cache. Você sabe. Todo mundo sabe…

Mas ainda acontece que as requisições são muito lentas! Muito! O cache não pode ser para sempre e as vezes acontecem coisas que lhe obrigam a limpar todo o cache.

Não importa quantos processos Ruby você abrir (ou quantas requisições/segundo seu Apache sirva em uma maquina de 16 bits), sua aplicação não escala e a culpa é da sua fonte de dados que para piorar é externa e você não pode fazer nada.

Solução

Eu estou planejando cortar muito processos Ruby de uma aplicação em Merb que bate 2MM req/dia com uma solução parecida com a experiência de hoje (que alias se encontra no final do post).

Por enquanto, para manter o serviço no ar a solução foi usar o que havia de melhor na infra: Nginx, Memcached e Thin. Ainda assim eu preciso de 75 instancias do Thin para que tudo funcione (lembre-se: cache não é eterno e requisições sem cache são extremamente lentas).

Como irei mudar o cenário? Quase da mesma forma como espanquei um WebServer a poucas horas com somente um processo Ruby aberto e muito menos banda do que um VPS costuma ter… “Threads”.

Usei Threads de Ruby aqui para a experiência que vou passar, mas para produção recomendo o uso de bibliotecas que irão gerenciar o trabalho. Nanite leva a carga de conhecimento em engenharia que a EngineYard possui, e apesar de não ter uma solução baseada especificamente na questão de threads pode servir para distribuir instâncias do servidor entre várias maquinas numa rede local (lógico que não é – e nem pretende ser – uma implementação do MapReduce). Ainda preciso fazer um bom teste com Nanite e conhecer soluções como NeverBlock. De qualquer forma, essas são boas soluções para o problema de escalabilidade. Pode confiar :)

Abaixo o código (experimental) que derrubou um IIS (inclusive expondo pedaços da aplicação). Ele mostra como eu usei o máximo da banda disponível para fazer tantas requisições em um servidor remoto até ele cair. Isso com somente um processo Ruby.

Em casos comuns o gargalo seria do script Ruby que faz as requisições no outro servidor, no caso desse script eu tranformei o gargalo em banda e na capacidade do outro servidor de me responder. Situação bem melhor do que gargalo na requisição em si.

Ouvi dizer que essa não é a maneira mais fácil de lidar com o problema, certamente no Ruby NeverBlock é uma solução legal. Como eu disse, preciso olhar com atenção.

Boa parte do código serve para estimar o tempo necessário para conclusão da tarefa e para exibir quanto tempo cada Thread está levando.
O ideal é achar um bom número para colocar no método join da Thread. Tente deixar esse número um pouco acima do tempo médio que o servidor remoto (ou serviço externo) responda tranquilo suas requisições sem cair no mesmo problema de bola de neve. O grande segredo do código está nesse valor e você precisa ajustar ele para sua realidade.

Notas da brincadeira:

  • Prefira utilizar Ruby 1.8.6. A documentação do 1.8.7 não está muito real. Principalmente com bibliotecas, onde enfrentei um probleminha com as classes Date e Time no 1.8.7.
  • Cuidado com comparação de Floats (principalmente se tratando de datas), Floats são números muito mais complexos do que parecem.
  • Hadoop