概述Redis 的 client-output-buffer-limit 可以用来强制断开无法足够快从 redis 服务器端读取数据的客户端 。保护机制规则如下:
- [hard limit] 大小限制,当某一客户端缓冲区超过设定值后,直接关闭连接 。
- [soft limit] 持续时间限制,当某一客户端缓冲区持续一段时间占用过大空间时关闭连接 。
- 普通 client,包括 monitor
- 主从同步时的 slave client
- Pub/Sub 模式中的 client
client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>
配置实例:# 普通client buffer限制client-output-buffer-limit normal 0 0 0# slave client buffer限制client-output-buffer-limit slave 256mb 64mb 60# pubsub client buffer限制client-output-buffer-limit pubsub 32mb 8mb 60
- client-output-buffer-limit normal 0 0 0
- client-output-buffer-limit slave 256mb 64mb 60
- client-output-buffer-limit pubsub 32mb 8mb 60
概括说明:一般情况下,对于普通客户端,client-output-buffer 是不设限制的,因为 server 只会在 client 请求数据的时候才会发送,不会产生积压 。而在 server 主动发送,client 来处理的场景下,这种一般都是异步处理的,会划出一个缓冲区来“暂存”未处理的数据,若 server 发送数据比 client 处理数据快时,就会发生缓冲区积压 。对于用作 Pub/Sub 和 slave 的客户端,server 会主动把数据推送给他们,故需要设置 client-output-buffer 的限制 。
示例分析下面我们以主从同步时的 slave 客户端,来具体分析下 。在 redis 在主从同步时,master 会为 slave 创建一个输出缓冲区 。在 master 保存 rdb,将 rdb 文件传输给 slave,slave 加载 rdb 完成之前,master 会将接收到的所有写命令,写入到内存中的这个输出缓冲区去 。若 rdb 的保存,传输,加载耗时过长,或者在此期间的写命令过多,则可能会造成超过缓冲区限制,造成 master 和 slave 的连接断开 。此时则需要适当调整下
client-output-buffer-limit slave
配置 。源码浅析-主从同步时 output buffer 使用
基于 redis5.0 版本源码redis server 通过 addReply 将数据发送给客户端,以下源码见 https://github.com/redis/redis/blob/5.0/src/networking.c#L190-L211
/* Add the object 'obj' string representation to the client output buffer. */void addReply(client *c, robj *obj) {if (prepareClientToWrite(c) != C_OK) return;if (sdsEncodedObject(obj)) {if (_addReplyToBuffer(c,obj->ptr,sdslen(obj->ptr)) != C_OK)_addReplyStringToList(c,obj->ptr,sdslen(obj->ptr));} else if (obj->encoding == OBJ_ENCODING_INT) {char buf[32];size_t len = ll2string(buf,sizeof(buf),(long)obj->ptr);if (_addReplyToBuffer(c,buf,len) != C_OK)_addReplyStringToList(c,buf,len);} else {serverPanic("Wrong obj->encoding in addReply()");}}
在函数的开头,会通过prepareClientToWrite(c)
判断是否需要将数据写入客户端的 output buffer 中 。我们看下什么条件下数据会被写入客户端的 output buffer 中,即返回 C_OK
。/* This function is called every time we are going to transmit new data * to the client. The behavior is the following: * * If the client should receive new data (normal clients will) the function * returns C_OK, and make sure to install the write handler in our event * loop so that when the socket is writable new data gets written. * * If the client should not receive new data, because it is a fake client * (used to load AOF in memory), a master or because the setup of the write * handler failed, the function returns C_ERR. * * The function may return C_OK without actually installing the write * event handler in the following cases: * * 1) The event handler should already be installed since the output buffer *already contains something. * 2) The client is a slave but not yet online, so we want to just accumulate *writes in the buffer but not actually sending them yet. * * Typically gets called every time a reply is built, before adding more * data to the clients output buffers. If the function returns C_ERR no * data should be appended to the output buffers. */int prepareClientToWrite(client *c) {/* If it's the Lua client we always return ok without installing any* handler since there is no socket at all. */if (c->flags & (CLIENT_LUA|CLIENT_MODULE)) return C_OK;/* CLIENT REPLY OFF / SKIP handling: don't send replies. */if (c->flags & (CLIENT_REPLY_OFF|CLIENT_REPLY_SKIP)) return C_ERR;/* Masters don't receive replies, unless CLIENT_MASTER_FORCE_REPLY flag* is set. */if ((c->flags & CLIENT_MASTER) &&!(c->flags & CLIENT_MASTER_FORCE_REPLY)) return C_ERR;if (c->fd <= 0) return C_ERR; /* Fake client for AOF loading. *//* Schedule the client to write the output buffers to the socket, unless* it should already be setup to do so (it has already pending data). */if (!clientHasPendingReplies(c)) clientInstallWriteHandler(c);/* Authorize the caller to queue in the output buffer of this client. */return C_OK;}/* Return true if the specified client has pending reply buffers to write to * the socket. */int clientHasPendingReplies(client *c) {return c->bufpos || listLength(c->reply);}void clientInstallWriteHandler(client *c) {/* Schedule the client to write the output buffers to the socket only* if not already done and, for slaves, if the slave can actually receive* writes at this stage. */if (!(c->flags & CLIENT_PENDING_WRITE) &&(c->replstate == REPL_STATE_NONE ||(c->replstate == SLAVE_STATE_ONLINE && !c->repl_put_online_on_ack))){c->flags |= CLIENT_PENDING_WRITE;listAddNodeHead(server.clients_pending_write,c);}}
经验总结扩展阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Docker | redis安装及测试
- 七 SpringBoot - Redis 缓存
- redis bitmap数据结构之java对等操作
- 带你认识JDK8中超nice的Native Memory Tracking
- Redis高可用之主从复制原理演进分析
- flinksql读写redis
- 追求性能极致:Redis6.0的多线程模型
- 教你认识小儿和妇女病例
- 如何看待焦虑症
- spring boot集成redis基础入门