4.4、客户端管理

客户端管理

Redis提供了客户端相关 API对其状态进行监控和管理,本节将深入介绍各个API的使
用方法以及开发运维中可能遇到的问题。

  1. 客户端API
  • client list

    client list命令能列出与Redis服务端相连的所有客户端连接信息。此命令输
    出结果每一行代表一个客户端的信息,可以看到每行包含了十几个属性,它们是
    每个客户端的一些执行状态,理解这些属性对于Redis的开发和运维人员非常有
    帮助。下面选择几个重要的属性进行说明,其余通过表格的形式进行展示。

    (1)标识:id、addr、fd、name

    这四个属性属于客户端的标识:

    • id:客户端连接的唯一标识,这个id是随着Redis的连接自增的,重启Redis后
      会重置为0。

    • addr:客户端连接的ip和端口

    • fd:socket的文件描述符,与lsof命令结果中的fd是同一个,如果fd=-1代表
      当前客户端不是外部客户端,而是Redis内部的伪装客户端

    • name:客户端的名字,后面的client setName和clinet getName两个命令会
      对其进行说明。

    (2)输入缓冲区:qbuf、qbuf-free

    Redis为每个客户端分配了输入缓冲区,它的作用是将客户端发送的命令临时保
    存,同时Redis会输入缓冲区拉取命令并执行,输入缓冲区为客户端发送命令到
    Redis执行命令提供了缓冲功能。

    client list中qbuf和qbuf-free分别代表这个缓冲区的总容量和剩余容量,
    Redis没有提供相应的配置来规定每个缓冲区的大小,输入缓冲区会根据输入内
    容大小的不同动态调整,只是要求每个客户端缓冲区的大小不能超时1G,超过后
    客户端将被关闭。

    输入缓冲使用不当会产生两个问题:

    • 一旦某个客户端的输入缓冲区超过1G,客户端将会被关闭。

    • 输入缓冲区不受maxmemory控制,假设一个Redis实例设置了maxmemory为
      4G,已经存储了2G数据,但是如果此时输入缓冲区使用了3G,已经超过
      maxmemory限制,可能会产生数据丢失、键值淘汰、OOM等情况。

    输入缓冲区过大主要是因为Redis的处理速度跟不上输入缓冲区的输入速度,并
    且每次进入输入缓冲区的命令包含了大量bigkey,从而造成了输入缓冲区过大的
    情况。还有一种情况就是Redis发生了阻塞,短期内不能处理命令,造成客户端
    输入的命令积压在了输入缓冲区,造成了输入缓冲区过大。监控呼入缓冲区异常
    的方法有两种:

    • 通过定期执行client list命令,收集qbuf和qbuf-free找到异常的连接记录
      并分析,最终找到可能出问题的客户端。

    • 通过info命令的info clients模块,找到最大的输入缓冲区,例如下面命令
      中的其中client_biggest_input_buf代表最大的输入缓冲区,例如可以设置超
      过10M就进行报警:

    127.0.0.1:6379> info clients
    # Clients
    conneted_clients:1414
    client_longest_output_list:0
    clinet_biggest_input_buf:2097152
    blocked_clients:0
    

    下表对两种方法进行了对比:

    命令 优点 缺点
    client list 能精准肥西每个客户端来定位问题 执行速度较慢(尤其在连接数

    较多的情况下),频繁执行存在阻塞Redis的可能
    info clients|执行速度比client list快,分析过程较为简单|不能精准定位
    到客户端,不能显示所有输入缓冲区的总量,只能显示最大量

    运维提示:输入缓冲区问题出现概率比较低,但是也要做好防范,在开发中要减
    少bigkey、减少Redis阻塞、合理的监控报警

    (3)输出缓冲区:obl、oll、omem

    Redis为每个客户端分配了输出缓冲区,他的作用是保存命令执行的结果返回给
    客户端,为Redis和客户端交互返回结果听缓冲。

    与输入缓冲区不同的是,输入缓冲区的容量可以通过参数
    client-output-buffer-limit来进行设置,并且输出缓冲区做的更加细致,按
    照客户端的不同分为三种:普通客户端、发布订阅客户端、slave客户端。

    对应配置规则是:

    client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>
    
    • < class >:客户端类型,分为三种。normal、slava、pubsub
    • < hard limit >:如果客户端使用的输出缓冲区大于< hard limit >,客户
      端会被立即关闭。
    • < soft limit >和< soft seconds >:如果客户端使用的输出缓冲区超过了
      < soft limit >并持续了< soft limit >秒,客户端会被立即关闭。

    Redis的默认配置是:

    client-output-buffer-limit normal 0 0 0
    client-output-buffer-limit slave 256mb 64mb 60
    client-output-buffer-limit pubsub 32mb 8mb 60
    

    和输入缓冲区相同的是,输入缓冲区也不会受到maxmemory的限制,如果使用不
    当同样会造成maxmemory用满产生的数据丢失、键值淘汰、OOM等情况。

    实际上输出缓冲区由两部分组成:固定缓冲区(16KB)和动态缓冲区,其中固定
    缓冲区返回比较小的执行结果,而动态缓冲区返回比较大的结果,例如大的字符
    串、hgetall、smemebers命令的结果等,通过Redis源码中redis.h的
    redisClient结构体可以看到两个缓冲区的实现细节:

    typedef struct redisClient{
        //动态缓冲区列表
        list *reply;
        //动态缓冲区列表的长度(对象个数)
        unsigned long reply_bytes;
        //固定缓冲区已经使用的字节数
        int bufpos;
        //字节数组作为固定缓冲区
        char buf[REDIS_REPLY_CHUNK_BYTES];
    } redisClient;
    

    固定缓冲区使用的是字节数组,动态缓冲区使用的是列表。当固定缓冲区存满后
    会将Redis新的返回结果存放在动态缓冲区的队列中,队列中的每个对象就是每
    个返回结果。

    client list中的obl’代表固定缓冲区的长度,oll代表动态缓冲区列表的长
    度,omem代表使用的字节数。

    监控输出缓冲区的方法依然有两种:

    • 通过定期执行client list命令,收集obl、oll、omem找到异常的连接记录并
      分析,最终找到可能出问题的客户端。

    • 通过info命令的info clients模块,找到输出缓冲区列表最大对象数。例
      如:

    127.0.0.1:6379> info clients
    # Clients
    connected_clients:502
    client_longest_output_list:4869
    client_biggest_input_buf:0
    blocked_clients:0
    

    其中,client_longest_output_list代表输出缓冲区列表最大对象数,这两种
    统计方法的优劣势和输入缓冲区是一样的,这里就不再赘述了。相比于输入缓冲
    区,输出缓冲区出现异常的概率相对会比较大,预防的方法如下:

    • 进行上述监控,设置阀值,超过阀值及时处理。
    • 限制普通客户端输出缓冲区的< hard limit >< soft limit >
      < soft seconds >,把错误扼杀在摇篮里。
    • 适当增大slave的输出缓冲区的< hard limit >< soft limit >
      < soft seconds >,如果master节点写入较大,slave客户端的输出缓冲区可
      能会比较大,一旦slave客户端连接因为输出缓冲区溢出被kill,会造成复制重
      连。
    • 限制容易让输出缓冲区增大的命令,例如,高并发下的monitor命令就是一个
      危险的命令。
    • 及时监控内存,一旦发现内存抖动频繁,可能就是输出缓冲区过大。

    (4)客户端的存活状态

    client list中的age和idle分别代表当前客户端已经连接的时间和最近一次的
    空闲时间,为了更加直观地描述age和idle,下面用一个例子进行说明:

    String key = "hello";
    // 1)生成jedis,并执行get操作
    Jedis jedis = new Jedis("127.0.0.1", 6379);
    System.out.println(jedis.get(key));
    // 2)休息10秒
    TimeUnit.SECONDS.sleep(10);
    // 3)执行新的操作ping
    System.out.println(jedis.ping());
    // 4)休息5秒
    TimeUnit.SECONDS.sleep(5);
    // 5)关闭jedis连接
    jedis.close();
    

    (5)客户端的限制maxclients和timeout

    Redis提供了maxclients参数来限制最大客户端连接数,一旦连接数超过
    maxclients,新的连接将被拒绝。maxclients默认值是10000,可以通过info
    clients来查询当前Redis的连接数:

    127.0.0.1:6379> info clients
    # Clients
    connected_clients:1414
    ...
    

    可以通过config set maxclients对最大客户端连接数进行动态设置:

    127.0.0.1:6379> config get maxclients
    1) "maxclients"
    2) "10000"
    127.0.0.1:6379> config set maxclients 50
    OK
    127.0.0.1:6379> config set maxclients
    1) "maxclients"
    2) "50"
    

    一般来说maxclient=10000在发部分场景下已经绝对够用,但是某些情况由于业
    务方使用不当(例如没有主动关闭连接)可能存在大量idle连接,无论是从网络
    连接的成本还是超过maxclients的后果来说都不是什么好事,因此Redis提供了
    timeout(单位为秒)参数来限制连接的最大空闲时间,一旦客户端连接的idle
    时间超过了timeout,连接将会被关闭,例如设置timeout为30秒:

    #Redis默认的timeout是0,也就是不会检测客户端的空闲
    127.0.0.1:6379> config set timeout 30
    OK
    

    Redis的默认配置给出的timeout=0,在这种情况下客户端基本不会出现异常,这
    是基于对客户端开发的一种保护。例如很多开发人员在使用JedisPool是不会对
    连接池对象做空闲检测和验证,如果设置了timeout>0,可能就会出现异常,对
    应用业务造成一定影响,但是如果Reids的客户端使用不当或者客户端本身的一
    些问题,造成没有及时释放客户端连接,可能会造成大量的idle连接占据着很多
    连接资源,一旦超过maxclients后果也是不堪设想。所以在实际开发和运维中,
    需要将timeout设置成大于0,例如可以设置为300秒,同时在客户端使用上添加
    空闲检测和验证等等措施,例如JedisPool使用common-poll提供了三个属性:
    minEvictableIdleTimeMillis、testWhileIdle、
    timeBetweenEvictionRunsMillis.

    (6)客户端类型

    client list中的flag是用于标识当前客户端的类型,例如flag=S代表当前客户
    端是slave客户端、flag=N代表当前是普通客户端,flag=O代表当前客户端正在
    执行monitor命令,下表例如了11中客户端类型:

    序号 客户端类型 说明
    1 N 普通客户端
    2 M 当前客户端是master节点
    3 S 当前客户端是slave节点
    4 O 当前客户端正在执行monitor命令
    5 x 当前客户端正在执行事务
    6 b 当前客户端正在等待阻塞事件
    7 i 当前客户端正在等待VM I/O,但是此状态目前已经废弃不用
    8 d 一个受监视的键已被修改,EXEC命令将失败
    9 u 客户端未被阻塞
    10 c 回复完整输出后,关闭连接
    11 A 尽可能快的关闭连接

    (7)其他

    下表列出client list属性:

    序号 参数 含义
    1 id 客户端连接id
    2 addr 客户端连接IP和端口
    3 fd socket的文件描述符
    4 name 客户端连接名
    5 age 客户端连接存活时间
    6 idle 客户端连接空闲时间
    7 flags 客户端类型标识
    8 db 当前客户端正在使用的数据库索引下标
    9 sub/psub 当前客户端订阅的频道或者模式数
    10 multi 当前事务中已执行命令个数
    11 qbuf 输入缓冲区总容量
    12 qbuf-free 输入缓冲区剩余总容量
    13 obl 固定缓冲区的长度
    14 oll 动态缓冲区列表的长度
    15 omem 固定缓冲区和动态缓冲区使用的容量
    16 events 文件描述符事件(r/w):r和w分别代表客户端套接字可读和可写
    17 cmd 当前客户端最后一次执行的命令,不包含参数
    • client setName和client getName

      client setName xx
      client getName
      

      client setName用于给客户端设置名字,这样比较容易标识出客户端的来
      源,例如将当前客户端命名为test_client,可以执行如下操作:

      127.0.0.1:6379> client setName test_client
      OK
      

      如果想直接查看当亲客户端的name,可以使用client getName命令,例如
      下面的操作:

      127.0.0.1:6379> client getName
      "test_client"
      

      client getName和setName命令可以作为标识客户端来源的一种方式,但
      是通常来讲,在Redis只有一个应用方使用的情况下,IP和端口作为标识会
      更加清晰。当多个应用方共同使用一个Redis,那么此时client setName可
      以作为标识客户端的一个依据。

    • client kill

      client kill ip:port

      此命令用于杀掉指定IP地址和端口的客户端,例如想杀掉127.0.0.1:52343
      的客户端,可以执行:

      127.0.0.1:6379> client kill 127.0.0.1:52343
      OK
      
    • clinet pause

      client pause timeout(毫秒)

      例如一个客户端执行:

      127.0.0.1:6379> client pause 10000
      OK
      

      在另一个客户端执行ping命令,发现整个ping命令执行了9.72秒:

      127.0.0.1:6379> ping
      PONG
      (9.72s)
      

      该命令可以在如下场景起到作用:

      • clinet pause只对普通和发布订阅客户端有效,对于主从复制(从节点
        内部伪装了一个客户端)是无效的,也就是此期间主从复制是正常进行的,
        所以此命令可以用来让主从复制保持一致。

      • client pause可以用一种可控的方式将客户端连接从一个Redis节点切换
        到另一个Redis节点

      需要注意的是在生产环境中,暂停客户端成本非常高。

    • monitor

      monitor命令用于监控Redis正在执行的命令,我们打开两个redis-cli,
      一个执行set get ping命令,另一个执行monitor命令。可以看到monitor
      命令能够监听其他客户端正在执行的命令,并记录详细的时间戳。

      monitor的作用很明显,如果开发和运维人员想监听Redis正在执行的命
      令,就可以用monitor命令,但事实并非如此美好,每个客户端都有自己的
      输出缓冲区,既然monitor能监听到所有的命令,一旦Redis的并发量过
      大,monitor客户端的输出缓冲会暴涨,可能瞬间会占用大量内存。

  1. 客户端相关配置

    此处将对剩余配置进行介绍:

    • timeout:检测客户端空闲连接的超时时间,一旦idle时间达到了timeout,
      客户端将会被关闭,如果设置为0就不进行检测。

    • maxclients:客户端最大连接数,这个参数会受到操作系统设置的限制。

    • tcp-keepalive: 检测TCP连接活性的周期,默认值是0,也就是不进行检
      测,如果需要设置,建议为60,那么Redis会每隔60秒对它创建的TCP连接进行活
      性检测,防止大量死连接占用系统资源。

    • tcp-backlog: TCP三次握手后,会将接受的连接放入队列中,tcp-backlog
      就是队列的大小,它在Redis中的默认值是511.通常来讲这个参数不需要调整,
      但是这个参数会受到操作系统的影响,例如在Linux操作系统中,如果/proc/
      sys/net/core/somaxconn小于tcp-backlog,那么在Redis启动时会看到如下
      日志,并建议将/proc/sys/net/core/somaxconn设置更大。

    修改方法也非常简单,只需要执行以下命令:

    echo 511 > /proc/sys/net/core/somaxconn

  2. 客户端统计片段

    例如下面就是一次info clients的执行结果:

    127.0.0.1:6379> info clients
    # Clients
    connected_clients:1414
    client_longest_output_list:0
    clients_biggest_input_buf:2097152
    blocked_clients:0
    

    说明如下:

    1)connected_clients:代表当前Redis节点的客户端连接数,需要重点监控,
    一旦超过maxclients,新的客户端连接将被拒绝。

    2)client_longest_output_list:当前所有输出缓冲区中队列对象个数的最
    大数。

    3)client_biggest_input_buf:当前所有输入缓冲区中占用的最大容量。

    4)blocked_clients:正在执行阻塞命令(例如blpop、brpop、
    brpoplpush)的客户端个数。

    除此之外info stats中还包含了两个客户端相关的统计指标,如下:

    127.0.0.1:6379> info stats
    # Stats
    total_connections_received:80
    ...
    rejected_connections:0
    

    参考说明:

    • total_connections_received:Redis自启动以来处理的客户端连接数总
      数。

    • rejected_connections:Redis自启动以来拒绝的客户端连接数,需要重点
      监控。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,340评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,762评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,329评论 0 329
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,678评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,583评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,995评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,493评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,145评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,293评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,250评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,267评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,973评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,556评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,648评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,873评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,257评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,809评论 2 339

推荐阅读更多精彩内容

  • 节选自《redis开发与运维》 先来看一段client list的执行结果 输出结果的每一行代表一个客户端的信息,...
    一帅阅读 11,729评论 1 7
  • Redis服务器是典型的一对多服务器程序:一个服务器可以与多个客户端建立网络连接,每个客户端可以向服务器发送命令请...
    猪大金阅读 376评论 0 0
  • 作者:黄湘龙 花了三天时间,把REDIS 3.0.6英文版大部分都翻译过来了,还有部分没翻译完,等我慢慢更新本文章...
    楚骧阅读 1,672评论 0 0
  • 一、INCLUDES include /path/to/local.conf额外载入配置文件,如果有需要的话,可以...
    高旸1988阅读 1,084评论 0 1
  • 综述 最近笔者阅读并研究redis源码,在redis客户端与服务器端交互这个内容点上,需要参考网上一些文章,但是遗...
    zbdba阅读 1,224评论 0 1