Java Redis: Utilizando o Redis com coleções Java

O Redis é um banco NOSQL de chave valor de arquitetura distribuída feito para memória. O objetivo desse artigo é falar sobre esse banco e mostrar como implementar Coleções no Java.

O Redis é um banco de dados NoSQL escrito em C. O Remote Dictionary Server é um banco de dados chave-valor cujo o armazenamento é feito na memória, fazendo com que a escrita e a leitura sejam rápidos.

Mas qual a diferença entre ele o cache? O que acontece quando o banco cai? Haverá perda total dos dados? O objetivo desse artigo é falar um pouco desse banco de dados e demonstrar o seu uso prático utilizando um pequeno projeto open source, o redis-collections.

Considerando que as ferramentas de caches, como memcache e infinitspam, também possui esse comportamento de chave-valor, a primeira dúvida normal de um desenvolvedor é saber qual a diferença entre os dois. A primeira diferença está na serialização dos objetos valores: enquanto os caches escritos em Java são serializados de forma binária, com o Kryo ou o java.io.Serializable, por exemplo, para o redis tudo é texto. Um problema da serialização binária é a impossibilidade de realizar alterações de forma manual como em um texto. Ao usar o Kryo ou o java.io.Serializable, caso haja uma alteração significativa dentro da estrutura do objeto, não será mais possível deserializar tal estrutura no novo objeto, ou seja, ao retornar para a estrutura anterior ou descartar as informações. A razão disso é que os binários tendem a ser mais rápido para leitura e escrita e mais compactos, e os caches não são feitos para perdurar por longo período de tempo, A grande maioria dura enquanto o programa está rodando.

Nota: Entre de uma vez por todas no mercado de desenvolvimento em Java com os Cursos DevMedia.

Outra diferença está no tipo de valores: enquanto o cache trata apenas chave e valor, sendo que o valor é um objeto, no redis existem diferentes estruturas de dados no lado do valor. São eles:

Além desses que são os mais conhecidos, existem também o Bit arrays e HyperLog que não serão abordados nesse artigo.

Uma vez explicando as diferenças básicas entre o Redis e um cache, o próximo passo será a instalação do banco que é bastante simples:

  1. Realizar o Download aqui;
  2. Descompartar, abrir no terminal do redis e em seguida compilar o redis, conforme o código abaixo:
    tar zxvf redis-versao.x.tar.gz cd redis-versao.x make
  3. Uma vez compilado, entrar na pasta src e executar o servidor, conforme o código abaixo:
    cd src ./redis-server

Pronto, o Redis está rodando. Para realizar alguns testes, basta executar o cliente que já vem com o ele. Para isso, basta ir em REDIS_HOME/src e em seguida executar o comando ./redis-cli.

Para saber mais sobre os comandos, acesse o Site da redis

Uma vez instalado e testado, o nosso objetivo agora será a utilização de algumas estruturas de dados em cima do redis. Nosso objetivo será a utilização e a implementação de java.util.List, java.util.Set, java.util.Queue e java.util.Map, além de mais três estruturas: o conceito de chave e valor (semelhante ao cache), uma para contador e outra para fazer Ranking, sorted Set.

Ao idealizar a API, vamos ter o mesmo código da Listagem 1.

public interface keyValueRedisStructure<T> { T get(String key); void set(String key, T bean); List<T> multiplesGet(Iterable<String> keys); void delete(String key); } public interface CountStructure<T extends Number> { T get(); T increment(); T increment(T count); T decrement(); T decrement(T count); void delete(); void expires(int ttlSeconds); void persist(); } public interface ListStructure <T> extends Expirable { List<T> get(String key); void delete(String key); } public interface MapStructure <T> extends Expirable{ Map<String, T> get(String key); void delete(String key); } public interface QueueStructure <T> extends Expirable { Queue<T> get(String key); void delete(String key); } public interface RankingStructure<T extends Number> extends Expirable { ScoresPoint<T> create(String key); void delete(String key); }
Listagem 1. Definição das estruturas

Uma vez vendo a estrutura, uma das dúvidas é o Expirable, devido a ideia em cima da chave. Conseguimos definir o tempo de vida em segundos do respectivo valor e para anular esse tempo de expiração existe o comando persist. Vale lembrar que mesmo sendo em memória, o redis de tempos em tempos realiza backup das informações e, dessa forma, caso o banco caia as informações serão mantidas. Como dito anteriormente, o redis trata como a chave e os valores como String. Dessa forma, para seralizar e desearlizar o objetivo precisar ser em texto. Por ser mais “leve”, a estrutura escolhida será o JSON utilizando o framework de leitura e escrita de JSON do Google - o GSON, mas poderia ser qualquer outra forma, por exemplo, o XML tem campos separados por pipe “|” etc. E para se comunicar com o redis será utilizado o Jedis.

Porque java.util?

As coleções dentro do java.util certamente são conhecidas pela grande maioria dos desenvolvedores Java. Dessa forma, não existiria dificuldade destes para a utilizarem.

A grande diferença entre as implementações dentro do JDK e a dessas coleções é que as últimas serão implementadas no modo lazy. Enquanto as implementações de List, como ArrayList e LinkedList, já possuem as informações, por exemplo, o RedisList usará o Jedis, a API de comunicação com o redis para realizar uma operação (seja inserir, verificar o tamanho ou retornar os valores dentro do redis). Desse é dito que um RedisList é igual ao outro se ele tiver a mesma chave.

Convenção do namespace

Por convenção, o redis utiliza o conceito de namespace que funciona como um prefixo da chave. O seu formato é: namespace:chave.

Por exemplo, para registrar informações do usuário na seção seria users:nickname.

Cliente do Redis

O Jedis é o client para o redis feito para o Java e com ele é possível realizar todas as operações em cima das chaves. O seu uso é bastante simples, conforme mostra o código abaixo:

Jedis jedis = new Jedis("localhost"); jedis.set("foo", "bar"); String value = jedis.get("foo");

No exemplo acima foi criada uma conexão para o redis na nossa máquina localhost e, em seguida, foram feitas duas operações simples: Setar o bar dentro da chave foo e retornar o valor dentro da chave “foo”.

O Redis possui uma arquitetura distribuída, ou seja, pode-se trabalhar com clusters. Para fazer o client com nós dentro do Jedis, é necessário utilizar o comando da Listagem 3.

Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>(); //O jedis cluster irá descobrir os outros nós automaticamente. jedisClusterNodes.add(new HostAndPort("127.0.0.1", 7379)); JedisCluster jc = new JedisCluster(jedisClusterNodes); jc.set("foo", "bar"); String value = jc.get("foo");
Listagem 3. Client do Jedis

Criando as estruturas

A base para a criação das estruturas é o RedisStrutureBuilder, sendo que, entre os métodos, o único campo obrigatório é o Jedis, que como dito anteriormente, é a nossa comunicação entre a nossa aplicação e o Redis. Acompanhe o código da Listagem 4.

ListStructure<ProductCart> shippingCart = RedisStrutureBuilder.ofList(jedis, ProductCart.class).withNameSpace("list_producs").build(); List<ProductCart> fruitsCarts = shippingCart.get(FRUITS); fruitsCarts.add(banana); ProductCart banana = fruitsCarts.get(0); User otaviojava = new User("otaviojava"); User felipe = new User("ffrancesquini"); SetStructure<User> socialMediaUsers = RedisStrutureBuilder.ofSet(RedisConnection.JEDIS, User.class).withNameSpace("socialMedia").build(); Set<User> users = socialMediaUsers.createSet("twitter"); users.add(otaviojava); users.add(otaviojava); users.add(felipe); users.add(otaviojava); users.add(felipe); //haverá apenas um objeto otaviojava e um felipe, já que ele impede a duplicação da lista //lembrando que os métodos de equals e hascode dos objetos não serão utilizados, o que será considerado é a String do objeto gerado. Species mammals = new Species("lion", "cow", "dog"); Species fishes = new Species("redfish", "glassfish"); Species amphibians = new Species("crododile", "frog"); MapStructure<Species> zoo = = RedisStrutureBuilder.ofMap(RedisConnection.JEDIS, Species.class) .withNameSpace("animalZoo").build(); Map<String, Species> vertebrates = zoo.get("vertebrates"); vertebrates.put("mammals", mammals); vertebrates.put("mammals", mammals); vertebrates.put("fishes", fishes); vertebrates.put("amphibians", amphibians); QueueStructure<LineBank> serviceBank = RedisStrutureBuilder.ofQueue(RedisConnection.JEDIS, LineBank.class).withNameSpace("serviceBank").build(); QueueStructure<LineBank> serviceBank = RedisStrutureBuilder.ofQueue(RedisConnection.JEDIS, LineBank.class).withNameSpace("serviceBank").build(); Queue<LineBank> lineBank = serviceBank.get("createAccount"); lineBank.add(new LineBank("Otavio", 25)); LineBank otavio = lineBank.poll();
Listagem 4. RedisStrutureBuilder

Com isso, foi discutido nesse artigo um pouco mais sobre o redis e um exemplo prático utilizando implementações das coleções Java para o redis. Salientamos a diferença entre o redis o cache. Os caches apresentados acima, por usarem formato binário e usarem o Kryo ou o java.io.Serializable acabam sendo mais compactos e rápidos na leitura e escrita. No entanto, uma grande mudança no objeto acaba impossibilitando a recuperação das informações antes da mudança, sendo imprescindível a eliminação das informações anteriores, afinal. É esse o objetivo do cache: um bloco de memória de acesso rápido de armazenamento temporário. Já o redis é um banco de dados chave valor cujo o armazenamento fica na memória. No entanto, de tempos em tempos é feito um autobackup das informações no disco com o intuito de, caso aconteça uma queda, não haja perdas das informações. E o outro fator é que o redis está no seu armazenamento utilizando String tanto na chave como no valor e possui algumas estruturas na parte do valor.

Artigos relacionados