• Nenhum resultado encontrado

TEMA DE CAPA

TEMA DA CAPA

Introdução

Neste artigo vamos estudar uma solução para o desenvolvi- mento de aplicações web mais fluídas recorrendo ao Local Storage. Esta tecnologia possibilita a persistência de dados no browser, o que permite a implementação de aplicações web que respondem visualmente de forma imediata – uma caraterística que sempre foi possível em aplicações nativas, mas que a web apenas recentemente começou a suportar. No decorrer deste artigo introduzimos a utilização do Local Storage e analisamos em maior detalhe a técnica utilizada para tirar partido da potencialidade da tecnologia. Finalmen- te, é feita a apresentação dos resultados de desempenho conseguidos, capturados recorrendo à plataforma de nome AnyKB desenvolvida como prova de conceito para esse ob- jectivo.

Buzzword: HTML5

O HTML5, para além de ser uma especificação, é uma buzzword que engloba todas as tecnologias emergentes que estão a revolucionar a Web. Apesar do nome, algumas des- tas tecnologias não estão relacionadas com a linguagem de marcação, mas tratam-se de APIs (Application Programming Interface) JavaScript que permitem a implementação de fun- cionalidades inovadoras: este é o caso do Local Storage. A especificação W3C “Web Storage” [1] define esta API do seguinte modo:

“This specification defines an API for persistent data storage of key-value pair data in Web clients.

Na prática, Local Storage é uma API JavaScript que permite guardar de forma persistente dados no formato chave-valor. Isto é, os dados são mantidos no browser mesmo após o utilizador fechar a página web.

O principio básico do Local Storage é idêntico ao dos coo- kies, mas de utilização simplificada e com limites de espaço mais generosos. De momento é possível guardar cerca de 5MB por domínio ou subdomínio, estando o acesso aos da- dos limitado igualmente ao domínio ou subdomínio origina- dor dos mesmos.

Apesar de recente, o suporte a esta tecnologia pelos browsers mais populares é bastante boa. O Local Storage é suportado em todas as actuais versões dos browsers Chro- me, Firefox, Internet Explorer, Safari e Opera, e ainda pelas versões móveis iOS Safari, Android Browser e Blackberry Browser [1].

Web Persistente: Local Storage

Introdução ao Local Storage

Aceder ao Local Storage através de JavaScript é bastante simples e directo. A especificação define a interface localSto- rage que contém os métodos setItem e getItem para guardar e obter dados, respectivamente. Vejamos o exemplo abaixo.

Listagem 1: Exemplo de acesso ao interface localStorage.

Infelizmente, a persistência de objectos não é nativamente suportada, para tal é necessário serialização e desserializa- ção dos objectos. Uma solução muito popular passa pela tradução dos objectos para JSON (JavaScript Object Notati- on) - uma notação para intercâmbio de dados derivada do JavaScript. Este processo é suportado pelo objecto JSON utilizando as funções stringify e parse para serializar e des- serializar, respectivamente. Vejamos o exemplo abaixo, onde aproveitámos para estender os métodos setItem e getItem para suportar a persistência de objectos.

//Guardar

localStorage.setItem("chave", "valor");

//Obter

var valor = localStorage.getItem("chave");

//Guardar referência às funções originais

Storage.prototype._setItem = Storage.prototype.setItem; Storage.prototype._getItem = Storage.prototype.getItem;

//Override da função setItem

Storage.prototype.setItem = function (key, value) {

//Se for um objecto, serializar

if (value instanceof Object) { value = JSON.stringify(value); }

//Guardar

this._setItem(key, value); }

/* Override da função getItem

* O parâmetro isObject é utilizado para * indicar se o valor a devolver é um * objecto

*/

Storage.prototype.getItem = function (key, isObject) {

//Obter

var item = this._getItem(key); //Se for um objecto, desserializar

if (isObject) { item = JSON.parse(item); } return item; } //Guardar um objecto

var obj = { foo: "bar" };

TEMA DA CAPA



Listagem 2: Persistência de objectos serializados em Local Storage.

Conseguimos assim uma interface de armazenamento per- sistente capaz de suportar não só tipos de dados primitivos mas também objectos.

De seguida analisamos o contexto em que pretendemos aplicar esta tecnologia.

O problema da Web não-persistente

As aplicações web executadas no browser têm como carac- terística a completa dependência de um servidor que fornece toda a lógica da aplicação. Isto significa que em cada acesso o browser tem de descarregar todos os elementos da aplica- ção, independentemente de qualquer acesso anterior. Esta redundância de dados que circulam na rede agrava-se ao verificarmos que a componente visual (HTML, CSS & JavaS- cript) não serve qualquer interesse ao servidor. O ideal seria persistir estes elementos no browser no primeiro acesso, eliminando a necessidade de nova transferência em acessos posteriores.

Os sistemas de cache implementados pelos browsers assis- tem ao guardar localmente alguns elementos, no entanto apenas reduzem a quantidade de informação trocada, não eliminando por completo a transferência da componente vi- sual da aplicação. Adicionalmente, não existe uma interface programática que permita a gestão deste recurso.

A utilização de AJAX (Asynchronous Javascript and XML) é outra técnica que permite a redução da quantidade de dados transferidos ao possibilitar ligações assíncronas ao servidor. É possível utilizar esta técnica para, por exemplo, descarre- gar a componente visual da aplicação na abertura da página web, e posteriormente utilizar ligações AJAX para obter ape- nas o resultado do servidor. No entanto introduz-se um novo problema: cada nova abertura da aplicação torna-se mais lenta e pesada. Para além disso, o problema inicial mantém- se pois continuamos a depender do servidor para fornecer a componente visual da aplicação em cada novo acesso. O Local Storage vem oferecer uma solução mais completa ao permitir que o programador persista no browser todos os elementos de apresentação da aplicação, eliminando com- pletamente a transferência desta componente a partir do servidor. Exceptuam-se apenas dois casos incontornáveis: quando ainda não foi persistida e quando existem actualiza- ções. Pode ser feita uma analogia a aplicações nativas: o primeiro acesso corresponde à “instalação”, existindo “actualizações” posteriores apenas quando necessário. Após “instalada”, a aplicação comunica com o servidor utilizando AJAX, consistindo a comunicação apenas da informação

relevante ao pedido do utilizador, geralmente estruturada recorrendo a JSON ou XML.

Figura 1: Diagrama da interacção entre browser e servidor utilizan-

do Local Storage para persistência da componente visual da aplica- ção.

Exemplo de Implementação

Como demonstração concreta deste conceito vamos imple- mentar uma aplicação simples, recorrendo à linguagem PHP para a implementação de um webservice.

O seguinte diagrama auxilia na contextualização dos fichei- ros que compõem este exemplo, indicando a posição final dos mesmos após a primeira abertura da plataforma pelo browser.

Figura 2: Contextualização dos ficheiros que compõem o exemplo de implementação.

Todo o código de seguida apresentado foi cuidadosamente documentado para melhor compreensão.

Ficheiro index.html

WEB PERSISTENTE: LOCAL STORAGE //Obter um objecto

var obj =

localStorage.getItem("chave", true);

<!DOCTYPE html> <html>

<head>

<title>Aplicação Exemplo</title>

<meta http-equiv="Content-Type"

content="text/html; charset=UTF-8">

<!-- Carregar jQuery -->

<script src="http://code.jquery.com/

TEMA DA CAPA



Listagem 3: Ficheiro index.html. Ponto de entrada para a aplicação.

Ficheiro html_index.html

Listagem 4: Ficheiro html_index.html. Conteúdo da página principal da aplicação.

Ficheiro html_pagina2.html

Listagem 5: Ficheiro html_pagina2.html. Conteúdo da segunda pági- na da aplicação.

Ficheiro styles.css

Listagem 6: Ficheiro styles.css. Folha de estilos da aplicação

Ficheiro api.php

Listagem 7: Ficheiro api.php. Webservice fornecedor do resultado da lógica da aplicação.

Ficheiro script.js

WEB PERSISTENTE: LOCAL STORAGE

</script>

<!-- Carregar script com a lógica de apresentação da aplicação -->

<script src="script.js"></script>

<style type="text/css"></style> </head> <body> </body> </html> <div> <p> Página de inicio.

<a href="pagina2">Ir para a Página 2.</a> </p>

</div> <div>

<p>

Página 2.

<a href="index">Voltar ao início.</a> </p>

<p id="serverContent"></p> </div>

<script type="text/javascript">

/**

Exemplo de pedido de recursos ao servidor através de AJAX.

O resultado é carregado para o parágrafo de ID "serverContent".

**/

$("#serverContent").load("api.php?getExampl

eText=1"); </script> body { background-color: #262626; } div { padding: 10pt; margin: 0 auto; width: 800pt; text-align: center; background-color: white; } p {

border: solid 2px orange;

padding: 5pt;

}

<?php

/**

Pedido de template (componente visual da aplicação)

**/

if($_GET["getTemplate"] == "1") {

//Construir um array com os elementos

visuais $result = array ( "version" => "0.1", //Páginas HTML "html_index" => file_get_contents("html_index.html"), "html_pagina2" => file_get_contents("html_pagina2.html"), //Folhas de estilo "css_styles" => file_get_contents("styles.css") );

//Traduzir o array para JSON e enviar //como resposta

echo json_encode($result); //Parar

die(); }

/**

Exemplo de pedido de um determinado recurso

**/

if($_GET["getExampleText"] == "1") {

//Responder com texto demonstrativo

echo "Este texto é o resultado de um pedido AJAX ao servidor.";

//Parar

die(); }

?>

//Quando o documento estiver pronto...

$(document).ready(function() {

captureLinkClick(); captureOnPopState();

//Se o template já existe no browser...

if(templateExists()) { //... mostrar aplicação showApplication(); } else {

//Senão, fazer download do template

getTemplate(function() {

//Quando concluído, mostrar

aplicação

showApplication(); });

} });

TEMA DA CAPA



WEB PERSISTENTE: LOCAL STORAGE Capturar click em links

Esta acção irá ter um comportamento diferente do normal @method captureLinkClick

**/

function captureLinkClick() {

//Ao clicar num link...

$(document).on("click", "a", function(e)

{

//...impedir o comportamento por defeito

e.preventDefault();

//Mudar para a página definida no attributo "href" do link changePage($(e.currentTarget).attr("href"), false); }); } /**

Capturar eventos retroceder e avançar do histórico do browser@method captureOnPopState

**/

function captureOnPopState() {

//Ao retroceder/avançar...

window.onpopstate = function(e) {

var pageName = "";

//Obter objecto state que contém o nome da página destino

var state = e.state; //Caso state seja null...

if(state == null) {

//...é o primeiro acesso à página.

//Mostrar página principal (index)

pageName = "index"; }

else

{

//Senão, mostrar página indicada no objecto state

pageName = state.page; }

//Mudar para a página destino

changePage(pageName, true); }

}

/**

Verificar se o template existe no browser @method templateExists

**/

function templateExists() {

//Neste caso verificamos a existência do

parâmetro "version" como teste da presença do template

return

localStorage.getItem("version") !== null;

//É possível aproveitar este parâmetro para //verificar se existe uma nova versão do template //no servidor

}

/**

Obter template e guardar em Local Storage @method getTemplate

@param {function} callback Função chamada após ter- minado o download do template

**/

function getTemplate(callback) {

//Fazer pedido ao servidor pelo

template $.ajax( {

url: "api.php?getTemplate=1", type: "get",

success: function(data) {

//Resposta JSON

//Fazer parse do resultado do servidor

var jsonData = $.parseJSON(data); //Iterar os elementos enviados pelo //servidor

$.each(jsonData, function(key, value) {

//Guardar em Local Storage

localStorage.setItem(key, value); });

//Chamar função callback

callback(); }

}); }

/**

Carregar pela primeira vez os elementos visuais para o ecrã

@method showApplication **/

function showApplication() {

//Carregar folha de estilos do template

$("style").append(localStorage.getItem ("css_styles"));

//Mostrar página inicial (index)

changePage("index", true); }

/**

Carregar uma determinada página guardada em localS- torage

@method changePage

@param {String} pageName Nome da página a carregar @param {boolean} replaceState Indica se deve ser criado ou reutilizado um estado do histórico **/

function changePage(pageName, replaceState) {

//Carregar para o corpo do documento o conteúdo presente em Local Storage correspondente à pagina indicada

$("body").html(localStorage.getItem

("html_+pageName));

//Construir objecto de estado stateObject, que indica o nome da página destino

var stateObject = {page: pageName};

//Se foi indicado que o estado deve ser //substituido...

if(replaceState) {

//...substituir o estado do histórico //utilizando o objecto de estado stateObject

history.replaceState(stateObject, null, pageName);

} else

{

//Em caso contário, criar um novo estado do histórico utilizando o objecto de estado

TEMA DA CAPA



Listagem 8: Ficheiro script.js. Lógica de apresentação da aplicação.

Caso prático: Plataforma AnyKB

AnyKB é uma plataforma web colaborativa para a partilha de conhecimento no formato de artigos criados pela comunidade, vocacionada para a área tecnológica. A plataforma surge da necessidade de implementar o conceito descrito neste artigo numa aplicação de utilidade real com o objectivo de gerar re- sultados num ambiente de utilização o mais intensivo possível.

Figura 3: Página inicial da plataforma AnyKB.

Na plataforma AnyKB, qualquer utilizador pode partilhar com a comunidade as suas descobertas, experiências e soluções, ou ainda melhorar o conteúdo já existente. A participação é re- compensada na forma de pontos que reflectem a reputação do utilizador dentro da plataforma e a qualidade dos conteúdos que cria. O utilizador enriquece a comunidade com o seu co- nhecimento e é recompensado com a aprovação desta, num formato quantitativo que destaca o utilizador entre os profissio- nais da sua área.

Deixo assim o convite para visitar e experimentar o desempe- nho da plataforma em http://www.anykb.net e contribuir para a comunidade com os seus artigos!

Resultados Estatísticos

Por forma a estudar a escalabilidade da aplicação foram com- paradas duas implementações equivalentes do mesmo projec- to. A versão “Proof of Concept” (PoC) implementa o conceito detalhado neste documento, e a versão “Clássica” funciona como a maioria dos websites de hoje: cada página correspon- de a um pedido novo (GET) ao servidor.

O desempenho das duas versões foi comparado ao criar um caso de teste que consiste na navegação pelas várias páginas da aplicação, de forma a tirar partido dos benefícios que am- bas as abordagens têm ao seu dispor. A métrica utilizada cor- responde ao tempo decorrido desde a acção do utilizador (clique num link, por exemplo) até ao carregamento completo da página pelo browser. Adicionalmente, o caso de teste foi executado em vários cenários de carga do servidor, por forma a analisar como este factor afecta o desempenho de ambas as versões. Os valores de CPU Load apresentados correspondem

ao formato conhecido como Unix CPU Load [2], tendo sido o servidor levado ao limiar da capacidade de resposta.

O servidor utilizado é uma máquina virtualizada recorrendo ao software para virtualização Xen [4], assente em infra- estrutura escalável de última geração, recorrentemente refe- renciada como Cloud. O servidor apresenta as seguintes características:

CPU: Quad-core Intel® Xeon® CPU E5649 @ 2.53GHz ou equivalente

RAM: 1.5GB DDR3 (1033Mhz) Disco: 25GB (10K)

Software utilizado:

CentOS Linux 2.6.32 (64-bit) Apache 2.2.15 (64-bit) PHP 5.3.3 (64-bit) MySQL 14.14 (64-bit) Google Chrome 28 (64-bit)

Para simular, de forma controlada, os vários cenários de carga no servidor foi utilizado um script que executa, sem objectivo, operações pesadas de I/O e CPU.

O gráfico abaixo mostra, em média e mediana, o speedup conseguido pela versão PoC relativamente à versão Clássi- ca, ao longo dos vários cenário de carga. É possível verificar que, em condições normais, a versão PoC consegue um desempenho cerca de 5 vezes superior. O desempenho bai- xa para cerca de 2 a 3 vezes superior à medida que a carga do servidor aumenta. No entanto, a mediana mostra-nos a tendência que a versão PoC tem para um desempenho mais elevado, conseguindo resultados até cerca de 24 vezes su- periores.

Gráfico 1: Speedup médio e mediano da versão PoC, comparativa-

mente à versão Clássica.

WEB PERSISTENTE: LOCAL STORAGE

history.pushState(stateObject, null, page- Name);

} }

TEMA DA CAPA



O seguinte gráfico mostra a evolução do tempo médio e media- no do carregamento das páginas da aplicação, ao longo dos vários cenários de carga. Como esperado, é possível verificar que o valor médio e mediano da versão clássica cresce à medi- da que a carga do servidor aumenta. O valor médio da versão PoC cresce igualmente com a carga do servidor mas mostra uma curva mais suave, enquanto que a mediana da versão PoC se mantém estável em cerca de 78ms. Isto acontece por- que existe uma compensação do tempo acrescido que a infor- mação demora a chegar do servidor pela instantaneidade de reposta do interface, e pela redução substancial de dados envi- ados pelo servidor.

Gráfico 2: Tempo médio e mediano (em milissegundos) do carrega- mento de várias páginas de ambas as versões.

Conclusão

Concluímos assim que a separação da lógica da apresentação da restante lógica da aplicação, fazendo recurso ao Local Sto- rage, possibilita ganhos de desempenho cerca de 5 vezes su- perior, comparativamente à mesma aplicação servida de forma clássica. Em casos extremos de carga o valor médio do de- sempenho baixa para 2 a 3 vezes superior, enquanto que o valor mediano mostra uma capacidade 24 vezes superior de resposta, colocando em evidencia a tendência para a fluidez, e não o contrário.

A implementação deste conceito possibilita assim a criação de aplicações mais rápidas e fluidas, fornecendo uma experiencia mais agradável ao utilizador.

WEB PERSISTENTE: LOCAL STORAGE

Referências

[1] World Wide Web Consortium (W3C), “W3C Proposed Recommendation 9 April 2013,” [Online]. Available: http:// www.w3.org/TR/webstorage/. [Acedido em Julho 2013]. [2] A. Deveria, “Can I use... Support tables for HTML5, CSS3, etc,” [Online]. Available: http://caniuse.com/#feat=namevalue -storage. [Acedido em Junho 2013].

[3] Linux Kernel Organization, Inc., “The Linux Kernel Archi- ves,” [Online]. Available: https://www.kernel.org/doc/ Documentation/cpu-load.txt. [Acedido em Junho 2013]. [4] Xen Project, “The Xen Project, the powerful open source industry standard for virtualization,” [Online]. Available: http:// www.xenproject.org/. [Acedido em Julho 2013].

O Local Storage

vem oferecer uma solu-

ção mais completa ao

permitir que o progra-

mador

persista

no

browser todos os ele-

mentos de apresenta-

ção da aplicação, elimi-

nando completamente

a transferência desta

componente a partir do

servidor.

AUTOR

Escrito por Telmo Marques

Estudantelicenciadoemeng.ªinformáƟcacomumafectoespecialportecnologiasweb,encontraͲseatualmentea concluiroMestradoemComputaçãoMóvel(MEIͲCM)doInsƟtutoPolitécnicodeLeiria(ESTGͲIPLeiria),soborienͲ taçãodoprofessorPatrícioDomingues,noâmbitodoqualseencontrainseridoestearƟgo.ProÞssionalmente, apósumacurtapassagempeloInsƟtutodeTelecomunicaçõescomoinvesƟgador,éatualmenteoresͲ ponsávelpelodesenvolvimentodesoŌwarenumaempresaportuguesadeserviçosweb.

Documentos relacionados