一个类型安全的`Golang Redis`客户端,支持`RESP2/RESP3`协议

timestee · 2022-09-13 12:08:45 · 1447 次点击 · 大约8小时之前 开始浏览    置顶
这是一个创建于 2022-09-13 12:08:45 的主题,其中的信息可能已经有所发展或是发生改变。

redisson

一个类型安全的Golang Redis客户端,支持RESP2/RESP3协议

github: https://github.com/sandwich-go/redisson

特征

  • 开发模式下,检查Redis命令的版本要求
  • 开发模式下,检查在当前版本已过期的Redis命令
  • 开发模式下,检查多Key是否属于同一Redis
  • 开发模式下,检查禁止使用的Redis命令
  • 监控Redis命令耗时时间
  • 监控Redis连接状态
  • 监控Redis RESP3客户端缓存命中状态
  • 支持RESP2/RESP3协议
  • 支持Redis RESP3客户端缓存
  • Redis RESP3客户端命令自动进行pipeline
  • Redis RESP3客户端自动管理阻塞的连接

要求

  • Golang >= 1.18

如果不能升级Golang1.18,请使用redisson/version/0.1版本。

基础库

链接

开始

package main

import (
    "context"
    "github.com/sandwich-go/redisson"
)

func main() {
    c := redisson.MustNewClient(redisson.NewConf(
          redisson.WithResp(redisson.RESP3), 
          redisson.WithDevelopment(false), 
    ))
    defer c.Close()

    ctx := context.Background()

    // SET key val NX
    _ = c.SetNX(ctx, "key", "val", 0).Err()
    // GET key
    _ = c.Get(ctx, "key").Val()
}

检查

仅在development模式下才会检查

版本检查

如果 Redis < 6.0

c := redisson.MustNewClient(redisson.NewConf(
      redisson.WithResp(redisson.RESP3), 
      redisson.WithDevelopment(true), 
))
defer c.Close()

res := c.Set(ctx, "key", "10", -1)

输出:

[SET KEEPTTL]: redis command are not supported in version "5.0.0", available since 6.0.0

:warning: 在development模式下,若校验版本失败,则会发生Panic

检查过期

如果 Redis >= 4.0

c := redisson.MustNewClient(redisson.NewConf(
      redisson.WithResp(redisson.RESP3), 
      redisson.WithDevelopment(true), 
))
defer c.Close()

res := c.HMSet(ctx, "key", "10")

输出:

[HMSET]: As of Redis version 4.0.0, this command is regarded as deprecated.
It can be replaced by HSET with multiple field-value pairs when migrating or writing new code.

检查槽位

c := redisson.MustNewClient(redisson.NewConf(
      redisson.WithResp(redisson.RESP3), 
      redisson.WithDevelopment(true), 
))
defer c.Close()

res := c.MSet(ctx, "key1", "10", "key2", "20")

输出:

[MSET]: multiple keys command with different key slots are not allowed

:warning: 在development模式下,若多个key分布在不同的slot中,则会发生Panic

命令禁用

c := redisson.MustNewClient(redisson.NewConf(
      redisson.WithResp(redisson.RESP3), 
      redisson.WithDevelopment(true), 
))
defer c.Close()

res := c.ClusterFailover(ctx)

输出:

[CLUSTER FAILOVER]: redis command are not allowed

:warning: 在development模式下,若使用禁用命令,则会发生Panic

禁用命令集

  • CLUSTER ADDSLOTS
  • CLUSTER ADDSLOTSRANGE
  • CLUSTER DELSLOTS
  • CLUSTER DELSLOTSRANGE
  • CLUSTER FAILOVER
  • CLUSTER FORGET
  • CLUSTER MEET
  • CLUSTER REPLICATE
  • CLUSTER RESET HARD/SOFT
  • CLUSTER SAVECONFIG
  • CLUSTER SLAVES
  • KEYS
  • MIGRATE
  • BGREWRITEAOF
  • BGSAVE
  • CONFIG GET
  • CONFIG RESETSTAT
  • CONFIG REWRITE
  • CONFIG SET
  • FLUSHALL ASYNC/SYNC
  • FLUSHDB ASYNC/SYNC
  • SAVE
  • SHUTDOWN NOSAVE/SAVE
  • SLAVEOF
  • SELECT

监控

导入Grafana dashboard id 16768

import (
    "github.com/prometheus/client_golang/prometheus"
    "github.com/sandwich-go/redisson"
)

var DefaultPrometheusRegistry = prometheus.NewRegistry()

c := redisson.MustNewClient(redisson.NewConf(
      redisson.WithResp(redisson.RESP3),
      redisson.WithEnableMonitor(true),
))
defer c.Close()

c.RegisterCollector(func(c prometheus.Collector) {
    DefaultPrometheusRegistry.Register(c)
})

image.png

自动pipeline

所有发送到单个Redis节点的非阻塞命令都会通过一个tcp连接自动pipeline传输, 这减少了整体往返和系统调用,并获得了更高的吞吐量。

注意:仅在使用Redis RESP3客户端时支持。

客户端缓存

始终启用服务器辅助客户端缓存的加入模式

c.Cache(time.Minute).Get(ctx, "key").Val()

需要显式指定客户端TTL,因为Redis服务器在以下情况下可能无法及时发送失效消息: 服务器上的密钥已过期。请遵循 #6833#6867

尽管需要显式的指定客户端TTLCache()仍然向服务器发送PTTL命令,并确保客户端TTL不长于服务器端TTL

注意:仅在使用Redis RESP3客户端时支持。

Benchmark

环境

Benchmarking Result

Single, Parallel mode, Get Command
+---------------------------------------------------+-----------+-------+-------+-----------+
| Single Parallel(128) Get                          | iteration | ns/op | B/op  | allocs/op |
+===================================================+===========+=======+=======+===========+
| sandwich-go/redisson/RESP2:Val(64):Pool(100)      | 362365    | 6136  | 279   |        6  |
| sandwich-go/redisson/RESP2:Val(64):Pool(1000)     | 504202    | 4731  | 286   |        6  |
| sandwich-go/redisson/RESP2:Val(256):Pool(100)     | 362181    | 6334  | 487   |        6  |
| sandwich-go/redisson/RESP2:Val(256):Pool(1000)    | 481341    | 4946  | 495   |        6  |
| sandwich-go/redisson/RESP2:Val(1024):Pool(100)    | 332634    | 6822  | 1351  |        6  |
| sandwich-go/redisson/RESP2:Val(1024):Pool(1000)   | 451609    | 5299  | 1360  |        6  |
| sandwich-go/redisson/RESP3:Val(64):Pool(100)      | 1208716   | 1923  | 320   |        4  |
| sandwich-go/redisson/RESP3:Val(256):Pool(100)     | 1000000   | 2013  | 512   |        4  |
| sandwich-go/redisson/RESP3:Val(1024):Pool(100)    | 728786    | 2816  | 1281  |        4  |
| rueian/rueidis/rueidiscompat:Val(64):Pool(100)    | 1253146   | 1847  | 256   |        4  |
| rueian/rueidis/rueidiscompat:Val(256):Pool(100)   | 1000000   | 2034  | 448   |        4  |
| rueian/rueidis/rueidiscompat:Val(1024):Pool(100)  | 792254    | 2686  | 1217  |        4  |
| go-redis/redis/v8:Val(64):Pool(100)               | 369186    | 6098  | 279   |        6  |
| go-redis/redis/v8:Val(64):Pool(1000)              | 506796    | 4750  | 286   |        6  |
| go-redis/redis/v8:Val(256):Pool(100)              | 357454    | 6266  | 487   |        6  |
| go-redis/redis/v8:Val(256):Pool(1000)             | 486217    | 4919  | 495   |        6  |
| go-redis/redis/v8:Val(1024):Pool(100)             | 331382    | 6779  | 1351  |        6  |
| go-redis/redis/v8:Val(1024):Pool(1000)            | 452067    | 5307  | 1360  |        6  |
| mediocregopher/radix/v4:Val(64):Pool(100)         | 596540    | 4284  | 26    |        1  |
| mediocregopher/radix/v4:Val(64):Pool(1000)        | 589083    | 4902  | 54    |        1  |
| mediocregopher/radix/v4:Val(256):Pool(100)        | 576108    | 4384  | 27    |        1  |
| mediocregopher/radix/v4:Val(256):Pool(1000)       | 597157    | 4993  | 54    |        1  |
| mediocregopher/radix/v4:Val(1024):Pool(100)       | 573411    | 4539  | 27    |        1  |
| mediocregopher/radix/v4:Val(1024):Pool(1000)      | 559611    | 5062  | 56    |        1  |
| joomcode/redispipe:Val(64):Pool(100)              | 1109589   | 2137  | 168   |        5  |
| joomcode/redispipe:Val(256):Pool(100)             | 1000000   | 2170  | 377   |        5  |
| joomcode/redispipe:Val(1024):Pool(100)            | 958350    | 2442  | 1241  |        5  |
+---------------------------------------------------+-----------+-------+-------+-----------+

image.png

Cluster, Parallel mode, Get Command
+---------------------------------------------------+-----------+-------+-------+-----------+ 
| Cluster Parallel(128) Get                         | iteration | ns/op | B/op  | allocs/op | 
+===================================================+===========+=======+=======+===========+ 
| sandwich-go/redisson/RESP2:Val(64):Pool(100)      | 361689    | 6246  | 279   |        6  |
| sandwich-go/redisson/RESP2:Val(64):Pool(1000)     | 494625    | 4819  | 286   |        6  |
| sandwich-go/redisson/RESP2:Val(256):Pool(100)     | 353413    | 6439  | 487   |        6  |
| sandwich-go/redisson/RESP2:Val(256):Pool(1000)    | 478305    | 5035  | 494   |        6  |
| sandwich-go/redisson/RESP2:Val(1024):Pool(100)    | 324940    | 6992  | 1351  |        6  |
| sandwich-go/redisson/RESP2:Val(1024):Pool(1000)   | 441291    | 5472  | 1360  |        6  |
| sandwich-go/redisson/RESP3:Val(64):Pool(100)      | 1036126   | 2275  | 320   |        4  |
| sandwich-go/redisson/RESP3:Val(256):Pool(100)     | 1008175   | 2420  | 513   |        4  |
| sandwich-go/redisson/RESP3:Val(1024):Pool(100)    | 766168    | 2906  | 1282  |        4  |
| rueian/rueidis/rueidiscompat:Val(64):Pool(100)    | 946216    | 2266  | 256   |        4  |
| rueian/rueidis/rueidiscompat:Val(256):Pool(100)   | 924811    | 2292  | 448   |        4  |
| rueian/rueidis/rueidiscompat:Val(1024):Pool(100)  | 856582    | 2802  | 1218  |        4  |
| go-redis/redis/v8:Val(64):Pool(100)               | 351850    | 6251  | 279   |        6  |
| go-redis/redis/v8:Val(64):Pool(1000)              | 489259    | 4821  | 286   |        6  |
| go-redis/redis/v8:Val(256):Pool(100)              | 356703    | 6385  | 487   |        6  |
| go-redis/redis/v8:Val(256):Pool(1000)             | 478236    | 5012  | 494   |        6  |
| go-redis/redis/v8:Val(1024):Pool(100)             | 333362    | 6972  | 1351  |        6  |
| go-redis/redis/v8:Val(1024):Pool(1000)            | 443264    | 5386  | 1360  |        6  |
| mediocregopher/radix/v4:Val(64):Pool(100)         | 477573    | 4598  | 113   |        2  |
| mediocregopher/radix/v4:Val(64):Pool(1000)        | 386779    | 5431  | 114   |        2  |
| mediocregopher/radix/v4:Val(256):Pool(100)        | 459818    | 4737  | 113   |        2  |
| mediocregopher/radix/v4:Val(256):Pool(1000)       | 383200    | 5656  | 114   |        2  |
| mediocregopher/radix/v4:Val(1024):Pool(100)       | 451070    | 4911  | 114   |        2  |
| mediocregopher/radix/v4:Val(1024):Pool(1000)      | 356745    | 5745  | 114   |        2  |
| joomcode/redispipe:Val(64):Pool(100)              | 1091751   | 2147  | 170   |        5  |
| joomcode/redispipe:Val(256):Pool(100)             | 1088572   | 2298  | 379   |        5  |
| joomcode/redispipe:Val(1024):Pool(100)            | 800530    | 2548  | 1246  |        5  |
+---------------------------------------------------+-----------+-------+-------+-----------+

image.png

详见 Benchmark Detail Result

特征 开发模式下,检查Redis命令的版本要求 开发模式下,检查在当前版本已过期的Redis命令 开发模式下,检查多Key是否属于同一Redis槽 开发模式下,检查禁止使用的Redis命令 监控Redis命令耗时时间 监控Redis连接状态 监控Redis RESP3客户端缓存命中状态 支持RESP2/RESP3协议 支持Redis RESP3客户端缓存 Redis RESP3客户端命令自动进行pipeline Redis RESP3客户端自动管理阻塞的连接 要求 Golang >= 1.18 如果不能升级Golang至1.18,请使用redisson/version/0.1版本。

基础库 RESP2, 使用 go-redis/redis. RESP3, 使用 rueian/rueidis. 链接 English 中文文档 开始 package main

import ( "context" "github.com/sandwich-go/redisson" )

func main() { c := redisson.MustNewClient(redisson.NewConf( redisson.WithResp(redisson.RESP3), redisson.WithDevelopment(false), )) defer c.Close()

ctx := context.Background()

// SET key val NX
_ = c.SetNX(ctx, "key", "val", 0).Err()
// GET key
_ = c.Get(ctx, "key").Val()

} 检查 仅在development模式下才会检查

版本检查 如果 Redis < 6.0

c := redisson.MustNewClient(redisson.NewConf( redisson.WithResp(redisson.RESP3), redisson.WithDevelopment(true), )) defer c.Close()

res := c.Set(ctx, "key", "10", -1) 输出:

[SET KEEPTTL]: redis command are not supported in version "5.0.0", available since 6.0.0 ⚠️ 在development模式下,若校验版本失败,则会发生Panic

检查过期 如果 Redis >= 4.0

c := redisson.MustNewClient(redisson.NewConf( redisson.WithResp(redisson.RESP3), redisson.WithDevelopment(true), )) defer c.Close()

res := c.HMSet(ctx, "key", "10") 输出:

[HMSET]: As of Redis version 4.0.0, this command is regarded as deprecated. It can be replaced by HSET with multiple field-value pairs when migrating or writing new code. 检查槽位 c := redisson.MustNewClient(redisson.NewConf( redisson.WithResp(redisson.RESP3), redisson.WithDevelopment(true), )) defer c.Close()

res := c.MSet(ctx, "key1", "10", "key2", "20") 输出:

[MSET]: multiple keys command with different key slots are not allowed ⚠️ 在development模式下,若多个key分布在不同的slot中,则会发生Panic

命令禁用 c := redisson.MustNewClient(redisson.NewConf( redisson.WithResp(redisson.RESP3), redisson.WithDevelopment(true), )) defer c.Close()

res := c.ClusterFailover(ctx) 输出:

[CLUSTER FAILOVER]: redis command are not allowed ⚠️ 在development模式下,若使用禁用命令,则会发生Panic

禁用命令集 CLUSTER ADDSLOTS CLUSTER ADDSLOTSRANGE CLUSTER DELSLOTS CLUSTER DELSLOTSRANGE CLUSTER FAILOVER CLUSTER FORGET CLUSTER MEET CLUSTER REPLICATE CLUSTER RESET HARD/SOFT CLUSTER SAVECONFIG CLUSTER SLAVES KEYS MIGRATE BGREWRITEAOF BGSAVE CONFIG GET CONFIG RESETSTAT CONFIG REWRITE CONFIG SET FLUSHALL ASYNC/SYNC FLUSHDB ASYNC/SYNC SAVE SHUTDOWN NOSAVE/SAVE SLAVEOF SELECT 监控 导入Grafana dashboard id 16768

import ( "github.com/prometheus/client_golang/prometheus" "github.com/sandwich-go/redisson" )

var DefaultPrometheusRegistry = prometheus.NewRegistry()

c := redisson.MustNewClient(redisson.NewConf( redisson.WithResp(redisson.RESP3), redisson.WithEnableMonitor(true), )) defer c.Close()

c.RegisterCollector(func(c prometheus.Collector) { DefaultPrometheusRegistry.Register(c) }) grafana_dashboard

自动pipeline 所有发送到单个Redis节点的非阻塞命令都会通过一个tcp连接自动pipeline传输, 这减少了整体往返和系统调用,并获得了更高的吞吐量。

注意:仅在使用Redis RESP3客户端时支持。

客户端缓存 始终启用服务器辅助客户端缓存的加入模式

c.Cache(time.Minute).Get(ctx, "key").Val() 需要显式指定客户端TTL,因为Redis服务器在以下情况下可能无法及时发送失效消息: 服务器上的密钥已过期。请遵循 #6833 和 #6867

尽管需要显式的指定客户端TTL,Cache()仍然向服务器发送PTTL命令,并确保客户端TTL不长于服务器端TTL。

注意:仅在使用Redis RESP3客户端时支持。


有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1447 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传