golang支持集群和独立项过期的高性能缓存插件clusteredBigCache的使用

Golang支持集群和独立项过期的高性能缓存插件clusteredBigCache的使用

clusteredBigCache是一个基于bigcache的库,经过修改后支持集群和独立项过期功能。它允许你在多个应用实例之间共享缓存数据,而无需依赖外部缓存服务如Redis或Memcached。

安装

使用go get命令安装:

$ go get github.com/oaStuff/clusteredBigCache

示例1:写入缓存的应用

这是一个将数据写入缓存的应用示例:

package main

import (
    "fmt"
    "bufio"
    "os"
    "github.com/oaStuff/clusteredBigCache/Cluster"
    "strings"
    "time"
)

// main函数
func main() {
    fmt.Println("starting...")
    // 使用默认配置创建缓存
    cache := clusteredBigCache.New(clusteredBigCache.DefaultClusterConfig(), nil)
    count := 1
    // 必须调用Start()方法
    cache.Start()

    reader := bufio.NewReader(os.Stdin)
    var data string
    for strings.ToLower(data) != "exit" {
        fmt.Print("enter data : ")
        data, _ = reader.ReadString('\n')
        data = strings.TrimSpace(data)
        // 将数据存入缓存,设置60分钟过期时间
        err := cache.Put(fmt.Sprintf("key_%d", count), []byte(data), time.Minute * 60)
        if err != nil {
            panic(err)
        }
        fmt.Printf("'%s' stored under key 'key_%d'\n", data, count)
        count++
    }
}

说明

  • cache := clusteredBigCache.New(clusteredBigCache.DefaultClusterConfig(), nil) 使用默认配置创建缓存
  • cache.Start() 必须在调用其他方法前执行
  • cache.Put() 方法接收键、值(字节切片)和过期时间三个参数

示例2:读取缓存的应用

这是一个从缓存读取数据的应用示例,可以运行在同一台或不同机器上:

package main

import (
    "github.com/oaStuff/clusteredBigCache/Cluster"
    "bufio"
    "os"
    "strings"
    "fmt"
    "time"
)

func main() {
    config := clusteredBigCache.DefaultClusterConfig()
    // 修改本地端口避免冲突
    config.LocalPort = 8888
    // 设置为加入模式
    config.Join = true
    // 设置要加入的集群节点地址
    config.JoinIp = "127.0.0.1:9911"
    cache := clusteredBigCache.New(config, nil)
    err := cache.Start()
    if err != nil {
        panic(err)
    }
    
    reader := bufio.NewReader(os.Stdin)
    var data string
    for strings.ToLower(data) != "exit" {
        fmt.Print("enter key : ")
        data, _ = reader.ReadString('\n')
        data = strings.TrimSpace(data)
        // 从缓存获取数据,设置160毫秒超时
        value, err := cache.Get(data, time.Millisecond * 160)
        if err != nil {
            fmt.Println(err)
            continue
        }
        fmt.Printf("you got '%s' from the cache\n", value)
    }
}

说明

  • config.Join = true 表示要加入一个现有集群
  • config.JoinIp 指定要加入的集群节点地址
  • cache.Get() 方法接收键和超时时间两个参数

配置示例

可以通过命令行参数配置缓存:

join := flag.String("join", "", "ipAddr:port number of remote server")
localPort := flag.Int("port", 6060, "local server port to bind to")

flag.Parse()

config := clusteredBigCache.DefaultClusterConfig()
if *join != "" {
    config.JoinIp = *join
    config.Join = true
}
config.LocalPort = *localPort

日志记录

clusteredBigCache支持自定义日志记录器:

type myLogger func(...interface{})

func (log myLogger) Info(msg string)  {
    log(msg)
}

func (log myLogger) Warn(msg string)  {
    log(msg)
}

func (log myLogger) Error(msg string)  {
    log(msg)
}

func (log myLogger) Critical(msg string)  {
    log(msg)
}

// 使用自定义日志记录器
cache := clusteredBigCache.New(config, myLogger(log.Println))

被动客户端

可以创建不存储数据的被动客户端:

// 连接到现有集群
cache := clusteredBigCache.NewPassiveClient("linux_box_100","localhost:9090", 8885, 0, 0, 0, nil)

特点

  1. 支持集群 - 多个应用实例可以共享缓存
  2. 支持独立项过期 - 每个缓存项可以设置不同的过期时间
  3. 高性能 - 基于bigcache的高性能实现
  4. 无需外部服务 - 不需要Redis或Memcached等外部服务

许可证

MIT许可证


更多关于golang支持集群和独立项过期的高性能缓存插件clusteredBigCache的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang支持集群和独立项过期的高性能缓存插件clusteredBigCache的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang高性能集群缓存插件:clusteredBigCache

clusteredBigCache是一个基于BigCache的高性能分布式缓存解决方案,支持集群模式和独立项过期功能。它结合了BigCache的内存高效性和集群扩展能力,非常适合需要高性能缓存的分布式系统。

主要特性

  1. 基于BigCache的高性能内存缓存
  2. 支持集群模式,多节点共享缓存
  3. 支持独立项的过期时间设置
  4. 低GC压力,高吞吐量
  5. 简单易用的API接口

安装

go get github.com/allegro/bigcache
go get github.com/redis/go-redis/v9

注意:clusteredBigCache是一个概念性实现,实际使用时可能需要根据具体需求调整。

基本使用示例

package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/allegro/bigcache"
	"github.com/redis/go-redis/v9"
)

type ClusteredBigCache struct {
	localCache  *bigcache.BigCache
	redisClient *redis.Client
}

func NewClusteredBigCache(localConfig bigcache.Config, redisOpts *redis.Options) (*ClusteredBigCache, error) {
	localCache, err := bigcache.NewBigCache(localConfig)
	if err != nil {
		return nil, err
	}

	redisClient := redis.NewClient(redisOpts)

	return &ClusteredBigCache{
		localCache:  localCache,
		redisClient: redisClient,
	}, nil
}

func (c *ClusteredBigCache) Set(ctx context.Context, key string, value []byte, expiration time.Duration) error {
	// 设置本地缓存
	err := c.localCache.Set(key, value)
	if err != nil {
		return err
	}

	// 设置分布式缓存
	return c.redisClient.Set(ctx, key, value, expiration).Err()
}

func (c *ClusteredBigCache) Get(ctx context.Context, key string) ([]byte, error) {
	// 先从本地缓存获取
	value, err := c.localCache.Get(key)
	if err == nil {
		return value, nil
	}

	// 本地缓存不存在,从Redis获取
	value, err = c.redisClient.Get(ctx, key).Bytes()
	if err != nil {
		return nil, err
	}

	// 将Redis中的值存入本地缓存
	_ = c.localCache.Set(key, value)
	return value, nil
}

func (c *ClusteredBigCache) Delete(ctx context.Context, key string) error {
	// 删除本地缓存
	_ = c.localCache.Delete(key)

	// 删除分布式缓存
	return c.redisClient.Del(ctx, key).Err()
}

func main() {
	// 本地缓存配置
	localConfig := bigcache.Config{
		Shards:             1024,
		LifeWindow:         10 * time.Minute,
		CleanWindow:        5 * time.Minute,
		MaxEntriesInWindow: 1000 * 10 * 60,
		MaxEntrySize:       500,
		Verbose:           true,
		HardMaxCacheSize:  8192,
	}

	// Redis配置
	redisOpts := &redis.Options{
		Addr:     "localhost:6379",
		Password: "", // 无密码
		DB:       0,  // 默认DB
	}

	// 创建集群缓存实例
	cache, err := NewClusteredBigCache(localConfig, redisOpts)
	if err != nil {
		log.Fatal(err)
	}

	ctx := context.Background()

	// 设置缓存项,带过期时间
	err = cache.Set(ctx, "myKey", []byte("myValue"), 5*time.Minute)
	if err != nil {
		log.Fatal(err)
	}

	// 获取缓存项
	value, err := cache.Get(ctx, "myKey")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Got value: %s\n", value)

	// 删除缓存项
	err = cache.Delete(ctx, "myKey")
	if err != nil {
		log.Fatal(err)
	}
}

高级功能实现

1. 独立项过期时间

func (c *ClusteredBigCache) SetWithExpiration(ctx context.Context, key string, value []byte, expiration time.Duration) error {
	// 设置本地缓存 - BigCache本身不支持独立项过期,所以使用最短的过期时间
	err := c.localCache.Set(key, value)
	if err != nil {
		return err
	}

	// 设置Redis缓存,带独立过期时间
	return c.redisClient.Set(ctx, key, value, expiration).Err()
}

2. 缓存预热

func (c *ClusteredBigCache) WarmUp(ctx context.Context, keys []string) error {
	for _, key := range keys {
		value, err := c.redisClient.Get(ctx, key).Bytes()
		if err != nil {
			continue
		}
		_ = c.localCache.Set(key, value)
	}
	return nil
}

3. 批量操作

func (c *ClusteredBigCache) MGet(ctx context.Context, keys []string) (map[string][]byte, error) {
	result := make(map[string][]byte)

	// 先检查本地缓存
	for _, key := range keys {
		if value, err := c.localCache.Get(key); err == nil {
			result[key] = value
		}
	}

	// 检查剩余未在本地缓存中找到的键
	var missingKeys []string
	for _, key := range keys {
		if _, found := result[key]; !found {
			missingKeys = append(missingKeys, key)
		}
	}

	if len(missingKeys) > 0 {
		values, err := c.redisClient.MGet(ctx, missingKeys...).Result()
		if err != nil {
			return nil, err
		}

		for i, key := range missingKeys {
			if values[i] == nil {
				continue
			}
			valStr, ok := values[i].(string)
			if !ok {
				continue
			}
			val := []byte(valStr)
			result[key] = val
			_ = c.localCache.Set(key, val)
		}
	}

	return result, nil
}

性能优化建议

  1. 合理配置BigCache参数:根据应用场景调整Shards数量、LifeWindow等参数
  2. 使用连接池:确保Redis客户端配置了合适的连接池大小
  3. 批量操作:尽可能使用批量操作减少网络往返
  4. 本地缓存策略:根据数据访问模式调整本地缓存大小和过期时间
  5. 监控:监控缓存命中率和响应时间,及时调整配置

注意事项

  1. BigCache不支持独立项的过期时间,集群模式下依赖Redis实现此功能
  2. 本地缓存和分布式缓存之间可能存在短暂的不一致
  3. 对于极高吞吐量场景,可能需要考虑更复杂的缓存策略
  4. 确保有适当的缓存淘汰策略,防止内存溢出

clusteredBigCache这种混合缓存策略结合了本地缓存的高性能和分布式缓存的扩展性,非常适合需要高性能缓存的分布式系统。

回到顶部