← Volver al listado de tecnologías

Clientes de Valkey

Por: Artiko
valkeypythonnodejsgoclientes

Clientes

Python

Instalación

pip install valkey
# o para compatibilidad
pip install redis

Conexión básica

import valkey

# Conexión simple
r = valkey.Valkey(host='localhost', port=6379, db=0)

# Con URL
r = valkey.from_url('valkey://localhost:6379/0')

# Con opciones
r = valkey.Valkey(
    host='localhost',
    port=6379,
    password='password',
    decode_responses=True,  # Retorna strings en lugar de bytes
    socket_timeout=5,
    socket_connect_timeout=5
)

Operaciones comunes

# Strings
r.set('nombre', 'Juan')
r.get('nombre')
r.setex('sesion', 3600, 'datos')  # Con TTL

# Hashes
r.hset('usuario:1', mapping={'nombre': 'Ana', 'edad': 25})
r.hget('usuario:1', 'nombre')
r.hgetall('usuario:1')

# Listas
r.lpush('tareas', 'tarea1', 'tarea2')
r.rpop('tareas')
r.lrange('tareas', 0, -1)

# Sets
r.sadd('tags', 'python', 'valkey')
r.smembers('tags')

# Sorted Sets
r.zadd('ranking', {'player1': 100, 'player2': 200})
r.zrange('ranking', 0, -1, withscores=True)

Async con asyncio

import asyncio
import valkey.asyncio as valkey

async def main():
    r = valkey.from_url('valkey://localhost')

    await r.set('clave', 'valor')
    valor = await r.get('clave')

    await r.close()

asyncio.run(main())

Pub/Sub

# Publicador
r.publish('canal', 'mensaje')

# Suscriptor
pubsub = r.pubsub()
pubsub.subscribe('canal')

for mensaje in pubsub.listen():
    if mensaje['type'] == 'message':
        print(mensaje['data'])

Node.js

Instalación

npm install redis
# o específico de valkey
npm install @valkey/valkey-js

Conexión

import { createClient } from 'redis';

const client = createClient({
    url: 'redis://localhost:6379'
});

client.on('error', err => console.log('Error:', err));

await client.connect();

Operaciones

// Strings
await client.set('nombre', 'Juan');
const nombre = await client.get('nombre');
await client.setEx('sesion', 3600, 'datos');

// Hashes
await client.hSet('usuario:1', { nombre: 'Ana', edad: '25' });
const user = await client.hGetAll('usuario:1');

// Listas
await client.lPush('tareas', ['tarea1', 'tarea2']);
const tarea = await client.rPop('tareas');

// Sets
await client.sAdd('tags', ['node', 'valkey']);
const tags = await client.sMembers('tags');

// Sorted Sets
await client.zAdd('ranking', [
    { score: 100, value: 'player1' },
    { score: 200, value: 'player2' }
]);
const ranking = await client.zRange('ranking', 0, -1);

Pub/Sub

// Suscriptor
const subscriber = client.duplicate();
await subscriber.connect();

await subscriber.subscribe('canal', (mensaje) => {
    console.log('Recibido:', mensaje);
});

// Publicador
await client.publish('canal', 'mensaje');

Go

Instalación

go get github.com/valkey-io/valkey-go
# o compatible redis
go get github.com/redis/go-redis/v9

Conexión

package main

import (
    "context"
    "github.com/redis/go-redis/v9"
)

func main() {
    ctx := context.Background()

    rdb := redis.NewClient(&redis.Options{
        Addr:     "localhost:6379",
        Password: "",
        DB:       0,
    })

    defer rdb.Close()
}

Operaciones

// Strings
err := rdb.Set(ctx, "nombre", "Juan", 0).Err()
nombre, err := rdb.Get(ctx, "nombre").Result()

// Con TTL
err = rdb.SetEx(ctx, "sesion", "datos", time.Hour).Err()

// Hashes
err = rdb.HSet(ctx, "usuario:1", map[string]interface{}{
    "nombre": "Ana",
    "edad":   25,
}).Err()
user, err := rdb.HGetAll(ctx, "usuario:1").Result()

// Listas
err = rdb.LPush(ctx, "tareas", "tarea1", "tarea2").Err()
tarea, err := rdb.RPop(ctx, "tareas").Result()

// Sets
err = rdb.SAdd(ctx, "tags", "go", "valkey").Err()
tags, err := rdb.SMembers(ctx, "tags").Result()

Pub/Sub

// Suscriptor
pubsub := rdb.Subscribe(ctx, "canal")
ch := pubsub.Channel()

go func() {
    for msg := range ch {
        fmt.Println(msg.Channel, msg.Payload)
    }
}()

// Publicador
rdb.Publish(ctx, "canal", "mensaje")

Java

Maven

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>5.0.0</version>
</dependency>

Uso básico

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

// Con pool
JedisPool pool = new JedisPool("localhost", 6379);

try (Jedis jedis = pool.getResource()) {
    jedis.set("nombre", "Juan");
    String nombre = jedis.get("nombre");

    jedis.hset("usuario:1", "nombre", "Ana");
    Map<String, String> user = jedis.hgetAll("usuario:1");
}

Rust

Cargo.toml

[dependencies]
redis = "0.24"
tokio = { version = "1", features = ["full"] }

Uso

use redis::Commands;

fn main() -> redis::RedisResult<()> {
    let client = redis::Client::open("redis://127.0.0.1/")?;
    let mut con = client.get_connection()?;

    let _: () = con.set("nombre", "Juan")?;
    let nombre: String = con.get("nombre")?;

    Ok(())
}

Patrones comunes

Wrapper con reintentos

import time
from functools import wraps

def retry(max_attempts=3, delay=1):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(max_attempts):
                try:
                    return func(*args, **kwargs)
                except valkey.ConnectionError:
                    if attempt < max_attempts - 1:
                        time.sleep(delay)
                    else:
                        raise
        return wrapper
    return decorator

@retry(max_attempts=3)
def get_data(key):
    return r.get(key)

Cache decorator

def cache(ttl=300):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            key = f"{func.__name__}:{args}:{kwargs}"
            cached = r.get(key)
            if cached:
                return json.loads(cached)
            result = func(*args, **kwargs)
            r.setex(key, ttl, json.dumps(result))
            return result
        return wrapper
    return decorator

@cache(ttl=60)
def get_user(user_id):
    # Consulta costosa a DB
    return db.get_user(user_id)

Repository pattern

class UserRepository:
    def __init__(self, valkey_client):
        self.r = valkey_client

    def get(self, user_id):
        data = self.r.hgetall(f'user:{user_id}')
        return data if data else None

    def save(self, user_id, data):
        self.r.hset(f'user:{user_id}', mapping=data)

    def delete(self, user_id):
        self.r.delete(f'user:{user_id}')

Comparativa de clientes

LenguajeClienteAsyncClusterSentinel
Pythonvalkey-py
Node.jsredis
Gogo-redis
JavaJedis
Rustredis-rs

Ejercicios

  1. Implementa un cache con TTL en tu lenguaje favorito
  2. Crea un sistema pub/sub entre dos servicios
  3. Implementa el patrón repository para una entidad

Resumen