Redis协议与客户端
- Redis协议与客户端
- 通信协议
- 发送格式
- 返回格式
- Python客户端redis-py
- Go客户端
- 客户端管理
- 客户端API
- 其他客户端配置
- 客户端统计片段
- 常见异常
通信协议
- TCP洗衣
- RESP(redis serialization protocol)redis序列化协议,每行\r\n分隔
发送格式
<参数数量>\r\n
<参数1字节数>\r\n
<参数1>\r\n
...
<参数N字节数>\r\n
<参数N>\r\n
例如发送: set hello world
3
3
set
5
hello
5
world
# 实际发送为 3\r\n3\r\nset\r\n5\r\nhello\r\n5\r\nworld\r\n
返回格式
- 状态回复:第一个字节为
+
,例如 set - 错误回复:第一个字节为
-
- 整数回复:第一个字节为
:
例如 incr - 字符串回复:第一个字节为
$
例如 get - 多条字符串回复:第一个字节为
*
例如 mget
redis源码中的解析如下:
static sds cliFormatReplyTTY(redisReply *r, char *prefix) {
sds out = sdsempty();
switch (r->type) {
case REDIS_REPLY_ERROR:
// 处理错误回复
case REDIS_REPLY_STATUS:
// 处理状态回复
case REDIS_REPLY_INTEGER:
// 处理整数回复
case REDIS_REPLY_STRING:
// 处理字符串回复
case REDIS_REPLY_NIL:
// 处理空
case REDIS_REPLY_ARRAY:
// 处理多条字符串回复
return out;
}
无论是字符串回复还是多字符串回复,如果有nil值都返回$-1
Python客户端redis-py
Github:https://github.com/andymccurdy/redis-py
redis-py的API保留了Redis API的原始风格
安装
pip install redis
基本使用
import redis
client = redis.StrictRedis(host='127.0.0.1', port=6379)
key = "hello"
result = client.set(key, "python-redis")
value = client.get(key)
操作5种数据结构
使用Pipeline
使用Lua脚本
Go客户端
Github: https://github.com/go-redis/redis
API文档:https://godoc.org/github.com/go-redis/redis
go get -u github.com/go-redis/redis
基本操作
client := redis.NewClient(&redis.Options{
Addr: "127.0.0.1:6379",
Password: "", // no password set
DB: 0, // use default DB
})
pong, err := client.Ping().Result()
fmt.Println(pong, err)
err = client.Set("key", "value", 0).Err()
if err != nil {
panic(err)
}
val, err := client.Get("key").Result()
if err != nil {
panic(err)
}
fmt.Println("key", val)
val2, err := client.Get("key2").Result()
if err == redis.Nil {
fmt.Println("key2 does not exists")
} else if err != nil {
panic(err)
} else {
fmt.Println("key2", val2)
}
客户端管理
客户端API
client list
- 客户端标识:
- id:客户端连接唯一标识,随Redis连接自增,重启Redis重置0
- addr:客户端ip和端口
- fd:socket文件描述符,fd=-1表示为redis内部的伪装客户端
- name:客户端名字(client setName, client getName)
- 输入缓冲区:动态调整,不超过1G,超过则断开,不受maxmemory限制
- qbuf:缓冲区总容量
- qbuf-free:缓冲区剩余容量
/* Protocol and I/O related defines */
#define REDIS_MAX_QUERYBUF_LEN (1024*1024*1024) /* 1GB max query buffer. */
出问题概率较低,可以监控输入缓冲区:
- 通过定期指定client list手机qbuf信息找到问题
- 通过info clients找到最大缓冲区
- 输出缓冲区:用于保存结果返回给客户端,client-output-buffer-limit设置
- 客户端类型:
- normal:普通客户端
- slave:slave客户端
- pubsub:发布订阅客户端
- 配置:
client-output-buffer-limit
class:客户端类型
hard limit:客户端输出缓冲区大于hard limit则关闭
客户端输出缓冲区超过了soft limit并且持续了soft limit秒,则关闭
- 默认配置如下:
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
- 组成:
- 固定缓冲区:16KB,返回较小的执行结果
- 动态缓冲区:返回较大的结果
typedef struct redisClient {
// 动态缓冲区列表
list *reply;
// 动态缓冲区列表的长度(对象个数)
unsigned long reply_bytes;
// 固定缓冲区已经使用的字节数
int bufpos;
// 字节数组作为固定缓冲区
char buf[REDIS_REPLY_CHUNK_BYTES];
} redisClient;
- 命令字段:
- obl:固定缓冲区长度
- oll:动态缓冲区列表长度
- omem:使用的字节数
- 预防:
- client list,info clients监控
- 限制普通客户端输出缓冲区, normal 20mb 10mb 120
- 适当增大slave输出缓冲区
- 限制此类命令,如高并发下的monitor命令
- 及时监控内存,内存抖动频繁可能是输出缓冲区过大
- 客户端存活状态
- age:客户端已经连接时间
- idle:最近一次的空闲时间
age==idle说明连接一直空闲
- 客户端的限制
- maxclients:最大客户端连接数,默认10000
- timeout:限制连接的最大空闲时间,单位秒,超时被关闭,默认0
- 可通过config set xx动态设置
- 客户端类型
- 其他
client setName
和client getName
client setName xx
client getName
client kill
client kill ip:port
可以用来手动kill客户端连接
client pause
client pause timeout(毫秒)
阻塞客户端指定的毫秒数
只对normal和pubsub客户端有效,slave客户端无效
可讲redis客户端连接从一个redis节点切换到另一个节点
生成环境慎用
monitor
监控redis正在执行的命令
redis并发量过大,monitor客户端输出缓冲区暴涨,可能瞬间占用大量内存
其他客户端配置
配置项 | 说明 |
timeout | 检测客户端空闲连接超时,0不检测 |
maxclients | 客户端最大连接数 |
tcp-keepalive | 检测TCP连接活性的周期,默认0不检测,建议60,防止大量死连接 |
tcp-backlog | TCP三次握手后,会将接受的连接放入队列,此为队列大小,默认511,一般不调整 |
客户端统计片段
常见异常