Go 是构建高性能 Web 应用程序的优秀语言,而高性能 Web 应用程序通常需要集中式缓存。
当今流行的 Go 库缺乏对内存高效流的支持。相反,他们提供了[]byte方式,如果您缓存小对象,这不是问题,但如果您缓存大于 1kb 的对象,则[]byte则慢了。
/ This code uses https://github.com/redis/go-redis, but the same // restrictions apply with Rueidis and Redigo. func redisHandler(w http.ResponseWriter, r *http.Request) { ctx := context.Background()
// Extract key from RequestURI key := strings.TrimLeft(r.RequestURI, "/")
//以字节片形式从 Redis 获取值 val, err := rdb.Get(ctx, key).Bytes() if err == redis.Nil { http.Error(w, "Key not found in Redis", http.StatusNotFound) return } else if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return }
_, err = w.Write(val) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } }
|
流式读取简介
Redis 协议并不妨碍我们构建流 API。因此,我编写了redjet,一个面向性能的 Redis 库。
使用redjet,您可以像这样编写代码:
func redisHandler(w http.ResponseWriter, r *http.Request) { ctx := context.Background()
// 从 RequestURI 中提取键值 key := strings.TrimLeft(r.RequestURI, "/")
//将值直接从 Redis 流式传输到响应。 _, err := rdb.Command("GET", key).WriteTo(w) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } }
|
流式写入
流行的 Redis 库在向 Redis 写入值时也遇到同样的问题。[]byte它们要求您在通过网络发送之前将整个值保存在内存中。
使用redjet,您可以将值流式传输到 Redis,如下所示:
fi := strings.NewReader("Some file contents")
err := rdb.Command("SET", "key", fi).Ok()
|
// handle error有一个重要的警告。在 Redis 协议中,值是有长度前缀的,所以我们不能流式传输 vanilla io.Reader。我推测这是流行库不支持流写入的主要原因。
为了解决这个问题,redjet需要将其redjet.LenReader定义为:
type LenReader interface { Len() int io.Reader }
|
可使用 redjet.NewLenReader 快速创建。
标准库中的某些类型(例如 bytes.Reader、strings.Reader和 bytes.Buffer)可以方便地隐式实现redjet.LenReader.
性能测试
对 redjet 进行了基准测试:
完整的基准测试结果可在此处获取