Nesse artigo vamos visitar um padrão de codificação para troca de mensagens que praticamente ninguém conhece: XML Fast Infoset. Entenderemos o seu uso, veremos um exemplo bem simples através do framework Apache CXF e o mais importante, que tipo de ganhos ele pretende oferecer tanto no processamento de XML quanto na troca de mensagens entre Web Services.
Figura 1: O antes e depois do uso de Fast Infoset
Antes de mergulharmos no XML Fast Infoset, devemos entender o que é XML Infoset. O XML Information Set simplesmente define um modelo de dados para o XML. Esse modelo de dados é um conjunto de abstrações que detalham as propriedades de árvores XML. Conhecemos outros modelos de dados em árvore que representam XML: DOM (document object model) ou Xpath Data Model.
A vantagem de termos um único modelo de dados compatível com todas as APIs XML é obvia: elas não precisam definir nenhum modelo novo. Além disso, o XML Infoset formaliza a extração de dados de um XML bem formado para as APIs que são compatíveis. Algumas tecnologias são descritas em termos XML Infoset como SOAP 1.2, XML Schema e Xquery.
O XML Fast Infoset (FI) ou formato X.891 nada mais é do que um padrão internacional que especifica um formato binário de codificação de XML baseado na árvore do XML Infoset.
O FI promete dois ganhos fundamentais: velocidade no parsing e diminuição drástica do tamanho do XML. Quando levamos esses ganhos para a troca de mensagens em Web Service, com milhares ou milhões de requisições, podemos obter uma redução considerável do custo de um projeto em termos principalmente de infra-estrutura. Obviamente tanto cliente quanto WebService devem suportar esse padrão para que a comunicação utilizando o formato FI seja estabelecida com sucesso.
Nesse artigo, utilizamos o framework Apache CXF que provê um suporte completo ao FI tanto para os clientes quanto na definição dos WebServices. Devemos antes de mais nada adicionar o jar do fastinfoset ao nosso projeto maven:
Listagem 1: Adicionando FI ao projeto maven
<dependency>
<groupId>com.sun.xml.fastinfoset</groupId>
<artifactId>FastInfoset</artifactId>
<version>1.2.9</version>
</dependency>
Vamos agora examinar nosso WebService minimalista na Listagem 2.
Listagem 2: Definição de HelloServiceOptimized
package br.com.devmedia.helloworldservice;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.ws.soap.Addressing;
import org.apache.cxf.annotations.FastInfoset;
@WebService(targetNamespace = HelloServiceOptimized.NS)
@Addressing(enabled = true, required = true)
@org.apache.cxf.interceptor.InInterceptors(interceptors = { "org.apache.cxf.interceptor.LoggingInInterceptor" })
@org.apache.cxf.interceptor.OutInterceptors(interceptors = { "org.apache.cxf.interceptor.LoggingOutInterceptor" })
@FastInfoset
public interface HelloServiceOptimized {
String NS = "http://devmedia.sayhello";
@WebResult(name = "response")
public abstract String digaOla(String user) ;
}
Até aqui nenhuma grande novidade a não ser uma anotação nova fornecida pelo framework: “@FastInfoset”. Ao adicionarmos essa anotação específica, informamos ao Apache CXF que o nosso serviço suporta o FI. Fácil não? Uma outra forma de se habilitar o FI é através de uma configuração específica no BUS. Vale lembrar que o objeto BUS é aquele que controla e configura os serviços da nossa aplicação. Vamos ver como fica isso na Listagem 3.
Listagem 3: Trecho de Configuração Spring habilitando o FI
<cxf:bus name="serverBusOptimized">
<cxf:features>
<wsa:addressing/>
<cxf:fastinfoset/>
</cxf:features>
</cxf:bus>
<jaxws:endpoint
id="server"
address="http://localhost:9293/hello"
implementor="br.com.devmedia.helloworldservice.SimpleOptimizedHelloService"
bus="serverBusOptimized"
>
<jaxws:properties>
<entry key="schema-validation-enabled" value="true" />
</jaxws:properties>
</jaxws:endpoint>
Um fato importante sobre o uso do FI é que ele pode ser ligado e desligado também do lado do cliente. Isso quer dizer que o cliente toma a decisão se quer ou não utilizar a otimização e o framework Apache CXF se encarrega de gerenciar a troca de mensagens para nós. Para que um cliente ligue o FI, ele deve enviar no cabeçalho HTTP da requisição a seguinte informação: Accept: application/fastinfoset, */* na primeira requisição. Isso iniciará a negociação entre cliente e servidor. A resposta retornada pelo servidor pode ser codificada se ele suportar o padrão ou pode ser um XML convencional em caso contrário.
A partir dai toda a conversação entre esse cliente o serviço já serão codificadas e otimizadas. Vamos ver na listagem 4 como é uma mensagem em XML codificado. Na listagem 5 vemos o mesmo conteúdo mas sem otimização. Reparem de cara a diferença no tamanho do payload.
Listagem 4: XML codificado de acordo com o FI
à####8Ï#soap(http://schemas.xmlsoap.org/soap/envelope/????#Envelope???#Header8Í#http://www.w3.org/2005/08/addressing?=?#Action?;http://devmedia.sayhello/HelloServiceOptimized/digaOlaResponse?8Í??=?#MessageID?*urn:uuid:41276479-646f-46b7-a7b4-dcb5a788b5d8?8Í??=?#To?+http://www.w3.org/2005/08/addressing/anonymous?8Í??=?#RelatesTo?*urn:uuid:cd9d837d-244a-4184-a94c-7f7e115cd03fÿ???#Body8Ï#ns2#http://devmedia.sayhello????#digaOlaResponse<#response?Hello Tester2ÿÿ??
Listagem 5: XML texto plano não otimizado
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Header><Action xmlns="http://www.w3.org/2005/08/addressing">http://devmedia.sayhello/HelloServiceOptimized/digaOlaResponse</Action><MessageID xmlns="http://www.w3.org/2005/08/addressing">urn:uuid:6d1b62cd-62e4-41b2-9d36-579d415b975b</MessageID><To xmlns="http://www.w3.org/2005/08/addressing">http://www.w3.org/2005/08/addressing/anonymous</To><RelatesTo xmlns="http://www.w3.org/2005/08/addressing">urn:uuid:bd37caef-3a92-44ee-adcf-4c90a9fe0adf</RelatesTo></soap:Header><soap:Body><ns2:digaOlaResponse xmlns:ns2="http://devmedia.sayhello"><response>Hello Tester3</response></ns2:digaOlaResponse></soap:Body></soap:Envelope>
Para completar a nossa experiência, vamos realizar um teste de carga muito simples utilizando a ferramenta SOAP UI (http://www.soapui.org/Load-Testing/loadtest-execution.html) . Foram testados dois cenários: o primeiro com FI ligado no cliente e serviço e o segundo com FI desligado. Nos dois cenários foram mandadas mensagens por vários clientes simultâneos durante 1 minuto. O relatórios podem ser vistos respectivamente nas figuras 2 e 3.
Figura 2: Cenário de teste com FI ligado
Figura 3: Cenário de teste com FI desligado
Reparem que na coluna avg (tempo médio em millisegundos), temos um ganho de aproximadamente 20% utilizando o FI. Também o nosso throughput é ligeiramente maior (6,48). Agora o que realmente chama a atenção é a quantidade de bytes trafegado: com FI fazemos uma média de 3214 bytes/segundo enquanto que sem FI chegamos a 4847 bytes/segundo. É um ganho muito bom comparando o gasto que tivemos para implementar a otimização!
Pessoal, com esse artigo conseguimos de uma maneira muito simples entender o que é XML Infoset, XML Fast Infoset e sua aplicação prática. Não se esqueçam de dar uma olhada no projeto completo em https://github.com/leogsilva/CxfFastInfoset.git. Até a próxima.