Uma implementação concorrente, modular e em memória de um servidor compatível com o protocolo Redis (RESP - REdis Serialization Protocol), desenvolvida em Go puro, utilizando exclusivamente a biblioteca padrão (standard library).
- Sobre o Projeto
- Funcionalidades
- Arquitetura
- Estrutura de Diretórios
- Comandos Suportados
- Pré-requisitos
- Como Executar
- Conectando ao Servidor
- Exemplos Práticos
- Roadmap & Próximos Passos
- Licença
O redis-go foi projetado com foco em simplicidade, concorrência idiomática em Go e aderência às especificações do protocolo de rede do Redis. Cada cliente conectado é gerenciado por uma goroutine independente, comunicando-se com uma camada de armazenamento em memória sincronizada e protegida contra condições de corrida (race conditions).
- Zero Dependências Externas: Construído 100% com recursos nativos do Go (
net,bufio,sync,time, etc.). - Concorrência Eficiente: Modelo multithreaded leve via goroutines atendendo conexões TCP simultâneas.
- Suporte a RESP e Comandos Inline: Compatível tanto com clientes oficiais (que usam arrays RESP) quanto com clientes simples de texto puro via terminal.
- Thread-Safety: Armazenamento protegido com
sync.RWMutex, viabilizando múltiplas leituras simultâneas e escritas exclusivas seguras.
- Servidor TCP configurável (padrão
:6379). - Parser completo de mensagens RESP (Arrays, Bulk Strings, validação de CRLF).
- Parser de comandos inline com suporte a argumentos delimitados por aspas (
"..."). - Formatador de respostas RESP (Simple String
+, Error-, Integer:, Bulk String$, Null Bulk String$-1). - Expiração de chaves com TTL e verificação passiva no acesso.
- Operações atômicas de incremento e decremento numérico (
INCR,DECR). - Registro centralizado e extensível de comandos (Command Registry pattern).
O fluxo de dados segue uma separação clara de responsabilidades entre transporte, protocolo, roteamento de comandos e armazenamento:
flowchart LR
Client([Cliente / redis-cli]) -->|TCP Socket| Server[Server TCP :6379]
Server -->|Goroutine per conn| Parser[Protocol Parser]
Parser -->|Args Deserializados| Registry[Command Registry]
Registry -->|Executa Handler| Store[(In-Memory Store\nsync.RWMutex)]
Store -->|Resultado| Writer[Protocol Writer]
Writer -->|RESP Serializado| Client
server: Gerencia o ciclo de vida do socket TCP e instancia uma goroutine por conexão ativa.protocol:- Parser: Lê streams de bytes do socket e decodifica requisições no formato RESP Array (
*<count>) ou texto inline. - Writer: Serializa os tipos de retorno segundo a especificação RESP (
+OK,:1,$3\r\nfoo\r\n, etc.).
- Parser: Lê streams de bytes do socket e decodifica requisições no formato RESP Array (
command: Mapeia nomes de comandos (ex:SET,GET) para seus respectivos handlers e valida aridade/sintaxe.storage: EstruturaStorecom mapa em memória (map[string]Value), gerenciando dados e prazos de expiração (ExpiresAt) sob locks de leitura/escrita.
redis-go/
├── cmd/
│ └── main.go # Ponto de entrada do programa (inicia o servidor em :6379)
├── internal/
│ ├── command/ # Registry e handlers de cada comando suportado
│ │ ├── command.go # Estrutura do Registry e despachante (Execute)
│ │ ├── del.go # Handler: DEL
│ │ ├── exists.go # Handler: EXISTS
│ │ ├── expire.go # Handler: EXPIRE
│ │ ├── get.go # Handler: GET
│ │ ├── incr.go # Handlers: INCR e DECR
│ │ ├── ping.go # Handler: PING
│ │ ├── set.go # Handler: SET (com suporte opcional a 'EX seconds')
│ │ └── ttl.go # Handler: TTL
│ ├── protocol/ # Tratamento de protocolo RESP e parsing
│ │ ├── parser.go # Leitor de comandos RESP e comandos inline
│ │ └── writer.go # Escritor de respostas formatadas no padrão RESP
│ ├── server/ # Camada de rede e concorrência
│ │ └── server.go # Listener TCP e rotina de gerenciamento de conexões
│ └── storage/ # Engine de armazenamento em memória
│ └── store.go # Key-Value store thread-safe com sync.RWMutex e TTL
├── go.mod # Declaração do módulo Go
└── README.md # Documentação do projeto
| Comando | Sintaxe | Descrição | Exemplo |
|---|---|---|---|
PING |
PING [message] |
Testa conectividade com o servidor. Retorna PONG ou o argumento enviado. |
PING → +PONG |
SET |
SET key value [EX seconds] |
Grava uma chave e valor, opcionalmente definindo TTL em segundos. | SET user "Alisson" EX 60 → +OK |
GET |
GET key |
Obtém o valor de uma chave. Retorna nil se não existir ou se expirada. |
GET user → "Alisson" |
DEL |
DEL key [key ...] |
Remove uma ou múltiplas chaves. Retorna o número de chaves removidas. | DEL key1 key2 → (integer) 2 |
EXISTS |
EXISTS key [key ...] |
Verifica a existência de uma ou mais chaves. Retorna o total encontrado. | EXISTS user → (integer) 1 |
INCR |
INCR key |
Incrementa o valor numérico da chave em 1 (cria com 1 se inexistente). | INCR contador → (integer) 1 |
DECR |
DECR key |
Decrementa o valor numérico da chave em 1 (cria com -1 se inexistente). | DECR contador → (integer) 0 |
EXPIRE |
EXPIRE key seconds |
Define um tempo de vida (TTL) em segundos para uma chave existente. | EXPIRE sessao 30 → (integer) 1 |
TTL |
TTL key |
Retorna o tempo restante de vida da chave em segundos (-1 sem TTL, -2 não existe). |
TTL sessao → (integer) 29 |
- Go versão 1.22 ou superior instalada no sistema.
- Cliente para conexão:
redis-cli(incluído no pacote Redis),nc(netcat) outelnet.
Clone o repositório em sua máquina:
git clone https://github.com/atwof/redis-go.git
cd redis-gogo run cmd/main.goPara compilar um binário otimizado para sua plataforma:
go build -o bin/redis-go cmd/main.go
./bin/redis-goO servidor inicializará emitindo o log:
redis-go listening on :6379
Abra outro terminal e utilize a ferramenta oficial do Redis:
redis-cli -p 6379Graças ao parser inline, você pode interagir diretamente via texto plano:
nc localhost 6379PING
+PONG
SET curso "Golang"
+OK
GET curso
$6
Golang
Demonstração de uso interativo via redis-cli:
127.0.0.1:6379> PING "Hello World"
"Hello World"
127.0.0.1:6379> SET nome "redis-go"
OK
127.0.0.1:6379> GET nome
"redis-go"
127.0.0.1:6379> EXISTS nome
(integer) 1
127.0.0.1:6379> INCR visualizacoes
(integer) 1
127.0.0.1:6379> INCR visualizacoes
(integer) 2
127.0.0.1:6379> DECR visualizacoes
(integer) 1
127.0.0.1:6379> SET token "abc123xyz" EX 10
OK
127.0.0.1:6379> TTL token
(integer) 8
127.0.0.1:6379> DEL nome visualizacoes
(integer) 2