A biblioteca Redigo é um dos clientes Redis mais utilizados na linguagem Go, oferecendo uma abstração poderosa sobre o protocolo RESP (Redis Serialization Protocol). O núcleo de sua operação reside na interface Conn, que define como as aplicações interagem com o servidor.
A Interface Principal: Conn
Qualquer interação com o Redis através do Redigo começa com a interface Conn. Ela gerencia o ciclo de vida da conexão e o envio/recebimento de dados.
type Conn interface {
Close() error
Err() error
Do(comando string, args ...interface{}) (interface{}, error)
Send(comando string, args ...interface{}) error
Flush() error
Receive() (interface{}, error)
}
Estabelecendo Conexões
A função Dial é o ponto de entrada comum para criar uma nova instância de conexão. Internamente, ela utiliza o pacote net para abrir um socket TCP e, opcionalmente, configurar auetnticação e TLS.
instancia, erro := redis.Dial("tcp", "127.0.0.1:6379",
redis.DialPassword("senha_secreta"),
redis.DialDatabase(0),
)
if erro != nil {
// Tratar falha na conexão
}
defer instancia.Close()
Por baixo do capô, o Redigo utiliza o DialContext para gerenciar timeouts e configurações de rede via net.Dialer. Se o TLS estiver habilitado, ele realiza o handshake antes de retornar a conexão pronta para uso. Após a conexão física, comandos como AUTH e SELECT são enviados automaticamente se configurados nas opções de Dial.
Execução de Comandos com Do
O método Do é uma operação síncrona que combina o envio de um comando, o esvaziamento do buffer (flush) e a leitura da resposta. Ele utiliza o DoWithTimeout internamente para garantir que a operação respeite os limites de tempo definidos.
func (c *conn) Executar(cmd string, args ...interface{}) (interface{}, error) {
// Gerencia prazos de escrita (Write Deadline)
if c.writeTimeout != 0 {
c.conn.SetWriteDeadline(time.Now().Add(c.writeTimeout))
}
// Serializa e escreve o comando no buffer
if cmd != "" {
if err := c.writeCommand(cmd, args); err != nil {
return nil, c.fatal(err)
}
}
// Envia os dados do buffer para o socket
if err := c.bw.Flush(); err != nil {
return nil, c.fatal(err)
}
// Define prazo de leitura e processa a resposta
return c.readReply()
}
O Protocolo RESP e a Escrita de Comandos
O Redis comunica-se através de um protocolo baseado em contagem de argumentos e tamanhos de bytes. O Redigo abstrai isso na função writeCommand. O formato segue o padrão:
*<número de argumentos> CR LF
$<tamanho do argumento 1> CR LF
<dados do argumento 1> CR LF
...
Exemplo de como o Redigo formata um comando SET chave valor:
*3
$3
SET
$5
chave
$5
valor
Processamento de Respostas
A leitura de dados do Redis exige o tratamento de diferentes prefixos de tipo. O método readReply identifica o primeiro byte da resposta para determinar como processar o restante do conteúdo:
+: Strings simples (ex: "OK", "PONG").-: Mensagens de erro do servidor.:: Inteiros.$: Bulk Strings (dados binários ou strings longas).*: Arrays (coleções de outros tipos RESP).
func (c *conn) processarResposta() (interface{}, error) {
linha, err := c.readLine()
if err != nil {
return nil, err
}
switch linha[0] {
case '+':
return string(linha[1:]), nil
case ':':
return strconv.ParseInt(string(linha[1:]), 10, 64)
case '$':
tamanho, _ := parseLen(linha[1:])
dados := make([]byte, tamanho)
io.ReadFull(c.br, dados)
c.readLine() // Consumir CR LF final
return dados, nil
// Outros casos omitidos para brevidade
}
return nil, errors.New("tipo desconhecido")
}
Conversão de Resultados
Como o Redis retorna tipos genéricos (interface{}), o Redigo fornece funções auxiliraes para converter esses dados em tipos nativos do Go de forma segura. A função String(), por exemplo, lida com bytes, strings e erros de forma transparente.
res, err := redis.String(c.Do("GET", "minha_chave"))
if err != nil {
if err == redis.ErrNil {
// Chave não existe
}
return err
}
fmt.Println("Valor:", res)
Essas ferramentas de conversão (como Int, Values, StringMap) são fundamentais para manter o código Go tipado e evitar asserções de tipo manuais repetitivas.