{"id":865,"date":"2012-05-03T20:35:52","date_gmt":"2012-05-03T20:35:52","guid":{"rendered":"http:\/\/leonardocotta.com.br\/blog\/?p=865"},"modified":"2012-05-03T20:35:52","modified_gmt":"2012-05-03T20:35:52","slug":"long-polling-com-node-js","status":"publish","type":"post","link":"https:\/\/leonardocotta.com.br\/?p=865","title":{"rendered":"Long Polling com Node.JS"},"content":{"rendered":"<p style=\"font-family:arial, sans-serif;line-height:normal;background-color:#ffffff;\">Bem, esse \u00e9 um assunto muito interessante e bastante discutido, trata-se de aplica\u00e7\u00f5es em tempo-real ( comet ) , nesse caso uma conex\u00e3o aberta com o servidor aguardando respostas. Enquanto n\u00e3o temos HTML5 em todos navegadores, para poder utilizar os WebSockets, temos que utilizar t\u00e9cnicas Comet para desenvolver aplica\u00e7\u00f5es em\u00a0<strong>realtime<\/strong>, como podemos fazer isso !? Podemos utilizar a t\u00e9cnica Long Polling !<\/p>\n<h2 style=\"font-family:arial, sans-serif;line-height:normal;background-color:#ffffff;\">Long Polling<\/h2>\n<p style=\"font-family:arial, sans-serif;line-height:normal;background-color:#ffffff;padding-left:20px;\">\u00c9 uma t\u00e9cnica comet, que permite que a conex\u00e3o fique aberta, aguardando uma resposta do servidor para esta ser fechada e reaberta novamente, a diferen\u00e7a do AJAX Polling, que fica enviando requisi\u00e7\u00f5es mesmo o servidor respondendo \u2018n\u00e3o tenho nenhuma resposta\u2019\u00a0<strong>consecutivamente<\/strong>, isso acaba prejudicando a aplica\u00e7\u00e3o e criando um excesso de requisi\u00e7\u00f5es no servidor, observa-se uma long polling em a\u00e7\u00e3o com a imagem abaixo\u00a0<a style=\"color:#1155cc;\" href=\"http:\/\/mbiosinformatica.com.br\/blog\/wp-content\/uploads\/2012\/01\/HttpLongPolling.gif\" target=\"_blank\" rel=\"noopener\"><img loading=\"lazy\" decoding=\"async\" title=\"HttpLongPolling\" src=\"http:\/\/mbiosinformatica.com.br\/blog\/wp-content\/uploads\/2012\/01\/HttpLongPolling.gif\" alt=\"\" width=\"339\" height=\"167\" \/><\/a><\/p>\n<h2 style=\"font-family:arial, sans-serif;line-height:normal;background-color:#ffffff;\">Na pr\u00e1tica, o que \u00e9 ?<\/h2>\n<p style=\"font-family:arial, sans-serif;line-height:normal;background-color:#ffffff;padding-left:20px;\">A conex\u00e3o deve ser\u00a0<strong>sempre<\/strong>\u00a0aberta, o que n\u00e3o pode acontecer \u00e9 essa conex\u00e3o ser fechada, e o cliente n\u00e3o tentar reabri-la, da\u00ed n\u00e3o estamos respondendo de volta. Com Node.JS, temos um Daemon ( Disk And Execution Monitor ) , que lida com todas as requisi\u00e7\u00f5es recebidas e enviadas ou seja o servi\u00e7o \u00e9 independente tanto de resposta, quanto de recebimento, nesse caso temos handlers ( manipuladores ) para as requisi\u00e7\u00f5es feitas, e dessa forma, manipulamos essas requisi\u00e7\u00f5es, \u00e9 a\u00ed que entra a m\u00e1gica !<\/p>\n<p style=\"font-family:arial, sans-serif;line-height:normal;background-color:#ffffff;\">Com Node.JS, guardamos todos os manipuladores em um objeto de manipuladores, para que possam ser utilizados de acordo com as requisi\u00e7\u00f5es feitas, logo seguinte trecho de c\u00f3digo<\/p>\n<pre style=\"font-size:11px;line-height:normal;background-color:#ffffff;\">var http = require ( 'http' ) ; \/\/ M\u00f3dulo HTTP do Node.JS ( Nativo )\nvar handlers = new Object ( ) ; \/\/ Objeto de manipuladores<\/pre>\n<p style=\"font-family:arial, sans-serif;line-height:normal;background-color:#ffffff;\">Dessa forma, ficam acess\u00edveis a todos , a vari\u00e1vel \u2018handlers\u2019 \u00e9 global para todo c\u00f3digo portanto, n\u00e3o se preocupem muito com o escopo, al\u00e9m do mais para o que vamos utiliza-la, n\u00e3o teremos esse tipo de problema, vamos criar o servidor HTTP (<span style=\"color:#1155cc;\">http.createServer<\/span>)<\/p>\n<pre style=\"font-size:11px;line-height:normal;background-color:#ffffff;\">var http = require ( 'http' ) ; \/\/ M\u00f3dulo HTTP do Node.JS ( Nativo )\nvar handlers = new Object ( ) ; \/\/ Objeto de manipuladores \n\nvar url = require ( 'url' ) ;\nvar server = http.createServer ( function ( request , response ) { \n\n       response.send = function ( httpCode , data , contentType ) {\n              var $data ;\n              if ( data instanceof Object ) {\n                     $data = new Buffer ( JSON.stringify ( data ) ) ;\n              } else $data = data.toString ( ) ;\n              this.writeHead ( parseInt ( httpCode ) , {\n                     'Content-Type' : contentType.toString ( ) ,\n                     'Content-Length' : $data.length\n              } ) ;\n              this.end ( $data ) ;\n       } ;\n\n       var path = url.parse ( request.url ).pathname.toString ( ) ;\n       if ( path !== null || path !== undefined ) {\n              if ( path in handlers !== false ) {\n                     if ( typeof handlers [ path ] === 'function' ) {\n                            handlers [ path ] ( request , response ) ;\n                     }\n              }\n       }\n} ).listen ( 8080 , function ( ) {\n       console.log ( 'Servidor iniciado !' ) ;\n} ) ;<\/pre>\n<p style=\"font-family:arial, sans-serif;line-height:normal;background-color:#ffffff;\">Vamos precisar do modulo \u2018url\u2019 para fazer o parser de urls, ou seja as urls de requisi\u00e7\u00e3o, \u00e9 necess\u00e1rio o require desse modulo, logo depois, a adi\u00e7\u00e3o de um m\u00e9todo auxiliar para emitir respostas para o cliente, trata-se do m\u00e9todo\u00a0<strong>send<\/strong>\u00a0no objeto response, que emite respostas de acordo com os par\u00e2metros passados,\u00a0<strong>contentType<\/strong>como todos conhecem, o tipo do conte\u00fado que est\u00e1 sendo enviado, o c\u00f3digo http e os dados, logo abaixo o parser do path da url, testando se este n\u00e3o \u00e9 nulo ou indefinido , e testando tamb\u00e9m se este path est\u00e1 registrado como um handler, o que esperamos que seja.<\/p>\n<h2 style=\"font-family:arial, sans-serif;line-height:normal;background-color:#ffffff;\">Importante !<\/h2>\n<p style=\"font-family:arial, sans-serif;line-height:normal;background-color:#ffffff;padding-left:30px;\">Os handlers s\u00e3o mapeados de acordo com o path, o que seria esse path ? \u00e9 exatamente o que estar\u00e1 no lugar de\u00a0<strong>[path]<\/strong>\u00a0em \u2018http:\/\/127.0.0.1:8080\/[path]\u2018.<br \/>\nMas porque o handler \u00e9 mapeado dessa forma !? porque essa \u00e9 a forma mais f\u00e1cil, existem outras, podemos fazer por query string enviando o par\u00e2metro handler com o nome do handler em quest\u00e3o.<\/p>\n<p style=\"font-family:arial, sans-serif;line-height:normal;background-color:#ffffff;\">Se esse handler for uma fun\u00e7\u00e3o, o que esperamos que seja, executamos ele passando os objetos request e response para poder manipular logo, n\u00e3o colocamos um monte de if\u2019s dentro da closure ( fica feio n\u00e3o !? ), vamos adicionar cada handler necess\u00e1rio no objeto de forma diferente ! ent\u00e3o, vamos ter o seguinte c\u00f3digo<\/p>\n<pre style=\"font-size:11px;line-height:normal;background-color:#ffffff;\">var messages = [ ] , callbacks = [ ] ;\n\nhandlers [ '\/message' ] = function ( request , response ) {\n       var message = url.parse ( request.url , true ).query.text ;\n       var $ = {\n              text : message.toString ( ) ,\n              appendMessage : function ( callBack ) {\n                     messages.push ( this ) ;\n                     callBack ( this ) ;\n              }\n       }.appendMessage ( function ( message ) {\n              while ( callbacks.length &gt; 0 ) {\n                     callbacks.shift ( ).callBack ( [ message ] ) ;\n              }\n       } ) ;\n       response.writeHead ( 200 , { } ) ;\n       response.end ( null ) ;\n} ;<\/pre>\n<p style=\"font-family:arial, sans-serif;line-height:normal;background-color:#ffffff;\">Primeira parte feita, criei um array para mensagens ( o que ser\u00e1 armazenado nele ? todas as mensagens que s\u00e3o enviadas, ficam ali ), voc\u00eas podem colocar um setInterval para remover as mensagens a cada 1 minuto, pra n\u00e3o manter um array enorme de mensagens antigas\u00a0<img decoding=\"async\" src=\"http:\/\/mbiosinformatica.com.br\/blog\/wp-includes\/images\/smilies\/icon_razz.gif\" alt=\":P\" \/>\u00a0, ent\u00e3o crio um objeto que representa uma mensagem, nesse objeto, tem o m\u00e9todo appendMessage que \u00e9 executado ap\u00f3s a cria\u00e7\u00e3o do objeto, esse m\u00e9todo adiciona a mensagem na lista\u00a0<strong>(this)<\/strong>\u00a0e executa o callBack passado por par\u00e2metro, essa \u00e9 a parte mais importante para tudo funcionar corretamente<\/p>\n<h2 style=\"font-family:arial, sans-serif;line-height:normal;background-color:#ffffff;\">O callBack .. o que \u00e9, e pra que serve nesse contexto ?<\/h2>\n<p style=\"font-family:arial, sans-serif;line-height:normal;background-color:#ffffff;padding-left:20px;\">O callback \u00e9 quem vai notificar todas as conex\u00f5es abertas, e \u00e9 exatamente por isso que ele \u00e9 executado quando uma mensagem \u00e9 adicionada, se uma mensagem \u00e9 adicionada, algo mudou no servidor, temos ent\u00e3o que notificar aos clientes que esperam esta mudan\u00e7a acontecer, se n\u00e3o notificarmos, o cliente vai ficar com a conex\u00e3o aberta esperando uma resposta que sabemos que n\u00e3o ser\u00e1 enviada. Portanto, chamamos o callback logo ap\u00f3s a mensagem ser adicionada na lista<\/p>\n<p style=\"font-family:arial, sans-serif;line-height:normal;background-color:#ffffff;\">Temos ent\u00e3o, a parte que envia a mensagem, deixando claro aqui tamb\u00e9m, lembre-se de emitir um HTTP-Code 200 para que a conex\u00e3o n\u00e3o fique aberta, por isso emito o header e executo o m\u00e9todo\u00a0<strong>end<\/strong>\u00a0pra enviar a resposta para o requisitante e fechar a conex\u00e3o, agora a parte que faz o polling:<\/p>\n<pre style=\"font-size:11px;line-height:normal;background-color:#ffffff;\">handlers [ '\/polling' ] = function ( request , response ) {\n       if ( messages.length === 0 ) {\n              callbacks.push ( {\n                     callBack : function ( $messages ) {\n                            response.send ( 200 , {\n                                   messages : $messages\n                            } , 'text\/json' ) ;\n                     }\n              } ) ;\n       } else {\n              response.send ( 200 , {\n                     'messages' : messages\n              } , 'text\/json' ) ;\n              messages = [ ] ;\n       }\n} ;<\/pre>\n<p style=\"font-family:arial, sans-serif;line-height:normal;background-color:#ffffff;\">Simples n\u00e3o !? verifico se o array de mensagens \u00e9 vazio, se for adiciono um cliente esperando resposta na lista de callBacks, porque este deve manter a conex\u00e3o, observem tamb\u00e9m que eu N\u00c3O dou fim na requisi\u00e7\u00e3o, a fun\u00e7\u00e3o callBack que \u00e9 adicionada na lista s\u00f3 \u00e9 chamada quando uma mensagem \u00e9 enviada ( algo muda no servidor ) , ent\u00e3o o pr\u00f3prio servidor j\u00e1 vai notificar quando houver novas mensagens, caso o array j\u00e1 tenha mensagens, respondo a requisi\u00e7\u00e3o e mando as mensagens que est\u00e3o no array, logo ap\u00f3s limpo a lista de mensagens, no lado do cliente ter\u00edamos algo semelhante ao seguinte fragmento de c\u00f3digo<\/p>\n<pre style=\"font-size:11px;line-height:normal;background-color:#ffffff;\">$ ( document ).ready ( function ( ) {\n       function startPolling ( data ) {\n              if ( data instanceof Object ) {\n                     for ( var i in data.messages ) {\n                            \/\/ ... exibe as mensagens\n                     }\n              }\n              $.ajax ( {\n                     url : 'http:\/\/127.0.0.1:8080\/polling' ,\n                     method : 'GET' ,\n                     success : function ( data ) {\n                            startPolling ( data ) ;\n                     }\n              } ) ;\n       }\n} ) ;<\/pre>\n<p style=\"font-family:arial, sans-serif;line-height:normal;background-color:#ffffff;\">O que vai acontecer ? vamos requisitar novas mensagens quando houver, essas mensagens ser\u00e3o mostradas e vamos requisitar de novo procurando por novas, s\u00f3 que a conex\u00e3o vai ficar aberta, at\u00e9 termos novas mensagens e enquanto a conex\u00e3o estiver aberta uma nova n\u00e3o \u00e9 criada, no console do google chrome aba network, mostra que n\u00e3o estamos trafegando dado algum, mas a requisi\u00e7\u00e3o est\u00e1 pendente ou seja, esperando uma resposta<br \/>\n<a style=\"color:#1155cc;\" href=\"http:\/\/mbiosinformatica.com.br\/blog\/wp-content\/uploads\/2012\/01\/cb6fc103ca9e45558368a54.png\" target=\"_blank\" rel=\"noopener\"><img loading=\"lazy\" decoding=\"async\" title=\"cb6fc103ca9e45558368a54\" src=\"http:\/\/mbiosinformatica.com.br\/blog\/wp-content\/uploads\/2012\/01\/cb6fc103ca9e45558368a54.png\" alt=\"\" width=\"688\" height=\"122\" \/><\/a><\/p>\n<h2 style=\"font-family:arial, sans-serif;line-height:normal;background-color:#ffffff;\">Demonstra\u00e7\u00e3o r\u00e1pida com cURL<\/h2>\n<p style=\"font-family:arial, sans-serif;line-height:normal;background-color:#ffffff;\">Uma breve demonstra\u00e7\u00e3o disto em a\u00e7\u00e3o, pode ser feita utilizando cURL, vou abrir 4 janelas do prompt-de-comando, em tr\u00eas delas, eu vou iniciar o polling, e na \u00faltima, vou enviar uma mensagem, observem o resultado<\/p>\n<pre style=\"font-size:11px;line-height:normal;background-color:#ffffff;\">-- Janela 1\nC:&gt;curl http:\/\/127.0.0.1:8080\/polling\n{\"messages\":[{\"text\":\"Hello!\"}]}\n-- Janela 2\nC:&gt;curl http:\/\/127.0.0.1:8080\/polling\n{\"messages\":[{\"text\":\"Hello!\"}]}\n-- Janela 3\nC:&gt;curl http:\/\/127.0.0.1:8080\/polling\n{\"messages\":[{\"text\":\"Hello!\"}]}\n-- Janela 4\nC:&gt;curl http:\/\/127.0.0.1:8080\/message?text=Hello!<\/pre>\n<p style=\"font-family:arial, sans-serif;line-height:normal;background-color:#ffffff;\">Bem legal n\u00e3o \u00e9 !? e voc\u00ea, sabe outra t\u00e9cnica ? alguma dica para melhorar o long-polling ? apresente-se !<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Bem, esse \u00e9 um assunto muito interessante e bastante discutido, trata-se de aplica\u00e7\u00f5es em tempo-real ( comet ) , nesse caso uma conex\u00e3o aberta com o servidor aguardando respostas. Enquanto n\u00e3o temos HTML5 em todos navegadores, para poder utilizar os WebSockets, temos que utilizar t\u00e9cnicas Comet para desenvolver aplica\u00e7\u00f5es em\u00a0realtime, como podemos fazer isso !? &hellip; <a href=\"https:\/\/leonardocotta.com.br\/?p=865\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Long Polling com Node.JS<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[33],"tags":[],"class_list":["post-865","post","type-post","status-publish","format-standard","hentry","category-ajax"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/leonardocotta.com.br\/index.php?rest_route=\/wp\/v2\/posts\/865","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/leonardocotta.com.br\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/leonardocotta.com.br\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/leonardocotta.com.br\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/leonardocotta.com.br\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=865"}],"version-history":[{"count":0,"href":"https:\/\/leonardocotta.com.br\/index.php?rest_route=\/wp\/v2\/posts\/865\/revisions"}],"wp:attachment":[{"href":"https:\/\/leonardocotta.com.br\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=865"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/leonardocotta.com.br\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=865"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/leonardocotta.com.br\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=865"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}