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.

O antes e depois do uso de Fast Infoset

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.

Cenário de teste com FI ligado

Figura 2: Cenário de teste com FI ligado

Cenário de teste com FI desligado

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.