Novos projetos: FastChat e Semaphore.js

Sempre curti micro-projetos úteis e reutilizáveis, principalmente widgets.

Bom, para o projeto que estou trabalhando vi a necessidade de dois widgets que imaginei que seria legal de reutilizar, e eis o resultado deles (ainda em desenvolvimento):

Semaphore.js

Um mini widget em javascript que apenas indica o status do servidor através de um semáforo.

Através de um url configurada do widget ele indica se o servidor está online, lento ou offline. É bem útil para aplicações totalmente em ajax, principalmente quando se depende de servidores instáveis ou serviços de terceiros. Permite ainda algumas integrações para cada estado.

Consiste apenas em um pequeno arquivo javascript.

FastChat

Fastchat: chat widget easy to use

Esse é mais legal! É um widget de bate-papo que basta carregar um pequeno javascript sem dependências e está pronto o chat!

Foi feito com um servidor em clojure, usando redis e javascript simples para o widget. Pode-se usar css para customizar a UI padrão, grava um histórico, possui uma “janela” por usuário, lista de onlines e etc.

Bom, é isso, o semaphore.js eu já usei em mais de um projeto e o fastchat ainda está em teste no projeto principal, mas acho que promete bastante.

Persistência de dados com Redis

Conhecendo o Redis

Redis é um banco de dados para persistência de dados no modelo Key/Value escrito em C ANSI para sistemas POSIX, com uma interface de rede embutida. Isso é o que diz no site.

O que quer dizer que Redis é um "Key/Value storage engine", parecido com o memcached mas persistente. Suporta dados em String e Intergers, Lists, Sets e Ordered Sets além de Hash Tables (que suportam apenas Strings e Intergers). Usa operações atômicas (uma alteração por vez), e suporta get/set, push/pop, add/remove, union, intersect, diffs e muito mais, incluindo opções de ordenação.

Ele mantêm, por padrão, todos os dados em memória, e salva snapshots para o disco eventualmente de forma assíncrona, ou escrevendo cada mudança para um "Append Only File" (o que é mais interessante). Ele é capaz de refazer o AOF em background. Também tem suporte trivial a replicação master-slave.

O Redis é insamente rápido para escritas e leituras(muito mesmo!), tem bibliotecas para "tipo" todas as linguagens, é desenvolvido em ritmo alucinante e não funciona bem no windows.

A prioridade para próxima versão é o projeto Redis Cluster.

Instalação e Configuração do Redis

Sendo um sistema de evolução muito rápido fique de olho no código-fonte no no site oficial e nos branches do github e pegue a ultima versão. Aqui vou usar a versão 2.0 do github, seguem passos para instalação:

$ git clone git://github.com/antirez/redis.git
$ cd redis
$ make

Pronto o sistema está construído. Você pode agora configurar o servidor redis pelo arquivo redis.conf. Alguns detalhes interessantes da configuração:

Uma boa opção é usar o Redis com AOF (append only file), a cada escrita ele vai adicionar esse dado ao log que será a forma de persistência. Basta usar a opção "appendonly" como "yes".

Importante novidade no Redis é o uso de memoria virtual (VM), como o Redis mantém tudo em memoria isso limita a capacidade de armazenamento a memoria disponível. Usando VM o Redis pode armazenar mais dados que a memoria disponível fazendo paginação de forma inteligente.

Para ativar a VM use a opção "vm-enable" como "yes". Você pode definir o máximo de memoria para a VM usando a opção "vm-maxmemory", se esse valor for zero o sistema vai manter apenas as chaves em memoria e todo os dados em disco. Tem ainda a opção do número de threads trabalhando na paginação com "vm-max-threads", você pode usar seu numero de "cores" (+2) ou 0 para ter blocking de leitura.

Usar paginação pode ser mais lento, mas ainda assim é bem rápido e permite uma capacidade maior de armazenamento. Claro, só vai ficar mais lento se usar o sistema até ser necessário paginar.

O ideal é usar o sistema com AOF para maior consistência e durabilidade (não há impacto na performance) e usar VM com um número razoável de threads, para caso seu sistema cresça bastante você não ter nenhum susto.

Você tem ainda a opção "slaveof" para torná-lo slave de outro servidor e pode definir a opção "requirepass" para usar autenticação.

Para iniciar o servidor basta executar:

$ ./redis-server

Feitas as configurações você pode rodar os testes de sistema(demora bastante!):

$ make test

E tem também um utilitário de benchmark, bom para testar diferentes configurações:

./redis-benchmark -n 1000 -q

Usando Redis

Com o servidor iniciado (./redis-server) você pode testar o redis usando o shell interativo, basta lançá-lo usando:

$ ./redis-cli

Você pode passar para o cli as operações que quer testar, ex:

$ ./redis-cli set foo "bar"
$ ./redis-cli get foo

Ou apenas usar o redis-cli para entrar em um prompt interativo.

Primeiro vamos ver um pouco dos tipos de dados do Redis.

String: O tipo mais básico, não tem mistério, são strings ou inteiros. É, inteiros também.

redis> set foo "bar"
OK
redis> get foo
"bar"
redis> set hello "Hello, "
OK
redis> append hello "Redis!"
(integer) 13
redis> mget foo hello
1."bar"
2."Hello, Redis!"
redis> substr hello 2 4
"llo"
redis> set um 1
OK
redis> incr um
(integer) 2
redis> incrby um 5
(integer) 7
redis> decr um
(integer) 6
redis> get um
"6"

List: Listas no Redis não são arrays, mas Linked Lists. São mais simples e rápidas, não são associativas, aceitam Strings (que podem ser inteiros, lembra). São muito rápidas para acesso nas pontas e para adição de itens e não tão rápidas para acessos aleatórios (pelo índice).

redis> rpush nums 1
redis> rpush nums 2
redis> lpush nums 0
redis> lrange nums 0 2
1."0"
2."1"
3."2"
redis> lrange nums 0 1
1."0"
2."1"
redis> lrange nums 0 -1
1."0"
2."1"
3."2"

Explicando um pouco, "rpush" insere um item a direita na lista (final), "lpush" a esquerda (inicio) e "lrange" retorna parte da lista.

redis> rpush msgs "Oi"
redis> rpush msgs "Meu nome"
redis> rpush msgs "É"
redis> rpush msgs "Redis!"
redis>lrange msgs 0 -1
1."Oi"
2."Meu nome"
3."É"
4."Redis!"
redis> rpop msgs
"Redis!"
redis> rpop msgs
"É"
redis> lpop msgs
"Oi"

O comando "lpop" retorna o elemento a esquerda da lista (começo) e o remove, o "rpop" o mesmo para a direita (final).

Set: É uma coleção de Strings não ordenadas.

redis> sadd users "diogok"
redis> sadd users "voce"
redis> sadd users "ninguem"
redis> smembers users
1."ninguem"
2."voce"
3."diogok"
redis> sadd admins "ninguem"
redis> sinter users admins
1."ninguem"
redis> sunion users admins
1."ninguem"
2."diogok"
3."voce"
redis> sdiff users admins
1."diogok"
2."voce"
redis> sdiff admins users
(empty list or set)
redis> srem "ninguem"

A vantagem dos sets é permitir operações de grupos como uniões (sunion), intercessões (sinter) e diferenças (sdiff).

Sorted Set: Parecido com Sets porem ordenados.

redis> zadd log 2210 "login"
redis> zadd log 2215 "view email"
redis> zadd log 2225 "view twitter"
redis>zrange log 0 -1
redis>zrevrange log 0 -1
redis> zrem log 2210

Hash: É um tipo novo (>= 2.0) que define hash tables, que contém chaves e conteúdos string.

redis> hset diogok name "Diogo"
redis> hmset diogok email "manifesto at manifesto.blog.br" password "123"
redis> hget diogok name
"Diogo"
redis> hkeys diogok
1."name"
2."email"
3."password"
redis> hvals
1."Diogo"
2."manifesto at manifesto.blog.br"
3."123"
redis> hgetall diogok
1."name"
2."Diogo"
3."email"
4."manifesto at manifesto.blog.br"
5."password"
6."123"
redis> hset diogok counter 1
redis> hincrby diogok counter 3
redis> hget diogok counter
4
redis> hdel diogok counter

Conclusão

Isso cobre o básico do uso do Redis, agora é escolher a biblioteca para sua linguagem e consultar a documentação do Redis e a lista completa de comandos.

O Redis se mostra muito interessante por se extremamente rápido, principalmente na escrita, e seus tipos de dados como set e ordered list o torna muito prático para certas aplicaçções:

  • Os Ordered Set são ótimos para listas manterem ordem temporal (noticias, tweets, logs…)
  • Os Sets servem muito bem para traçar grafos e comparar listas (amigos em comum, aprovações…)
  • Lists são perfeitos para queues ou listas de processamentos (urls para minerar, mensagens para enviar, dados a processar…)
  • Hash Tables com Strings armazenam dados genéricos de forma mais compacta.
  • Cada um desses casos tem performance excelente!

O mais importante ao adotar o Redis(ou qualquer “nosql” na verdade) é entender a nova modelagem. Um sistema em memória não é bom entulhar com dados mal formatados, deve-se retirar periodicamente esses dados para uma armazenagem secundária. Deve-se pensar também numa estratégia consisa de chaves, ex:

redis> sadd users diogok
redis> hmset users:diogok name "diogo" web "manifesto.blog.br"
redis> sadd users voce
redis> hmset users:voce name "voce" web "google.com"
redis> sadd conhecidos:diogok voce
redis> sinter conhecidos:diogok conhecidos:voce
(nenhum conhecido em comum)

Você pode ver um clone do twitter com php+redis ou uma aplicação de notas com ruby+redis e ainda um motor de busca com python+redis como exemplo.