Files
sa-token-go/docs/guide/redis-storage.md
c 04a9e5a72f 1、init project
2、完善部分功能
2025-10-18 06:38:51 +07:00

14 KiB

Redis Storage Configuration Guide

中文文档 | English

Overview

Redis storage is the recommended storage backend for production environments. It provides high performance, data persistence, and supports distributed deployments.

Installation

# Install Redis storage module
go get github.com/click33/sa-token-go/storage/redis

# Install Redis client
go get github.com/redis/go-redis/v9

Basic Usage

1. Simple Configuration

package main

import (
    "github.com/click33/sa-token-go/core"
    "github.com/click33/sa-token-go/stputil"
    "github.com/click33/sa-token-go/storage/redis"
    goredis "github.com/redis/go-redis/v9"
)

func main() {
    // Create Redis client
    rdb := goredis.NewClient(&goredis.Options{
        Addr:     "localhost:6379",
        Password: "", // No password
        DB:       0,  // Default DB
    })

    // Initialize Sa-Token with Redis storage
    stputil.SetManager(
        core.NewBuilder().
            Storage(redis.NewStorage(rdb)).
            TokenName("Authorization").
            Timeout(86400). // 24 hours
            Build(),
    )

    // Now you can use Sa-Token
    token, _ := stputil.Login(1000)
    println("Login successful, Token:", token)
}

2. With Password Authentication

rdb := goredis.NewClient(&goredis.Options{
    Addr:     "localhost:6379",
    Password: "your-redis-password", // Set password
    DB:       0,
})

stputil.SetManager(
    core.NewBuilder().
        Storage(redis.NewStorage(rdb)).
        Build(),
)

3. Using Redis Cluster

rdb := goredis.NewClusterClient(&goredis.ClusterOptions{
    Addrs: []string{
        "localhost:7000",
        "localhost:7001",
        "localhost:7002",
    },
    Password: "your-password",
})

stputil.SetManager(
    core.NewBuilder().
        Storage(redis.NewStorage(rdb)).
        Build(),
)

4. Using Redis Sentinel

rdb := goredis.NewFailoverClient(&goredis.FailoverOptions{
    MasterName:    "mymaster",
    SentinelAddrs: []string{
        "localhost:26379",
        "localhost:26380",
        "localhost:26381",
    },
    Password: "your-password",
    DB:       0,
})

stputil.SetManager(
    core.NewBuilder().
        Storage(redis.NewStorage(rdb)).
        Build(),
)

Advanced Configuration

Complete Configuration Example

package main

import (
    "time"
    
    "github.com/click33/sa-token-go/core"
    "github.com/click33/sa-token-go/stputil"
    "github.com/click33/sa-token-go/storage/redis"
    goredis "github.com/redis/go-redis/v9"
)

func main() {
    // Redis client with full options
    rdb := goredis.NewClient(&goredis.Options{
        Addr:         "localhost:6379",
        Password:     "",
        DB:           0,
        PoolSize:     10,              // Connection pool size
        MinIdleConns: 5,               // Minimum idle connections
        MaxRetries:   3,               // Maximum retry attempts
        DialTimeout:  5 * time.Second,
        ReadTimeout:  3 * time.Second,
        WriteTimeout: 3 * time.Second,
        PoolTimeout:  4 * time.Second,
    })

    // Initialize Sa-Token with Redis
    stputil.SetManager(
        core.NewBuilder().
            Storage(redis.NewStorage(rdb)).
            TokenName("Authorization").
            TokenStyle(core.TokenStyleJWT).
            JwtSecretKey("your-secret-key").
            Timeout(7200).              // 2 hours
            ActiveTimeout(1800).        // 30 minutes
            IsConcurrent(true).
            IsShare(false).             // Each login gets unique token
            MaxLoginCount(5).           // Max 5 concurrent logins
            AutoRenew(true).
            IsReadHeader(true).
            IsPrintBanner(true).
            Build(),
    )

    // Use Sa-Token
    token, _ := stputil.Login(1000)
    println("Token:", token)
}

Connection Pool Configuration

rdb := goredis.NewClient(&goredis.Options{
    Addr:     "localhost:6379",
    
    // Connection pool settings
    PoolSize:     100,              // Maximum connections
    MinIdleConns: 10,               // Minimum idle connections
    MaxIdleConns: 50,               // Maximum idle connections
    
    // Timeout settings
    DialTimeout:  5 * time.Second,  // Connection timeout
    ReadTimeout:  3 * time.Second,  // Read timeout
    WriteTimeout: 3 * time.Second,  // Write timeout
    PoolTimeout:  4 * time.Second,  // Pool get timeout
    
    // Retry settings
    MaxRetries:      3,              // Maximum retries
    MinRetryBackoff: 8 * time.Millisecond,
    MaxRetryBackoff: 512 * time.Millisecond,
})

Environment Variables

Using Environment Variables

package main

import (
    "os"
    "strconv"
    
    "github.com/click33/sa-token-go/core"
    "github.com/click33/sa-token-go/stputil"
    "github.com/click33/sa-token-go/storage/redis"
    goredis "github.com/redis/go-redis/v9"
)

func main() {
    // Read from environment variables
    redisAddr := os.Getenv("REDIS_ADDR")
    if redisAddr == "" {
        redisAddr = "localhost:6379"
    }
    
    redisPassword := os.Getenv("REDIS_PASSWORD")
    
    redisDB, _ := strconv.Atoi(os.Getenv("REDIS_DB"))
    
    rdb := goredis.NewClient(&goredis.Options{
        Addr:     redisAddr,
        Password: redisPassword,
        DB:       redisDB,
    })

    stputil.SetManager(
        core.NewBuilder().
            Storage(redis.NewStorage(rdb)).
            JwtSecretKey(os.Getenv("JWT_SECRET_KEY")).
            Build(),
    )
}

.env File Example

# Redis Configuration
REDIS_ADDR=localhost:6379
REDIS_PASSWORD=your-password
REDIS_DB=0

# Sa-Token Configuration
JWT_SECRET_KEY=your-256-bit-secret-key
TOKEN_TIMEOUT=7200

Redis Key Structure

Sa-Token-Go uses the following key patterns in Redis:

satoken:login:token:{tokenValue}        # Token -> LoginID mapping
satoken:login:session:{loginID}:token   # LoginID -> Token list
satoken:session:{loginID}               # User session data
satoken:permission:{loginID}            # User permissions
satoken:role:{loginID}                  # User roles
satoken:disable:{loginID}               # Account disable status

View Keys in Redis CLI

# Connect to Redis
redis-cli

# List all Sa-Token keys
KEYS satoken:*

# View token info
GET satoken:login:token:your-token-value

# View user session
GET satoken:session:1000

# View user permissions
SMEMBERS satoken:permission:1000

# View user roles
SMEMBERS satoken:role:1000

Production Best Practices

1. Connection Pool

rdb := goredis.NewClient(&goredis.Options{
    Addr:         "localhost:6379",
    PoolSize:     100,  // Adjust based on your load
    MinIdleConns: 10,   // Keep some connections alive
})

2. Error Handling

rdb := goredis.NewClient(&goredis.Options{
    Addr:     "localhost:6379",
    Password: os.Getenv("REDIS_PASSWORD"),
})

// Test connection
ctx := context.Background()
if err := rdb.Ping(ctx).Err(); err != nil {
    log.Fatalf("Failed to connect to Redis: %v", err)
}

3. High Availability (Sentinel)

rdb := goredis.NewFailoverClient(&goredis.FailoverOptions{
    MasterName:    "mymaster",
    SentinelAddrs: []string{
        "sentinel1:26379",
        "sentinel2:26379",
        "sentinel3:26379",
    },
    Password: os.Getenv("REDIS_PASSWORD"),
    DB:       0,
    
    // Sentinel options
    SentinelPassword: os.Getenv("SENTINEL_PASSWORD"),
    
    // Connection pool
    PoolSize:     100,
    MinIdleConns: 10,
})

4. TLS/SSL Support

import "crypto/tls"

rdb := goredis.NewClient(&goredis.Options{
    Addr:     "localhost:6379",
    Password: os.Getenv("REDIS_PASSWORD"),
    
    // Enable TLS
    TLSConfig: &tls.Config{
        MinVersion: tls.VersionTLS12,
    },
})

5. Graceful Shutdown

func main() {
    rdb := goredis.NewClient(&goredis.Options{
        Addr: "localhost:6379",
    })
    
    stputil.SetManager(
        core.NewBuilder().
            Storage(redis.NewStorage(rdb)).
            Build(),
    )

    // ... your application code ...

    // Graceful shutdown
    defer func() {
        if err := rdb.Close(); err != nil {
            log.Printf("Error closing Redis: %v", err)
        }
    }()
}

Performance Optimization

1. Use Pipelining

Redis storage in Sa-Token-Go automatically uses pipelining for batch operations.

2. Key Expiration

Sa-Token automatically sets expiration time for keys based on your Timeout configuration:

core.NewBuilder().
    Timeout(3600).  // Keys will expire in 1 hour
    Build()

3. Connection Reuse

The Redis client maintains a connection pool for optimal performance:

rdb := goredis.NewClient(&goredis.Options{
    PoolSize:     100,  // Reuse up to 100 connections
    MinIdleConns: 10,   // Always keep 10 warm connections
})

Monitoring

Check Redis Status

import "context"

ctx := context.Background()

// Ping
pong, err := rdb.Ping(ctx).Err()
if err != nil {
    log.Printf("Redis ping failed: %v", err)
}

// Get info
info, err := rdb.Info(ctx).Result()
if err != nil {
    log.Printf("Failed to get Redis info: %v", err)
}
println(info)

Monitor Key Count

# In Redis CLI
INFO keyspace

# Output example:
# db0:keys=1234,expires=567,avg_ttl=3600000

Troubleshooting

Connection Refused

// Problem: cannot connect to Redis
// Solution: Check if Redis is running
// Command: redis-cli ping

Authentication Failed

// Problem: NOAUTH Authentication required
// Solution: Set correct password
rdb := goredis.NewClient(&goredis.Options{
    Addr:     "localhost:6379",
    Password: "correct-password",
})

Too Many Connections

// Problem: ERR max number of clients reached
// Solution: Increase Redis max clients or reduce pool size
// Redis config: maxclients 10000

rdb := goredis.NewClient(&goredis.Options{
    PoolSize: 50, // Reduce pool size
})

Docker Deployment

Docker Compose Example

version: '3.8'

services:
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    command: redis-server --requirepass your-password
    volumes:
      - redis-data:/data
    restart: unless-stopped

  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      - REDIS_ADDR=redis:6379
      - REDIS_PASSWORD=your-password
      - JWT_SECRET_KEY=your-secret-key
    depends_on:
      - redis

volumes:
  redis-data:

Application Code

// In your Go application
func main() {
    rdb := goredis.NewClient(&goredis.Options{
        Addr:     os.Getenv("REDIS_ADDR"),     // redis:6379
        Password: os.Getenv("REDIS_PASSWORD"),
        DB:       0,
    })

    stputil.SetManager(
        core.NewBuilder().
            Storage(redis.NewStorage(rdb)).
            JwtSecretKey(os.Getenv("JWT_SECRET_KEY")).
            Build(),
    )
    
    // Start your web server...
}

Kubernetes Deployment

ConfigMap

apiVersion: v1
kind: ConfigMap
metadata:
  name: satoken-config
data:
  REDIS_ADDR: "redis-service:6379"
  REDIS_DB: "0"

Secret

apiVersion: v1
kind: Secret
metadata:
  name: satoken-secret
type: Opaque
stringData:
  REDIS_PASSWORD: "your-redis-password"
  JWT_SECRET_KEY: "your-jwt-secret-key"

Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: satoken-app
spec:
  replicas: 3
  template:
    spec:
      containers:
      - name: app
        image: your-app:latest
        env:
        - name: REDIS_ADDR
          valueFrom:
            configMapKeyRef:
              name: satoken-config
              key: REDIS_ADDR
        - name: REDIS_PASSWORD
          valueFrom:
            secretKeyRef:
              name: satoken-secret
              key: REDIS_PASSWORD
        - name: JWT_SECRET_KEY
          valueFrom:
            secretKeyRef:
              name: satoken-secret
              key: JWT_SECRET_KEY

Comparison: Memory vs Redis

Feature Memory Redis
Performance Excellent Very Good
Persistence Lost on restart Persistent
Distributed Not supported Supported
Scalability Limited Excellent
Setup Simple Requires Redis
Use Case Development/Testing Production

Complete Example

package main

import (
    "context"
    "log"
    "os"
    "time"

    "github.com/gin-gonic/gin"
    "github.com/click33/sa-token-go/core"
    "github.com/click33/sa-token-go/stputil"
    "github.com/click33/sa-token-go/storage/redis"
    sagin "github.com/click33/sa-token-go/integrations/gin"
    goredis "github.com/redis/go-redis/v9"
)

func main() {
    // Initialize Redis
    rdb := goredis.NewClient(&goredis.Options{
        Addr:     os.Getenv("REDIS_ADDR"),
        Password: os.Getenv("REDIS_PASSWORD"),
        DB:       0,
        
        PoolSize:     100,
        MinIdleConns: 10,
        DialTimeout:  5 * time.Second,
        ReadTimeout:  3 * time.Second,
        WriteTimeout: 3 * time.Second,
    })

    // Test Redis connection
    ctx := context.Background()
    if err := rdb.Ping(ctx).Err(); err != nil {
        log.Fatalf("Failed to connect to Redis: %v", err)
    }

    // Initialize Sa-Token
    stputil.SetManager(
        core.NewBuilder().
            Storage(redis.NewStorage(rdb)).
            TokenName("Authorization").
            TokenStyle(core.TokenStyleJWT).
            JwtSecretKey(os.Getenv("JWT_SECRET_KEY")).
            Timeout(7200).
            ActiveTimeout(1800).
            IsConcurrent(true).
            IsShare(false).
            MaxLoginCount(5).
            AutoRenew(true).
            IsReadHeader(true).
            IsPrintBanner(true).
            IsLog(true).
            Build(),
    )

    // Setup Gin
    r := gin.Default()
    r.Use(sagin.NewPlugin(stputil.GetManager()).Build())

    // Routes
    r.POST("/login", loginHandler)
    r.GET("/user/info", sagin.CheckLogin(), userInfoHandler)
    r.GET("/admin", sagin.CheckPermission("admin"), adminHandler)

    // Start server
    if err := r.Run(":8080"); err != nil {
        log.Fatal(err)
    }

    // Graceful shutdown
    defer rdb.Close()
}

func loginHandler(c *gin.Context) {
    // ... login logic ...
}

func userInfoHandler(c *gin.Context) {
    // ... user info logic ...
}

func adminHandler(c *gin.Context) {
    // ... admin logic ...
}

Redis Resources