Pontos Principais
- O uso de caches de dados é uma necessidade cada vez mais presentes nos sistemas modernos.
- Soluções Open Source se mostram sólidas e atendem às reais necessidades dos sistemas
- Configure de forma simples o JBoss Fuse para se comunicar com um cache remoto.
- As operações básicas necessárias para trabalhar com o cache remoto.
- Entenda o ciclo de vida de uma chave no cache remoto.
Atualmente a realidade das empresas exige cada vez mais que diversos sistemas usando diferentes tecnologias trabalhem em conjunto trocando e processando informações. Neste ecossistema o uso de uma plataforma de integração é muito importante, porém, muitas vezes o uso de um banco "In-memory" é necessário para um ganho significativo na performance no processamento de informações. Neste artigo iremos demonstrar o uso da plataforma de integração Red Hat Fuse (mais conhecido como JBoss Fuse) em conjunto com o banco "In-memory" Red Hat Data Grid.
O Red Hat Fuse, ou simplesmente JBoss Fuse é uma plataforma de integração baseada no framework Apache Camel, focada na mediação, transformação e roteamento de dados entre várias aplicações, serviços e dispositivos.
O Red Hat Data Grid ou simplesmente datagrid é uma solução "In-memory" de gerenciamento de dados no formato chave-valor que é baseado no produto open source Infinispan. O Red Hat Data Grid pode ser utilizado como uma simples solução de cache, um banco de dados NoSql ou mesmo um event Broker. Podemos assumir então que o Red Hat Data Grid é a versão enterprise do produto e o Infinispan é a versão community. Este artigo aborda como o JBoss Fuse pode ser utilizado como solução de middleware para comunicação com o Red Hat Data Grid, estando este configurado como um servidor de cache remoto.
Por ser baseado no Apache Camel, o JBoss Fuse utiliza o componente camel-infinispan para se conectar ao Red Hat Data Grid. Este componente Camel é utilizado para ambas as versões: enterprise e community.
Nas documentações das versões 6.3 e 7.1 do JBoss Fuse podemos encontrar referências a um componente chamado camel-jbossdatagrid. Este componente deixou de ser suportado pela Red Hat, sendo substituído pelo componente camel-infinispan nas versões mais atuais do JBoss Fuse.
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-infinispan</artifactId>
<version>${camel.version}</version>
</dependency>
Para demonstrar a integração entre JBoss Fuse com o Red Hat Data Grid, criamos um projeto quickstart e neste artigo detalhamos como foi feita essa integração. O fonte do projeto está disponível no github pela url https://git.io/Je1J2.
Para este projeto foi utilizada a versão 7.4 do JBoss Fuse Standalone que usa o Apache Camel 2.21 e Red Hat Data Grid 7.3 equivalente ao Infinispan 9.4.
Além do modo standalone o JBoss Fuse pode ser executado no EAP, KARAF e Openshift. Todos os conceitos aplicados aqui são compatíveis com qualquer modo de uso.
Primeiros passos
Para conectarmos ao Red Hat Data Grid, precisamos inicialmente criar uma instância de um objeto do tipo RemoteCacheManager
. Ele é o responsável por manter as configurações para acesso ao servidor remoto e gerenciar a comunicação. O componente Apache Camel utilizará este objeto sempre que uma operação for solicitada ao servidor remoto.
No exemplo abaixo podemos visualizar a implementação do RemoteCacheManager
configurado no nosso projeto de exemplo. O RemoteCacheManager é basicamente um bean que utiliza a classe ConfigurationBuilder
para armazenar as configurações do Red Hat Data Grid.
@Component
public class RemoteCacheConfig {
private String rhdgHosts = "127.0.0.1:11222";
private Integer socketTimeout = 5000;
private Integer connecionTimeout = 5000;
private Integer maxRetries = 10;
@Bean
public RemoteCacheManager cacheContainer() {
ConfigurationBuilder builder = new ConfigurationBuilder();
builder.addServers(rhdgHosts);
builder.socketTimeout(socketTimeout);
builder.connectionTimeout(connecionTimeout);
builder.maxRetries(maxRetries);
builder.connectionPool().minIdle(10);
builder.connectionPool().maxIdle(50);
return new RemoteCacheManager(builder.build());
}
}
(*) Para fins explicativos, os valores foram mantidos fixos. No fonte disponível para download eles estão em um arquivo properties.
Os métodos básicos para essa conexão são:
addServers | Endereço do servidor remoto. Devemos passar a lista de hosts separados por ponto e vírgula. Ex: 192.168.0.4:11222;192.168.0.5:11222. |
socketTimeout | Valor em milissegundos do timeout do socket de leitura. |
connectionTimeout | O tempo de espera antes da conexão retornar um timeout. |
maxRetries | Quantidade de retentativas para cada request feita. O valor deve ser superior a 0, onde 0 significa nenhuma tentativa. O valor default é 10. |
Com essa configuração o componente camel-infinispan já está pronto para se conectar e efetuar operações de inserção, consulta e exclusão de chaves no Red Hat Data Grid através do protocolo Hot Rod.
Este protocolo foi desenvolvido para permitir que as interações entre cliente/servidor sejam mais rápidas (em comparação com outros protocolos, baseados em texto), e também para permitir que os clientes tomem decisões mais inteligentes em relação ao balanceamento de carga, failover e até operações de localização de dados.
Antes de continuar é importante entender a sintaxe de um endpoint Infinispan (RHDG). Para criarmos producers e consumers o componente utiliza a seguinte sintaxe em sua URI:
infinispan://cacheName?[options]
Para enviarmos uma chave, a URI ficará conforme o exemplo:
.to(infinispan:cacheCustomers?cacheContainer=#remoteCacheManagerExample")
No exemplo acima estamos enviando algo para um cache remoto chamado cacheCustomers
, e as configurações necessárias para esse envio estão no bean chamado remoteCacheManagerExample
. O RemoteCacheManager
é o equivalente a um dataSource para conexões jdbc ou jpa.
Existem diversas opções de configurações que podem ser passadas pela URI, a lista destas opções pode ser encontrada no site do componente, dentro do portal 'The Apache Software Foundation', no endereço https://camel.apache.org/components/latest/infinispan-component.html.
Incluindo uma chave
Em nosso projeto de exemplo criamos um webservice que envia uma chave e um valor através de um objeto JSON para uma rota chamada put-key-route. Esta rota faz a comunicação com o Red Hat Data Grid, passando os dados da chave para serem armazenadas através de headers na mensagem Camel.
Quando a rota direct: saveKey
for acionada, ela irá inserir 3 headers na mensagem Camel informando o tipo de operação, o nome e o conteúdo da chave.
CamelInfinispanOperation | Operação que será executada. No caso da inserção de uma chave, a operação é PUT. |
CamelInfinispanKey | Nome da chave no cache remoto. |
CamelInfinispanValue | Conteúdo da chave. |
Abaixo é exibida a implementação da rota direct: saveKey
.
//Save a key
from("direct:saveKey").routeId("put-key-route")
.setHeader(InfinispanConstants.OPERATION,
constant(InfinispanOperation.PUT))
.process(new Processor() {
@Override
public void process(Exchange exchange) throws Exception {
KeyDTO chave = exchange.getIn().getBody(KeyDTO.class);
exchange.getIn().setHeader(InfinispanConstants.KEY,
chave.getKey());
exchange.getIn().setHeader(InfinispanConstants.VALUE,
chave.getValue());
}
}).to("infinispan:{{custom.rhdg.cache.name}}?cacheContainer=#remoteCacheManagerExample")
.process(new ResponseSaveKeyProcessor());
Na implementação acima foram utilizadas constantes da própria API do componente camel-Infinispan para informar os nomes dos headers e inclusive o valor da operação.
Consultando uma chave
Para consultar uma chave no cache remoto precisamos através da rota Camel definir o header chamado CamelInfinispanOperation
com o valor GET para informar ao servidor remoto que iremos fazer uma operação de consulta de chave. Após definir o tipo de operação, precisamos através de um segundo header chamado CamelInfinispanKey
informar o nome da chave que queremos consultar.
O conteúdo da chave será retornado no body da mensagem Camel. Caso a chave não exista o valor do body será null.
Abaixo é exibida a implementação da rota que faz a consulta de uma chave.
// get a key
from("direct:getKey")
.routeId("get-key-route")
.setHeader(InfinispanConstants.OPERATION,
constant(InfinispanOperation.GET))
.setHeader(InfinispanConstants.KEY,
simple("${in.header.id}")).to("infinispan:{{custom.rhdg.cache.name}}?cacheContainer=#remoteCacheManagerExample");
Se for informada uma chave que não existe no Red Hat Data Grid, o body retornará null e não haverá nenhum código de erro associado.O valor da chave pode ser recuperado através do body da mensagem em exchange.getIn().getBody()
.
Excluindo uma chave
A exclusão da chave segue o mesmo modelo da consulta, mudando somente o tipo de operação. Enviamos um header com a operação REMOVE e outro header com o valor da chave.
Abaixo é exibida a implementação da rota que faz a remoção de uma chave no Red Hat Data Grid.
//Remove a key
from("direct:deleteKey")
.routeId("delete-key-route")
.setHeader(InfinispanConstants.OPERATION,
constant(InfinispanOperation.REMOVE))
.setHeader(InfinispanConstants.KEY ,
simple("${in.header.id}")).to("infinispan:{{custom.rhdg.cache.name}}?cacheContainer=#remoteCacheManagerExample");
Ciclo de vida da chave
O ciclo de vida de uma chave dentro do cache é controlado pelas propriedades LifeSpan e MaxIdle. Essas propriedades são configuradas no próprio Red Hat Data Grid, sendo possível ajustá-las para cada nova chave inserida através do JBoss Fuse.
O LifeSpan é o tempo que uma chave permanece válida no cache, se este valor for -1 a chave nunca expirará. O MaxIdle é o tempo que uma chave pode permanecer no cache sem ser utilizada. O valor de MaxIdle deve sempre ser menor que o valor do LifeSpan.
Para manipular os valores de LifeSpan e MaxIdle através do JBoss Fuse, utilizamos 4 headers:
CamelInfinispanLifespanTime | Valor do LifeSpan. Podemos nos referir a ele através da constante LIFESPAN_TIME da classe InfinispanConstants. |
CamelInfinispanTimeUnit | Unidade de medida utilizada na configuração do LifeSpan Podemos nos referir a ele através da constante LIFESPAN_TIME_UNIT da classe InfinispanConstants. |
CamelInfinispanMaxIdleTime | Valor do MaxIdle Podemos nos referir a ele através da constante MAX_IDLE_TIME da classe InfinispanConstants. |
CamelInfinispanMaxIdleTimeUnit |
Unidade de medida utilizada na configuração do MaxIdle. Podemos nos referir a ele através da constante MAX_IDLE_TIME_UNIT da classe InfinispanConstants. Podemos nos referir a ele através da constante MAX_IDLE_TIME_UNIT da classe InfinispanConstants. |
No exemplo abaixo a chave inserida terá um tempo de vida de 10 segundos:
//Save a key
from("direct:saveKey").routeId("put-key-route")
.setHeader(InfinispanConstants.OPERATION,
constant(InfinispanOperation.PUT))
.setHeader(InfinispanConstants.LIFESPAN_TIME, simple("10"))
.setHeader(InfinispanConstants.LIFESPAN_TIME_UNIT,
simple(TimeUnit.SECONDS.toString()))
.setHeader(InfinispanConstants.MAX_IDLE_TIME, simple("10"))
.setHeader(InfinispanConstants.MAX_IDLE_TIME_UNIT,
simple(TimeUnit.SECONDS.toString()))
.process(new Processor() {
@Override
public void process(Exchange exchange) throws Exception {
KeyDTO chave = exchange.getIn().getBody(KeyDTO.class);
exchange.getIn().setHeader(InfinispanConstants.KEY,
chave.getKey());
exchange.getIn().setHeader(InfinispanConstants.VALUE,
chave.getValue());
}
})
.to("infinispan:{{custom.rhdg.cache.name}}?cacheContainer=#remoteCacheManagerExample");
Considerações finais
O JBoss Fuse e o Data Grid são ferramentas muito poderosas com diversos recursos disponíveis. Apesar de termos abordado as funções básicas de uma integração entre eles, existem outros recursos a serem explorados como o uso de um cache local, métricas, ou pesquisas avançadas de chaves.
Maiores detalhes podem ser encontrados nas documentações oficiais do JBoss Fuse, componente camel-infinispan, Infinispan, e Red Hat Data Grid.
Sobre o autor
Rogério L Santos é arquiteto de software e atualmente tem atuado como Consultor de middleware na Red Hat Brasil. Com mais de 15 anos na área de tecnologia tem grande experiência em desenvolvimento com atuação em empresas como IBM e Porto Seguro. Possui MBA em arquitetura de soluções e é entusiasta de soluções Open Source.