Redis:22---客户端API:client、monitor)

 

一、client list

  • client list命令能列出与Redis服务端相连的所有客户端连接信息。例如下面代码是在一个Redis实例上执行client list的结果,其中每一行代表一个客户端信息:

  •  

  • 下面将选择几个重要的属性进行说明,其余通过表格的形式进行展示

①标识:id、addr、fd、name

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

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

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

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

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

②输入缓冲区:qbuf、qbuf-free

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

  • client list中qbuf和qbuf-free:

    • 这两个属性分别代表这个缓冲区的总容量和剩余容量

    • Redis没有提供相应的配置来规定每个缓冲区的大小,输入缓冲区会根据输入内容大小的不同动态调整,只是要求每个客户端缓冲区的大小不能超过1G,超过后客户端将被关闭

    • 下面是Redis源码中对于输入缓冲区的硬编码:

/* Protocol and I/O related defines */
#define REDIS_MAX_QUERYBUF_LEN (1024*1024*1024) /* 1GB max query buffer. */
  • 输入缓冲使用不当会产生两个问题:

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

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

  • 上图的执行效果如下:

  • 上面已经看到,输入缓冲区使用不当造成的危害非常大,那么造成输入缓冲区过大的原因有哪些?

    • 输入缓冲区过大主要是因为Redis的处理速度跟不上输入缓冲区的输入速度,并且每次进入输入缓冲区的命令包含了大量 bigkey,从而造成了输入缓冲区过大的情况

    • 还有一种情况就是Redis发生了阻塞,短期内不能处理命令,造成客户端输入的命令积压在了输入缓冲区, 造成了输入缓冲区过大

  • 那么如何快速发现和监控呢?监控输入缓冲区异常的方法有两种:

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

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

  • 上面两种方法各有自己的优劣势,下图对两种方法进行了对比:

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

③输出缓冲区:obl、oll、omem

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

  • 与输入缓冲区不同的是:

    • 输出缓冲区的容量可以通过参数client-outputbuffer-limit来进行设置

    • 并且输出缓冲区做得更加细致,按照客户端的不同分为三种:普通客户端、发布订阅客户端、slave客户端。如下图所示

  • client-output-buffer-limit格式如下。参数意义为:

    • <class>:客户端类型,分为三种。a)normal:普通客户端;b) slave:slave客户端,用于复制;c)pubsub:发布订阅客户端

    • <hard limit>:如果客户端使用的输出缓冲区大于该值,客户端会被立即关闭

    • <soft limit>和<soft seconds>:如果客户端使用的输出缓冲区超过了并且持续了秒,客户端会被立即关闭

  • 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、smembers命令的结果等,通过Redis源码中redis.h的redisClient结构体(Redis3.2版本变为Client)可以看到两个缓冲区的实现细节:

typedef struct redisClient {
// 动态缓冲区列表
list *reply;
// 动态缓冲区列表的长度(对象个数)
unsigned long reply_bytes;
// 固定缓冲区已经使用的字节数
int bufpos;
// 字节数组作为固定缓冲区
char buf[REDIS_REPLY_CHUNK_BYTES];
} redisClient;
  • 固定缓冲区使用的是字节数组,动态缓冲区使用的是列表。当固定缓冲区存满后会将Redis新的返回结果存放在动态缓冲区的队列中,队列中的每个对象就是每个返回结果,如下图所示:

  • obl、oll、omem:

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

    • 例如下面代表当前客户端的固定缓冲区的长度为0,动态缓冲区有4869个对象,两个部分共使用了133081288字节=126M 内存:

id=7 addr=127.0.0.1:56358 fd=6 name= age=91 idle=0 flags=O db=0 sub=0 psub=0 multi=-1
qbuf=0 qbuf-free=0 obl=0 oll=4869 omem=133081288 events=rw cmd=monitor
  • 监控输出缓冲区的方法依然有两种:

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

    • ②通过info命令的info clients模块,找到输出缓冲区列表最大对象数,例如(其中,client_longest_output_list代表输出缓冲区列表最大对象数):

    • 这两种统计方法的优劣势和输入缓冲区是一样的,这里就不再赘述了

  • 相比于输入缓冲区,输出缓冲区出现异常的概率相对会比较大,那么如何预防呢?方法如下:

    • 进行上述监控,设置阀值,超过阀值及时处理

    • 适当增大slave的输出缓冲区的,如果master节点写入较大,slave客户 端的输出缓冲区可能会比较大,一旦slave客户端连接因为输出缓冲区溢出 被kill,会造成复制重连

    • 限制容易让输出缓冲区增大的命令,例如,高并发下的monitor命令就 是一个危险的命令

    • 及时监控内存,一旦发现内存抖动频繁,可能就是输出缓冲区过大

    • 限制普通客户端输出缓冲区的,把错误扼杀在摇篮中,例如可以进行如下设置:

client-output-buffer-limit normal 20mb 10mb 120

④客户端的存活状态(age、idle)

  • client list中的age和idle分别代表:当前客户端已经连接的时间、最近一次的空闲时间:

  • 例如下面这条记录代表当期客户端连接Redis的时间为304秒,其中空闲了0秒:

  • 例如下面这条记录代表当期客户端连接Redis的时间为8888581秒,其中空闲了8888581秒。实际上这种就属于不太正常的情况,当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();
  • 下面对代码中的每一步进行分析,用client list命令来观察age和idle参数的相应变化(备注:为了与redis-cli的客户端区分,本次测试客户端IP地址:10.7.40.98)

  • 1)在执行代码之前,client list只有一个客户端,也就是当前的rediscli,下面为了节省篇幅忽略掉这个客户端。

  • 2)使用Jedis生成了一个新的连接,并执行get操作,可以看到IP地址为 10.7.40.98的客户端,最后执行的命令是get,age和idle分别是1秒和0秒

  • 3)休息10秒,此时Jedis客户端并没有关闭,所以age和idle一直在递 增:

  • 4)执行新的操作ping,发现执行后age依然在增加,而idle从0计算,也 就是不再闲置

  • 5)休息5秒,观察age和idle增加:

  • 6)关闭Jedis,Jedis连接已经消失:

⑤客户端类型(flag)

  • client list中的flag是用于标识当前客户端的类型

  • 例如flag=S代表当前客 户端是slave客户端、flag=N代表当前是普通客户端,flag=O代表当前客户端 正在执行monitor命令。下图列出了11种客户端类型:

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

二、client setName和client getName

client setName xx

client setName

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

  • 此时再执行client list命令,就可以看到当前客户端的name属性为test_client:

client getName

client getName
  • 如果想直接查看当前客户端的name,可以使用client getName命令

  • 第一次进入客户端时,客户端是没有名字的,因此名字为空

  • 更改名字之后,就可以看到更改后的名字了。例如:

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

三、client kill

client kill ip:port
  • 此命令用于杀掉指定IP地址和端口的客户端

  • 由于一些原因(例如设置timeout=0时产生的长时间idle的客户端),需要手动杀掉客户端连接时,可以使用client kill命令

演示案例

  • 例如左侧为一个客户端(127.0.0.1:34658),右侧为一个客户端(127.0.0.1:34660)

  • 如果想杀掉127.0.0.1:34656的客户端,可以执行:

  • 执行命令后,client list结果只剩下了127.0.0.1:34658自己这个客户端:

四、client pause

client pause timeout(毫秒)
  • client pause命令用于阻塞客户端timeout毫秒数,在此期间客户端连接将被阻塞。如下图所示:

演示案例

  • 例如在一个客户端执行下面的命令,在之后的10000毫秒内的其他客户端连接都会被阻塞

  • 过一会后在另一个客户端执行ping命令,发现整个ping命令执行了2.40秒(手动执行redis-cli,只为了演示,不代表真实执行时间):

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

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

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

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

五、monitor

  • monitor命令用于监控Redis正在执行的命令 

演示案例

  • 如下图所示:

    • 我们打开了两个redis-cli,右侧先执行monitor命令,左侧再执行其他命令

    • 可以看到monitor命令能够监听其他客户端正在执行的命令,并记录了详细的时间戳

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/444303.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis:20---常用功能之(发布与订阅)

一、发布与订阅概述Redis提供了基于“发布/订阅”模式的消息机制&#xff0c;此种模式下&#xff0c;消息发布者和订阅者不进行直接通信&#xff0c;发布者客户端向指定的频道&#xff08;channel&#xff09;发布消息&#xff0c;订阅该频道的每个客户端都可以收到该消息。 下…

Redis:19---常用功能之(HyperLogLog)

一、HyperLogLog概述HyperLogLog并不是一种新的数据结构&#xff08;实际类型为字符串类型&#xff09;&#xff0c;而是一种基数算法&#xff0c;通过HyperLogLog可以利用极小的内存空间完成独立总数的统计&#xff0c;数据集可以是IP、Email、ID等HyperLogLog提供了3个命令&a…

UNIX(多线程):13---condition_variable、wait、notify_one、notify_all

条件变量std::condition_variable、wait()、notify_one() 线程A: 等待一个条件满足线程B: 专门往消息队列中放入消息(数据),达到一定条件,通知处于等待中的线程A。std::condition_variable实际上是一个类,是一个和条件相关的一个类,说白了就是等待一个条件达成。这个类是…

UNIX(多线程):27---多线程并发之原子操作与无锁编程

原子操作:顾名思义就是不可分割的操作,该操作只存在未开始和已完成两种状态,不存在中间状态; 原子类型:原子库中定义的数据类型,对这些类型的所有操作都是原子的,包括通过原子类模板std::atomic< T >实例化的数据类型,也都是支持原子操作的。 二、如何使用原子类…

UNIX(进程间通信):01---Linux进程通信方式

Linux下的进程通信手段基本上是从Unix平台上的进程通信手段继承而来的。而对Unix发展做出重大贡献的两大主力AT&T的贝尔实验室及BSD(加州大学伯克利分校的伯克利软件发布中心)在进程间通信方面的侧重点有所不同。前者对Unix早期的进程间通信手段进行了系统的改进和扩充,…

UNIX(进程间通信):02---父子进程之间的数据共享分析

之前我们通过fork()函数,得知了父子进程之间的存在着代码的拷贝,且父子进程都相互独立执行,那么父子进程是否共享同一段数据,即是否存在着数据共享。接下来我们就来分析分析父子进程是否存在着数据共享。 我们都知道,在linux下,内存存储的位置是全局变量,栈区,堆区,以…

UNIX(进程间通信):04---孤儿进程

上一篇文章讲过僵尸进程,这里再分享给大家另外一种状态。 孤儿进程 什么是孤儿进程?当一个子进程还在执行时,它的父进程已经退出了,那么这个子进程的退出信息也没有被父进程接收到,如果子进程的退出信息没有被别的进程接收到,那么这个子进程就会变成一个僵尸进程,所…

UNIX(进程间通信):16深入理解Socket

socket又叫套接字或者插口,它也是进程间通信的一种方式,实际上就是网络上的通信节点,应用程序只需要链接到socket就可以和网络上任何一个通信端点连接、传送数据。socket封装了通信的细节,我们可以不必关心通信协议内容而专注于应用程序开发。根据数据传送方式,socket分为…

苹果支付:如何解决沙盒环境下获取可恢复购买项为空

在传统手游开发中免不了和苹果支付打交道,而且苹果也会有各种奇奇怪怪的问题和BUG 。 曾经有一次某一个游戏出现了调单问题,现象如下(我有点懒,不想画泳道图或者时序图): 客户端拉起支付付款成功,OC底层收到支付成功回调->发送支付成功的收据到服务器验…

游戏服务器架构:如何设计开发战斗系统的技能和buff系统

战斗系统中buff和skill如何配合 在网络游戏中的战斗形式多种多样,不同游戏的战斗逻辑也有很大的差异。但是一般都会涉及技能系统和buff系统,两种之间相互关联,技能可以产生buff作用在目标上,影响目标。同时buff也会影响技能的释放效果,两者都可以算得上游戏战斗系统最重要…

一文彻底搞懂静态库和动态库,显示链接和隐式链接

定义&#xff1a;运行时库 静态库 动态库运行时库&#xff1a;Unix中一个典型的运行时库例子就是libc&#xff0c;它包含标准的C函数&#xff0c;如&#xff0c;print()&#xff0c;exit()等等&#xff0c;用户能创建他们自己的运行库&#xff08;在Windows中是DLL&#xff09;…

mysql的cpu高定位

导致数据库CPU很高的原因有很多种,一般和慢SQL也有关(因为每条SQL要么占CPU高,要么占IO高,大体是这样)。 (1)、如果服务器有多个mysql实例,需要通过top命令看看是哪个mysql实例导致的cpu高(如果不是mysql导致的cpu高,需要优化其他导致cpu的程序): (2)、定位到占用…

游戏服务器架构-设计模式之发布订阅模式

发布订阅模式场景 熟悉消息中间件的同学应该对发布/订阅模式(Publish Subscribe Pattern)并不陌生。即使你不了解消息中间件,那么在平时生活中发布/订阅模式也是非常常见的场景。 比如你打开你的微信订阅号,你订阅的作者发布的文章,会广播给每个订阅者。在这个场景里,微信公…

聊一下CPU占用高的解决方案

前言: 在软件开发和性能测试中,CPU占用率是服务器开发一个很重要的指标,到底有哪些因素会导致CPU占 用率上升呢?又有哪些手段可以降低CPU的占用率呢? 如果你看了这篇文章后仍然没有解决项目问题的思路,请在下方留言或公众号后台留言。(后续我将更新一到两篇…

闲话目前游戏服务器的开发

我是从12年开始进入页游行业&#xff0c;接触到的第一个游戏项目就是淘米网的《摩尔庄园》&#xff0c;公司那个时候也刚在美纽交所上市&#xff0c;被Benson&#xff0c;魏震和Rock腾讯三巨头的感染下&#xff0c;做着喜欢的游戏... &#xff08;后来在工作中我经常会遇到过不…

危险!!!也许你的web网站或服务正在悄无声息地被SQL注入

2010年秋季,联合国官方网站遭受SQL注入攻击。 2014年一个叫“TeamDigi7al”的黑客组织攻击了美国海军的一个名为“Smart Web Move”的web应用。此次事件直接造成美国海军数据库超过22万服役人员的个人信息被泄露。而事后,美国海军动用了超过50万美元来弥补此次的数据泄密事故…

手把手教你使用sql注入来绕过游戏后台检测

SQL注入毫无疑问是最危险的Web漏洞之一,因为我们将所有信息都存储在数据库中。其解决方案之一,有许多公司实施Web应用程序防火墙和入侵检测/预防系统来试图保护自己。但不幸的是,这些对策往往是不充分的,并且很容易被绕过。 尽管不能依赖防火墙来防止所有SQL注入,但一些防…

设计模式 ---适配器模式

在一些业务场景里,你是否遇到过如下类似的需求: 1、系统需要使用现有的类,而此类的接口不符合系统的需要。 2、想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口。 3、通过接口转换…

关于游戏排行榜设计开发的一些总结

前言 不管是手游还是端游,貌似都离不开排行榜,没有排行榜的游戏是没有灵魂的游戏,因为排行榜可以让用户分泌多巴胺,这样日活才会上来,有了用户就有钱赚。产品想方设法的让用户留存,设计各种排行榜:个人段位排名、个人积分或金币排名、全球榜单实时排名。如果用户量少的话…

游戏中的常见概率设计分析

前言游戏中的概率真的是让人又爱又恨&#xff0c;很多玩家因为自己的屌丝气质&#xff08;白嫖&#xff09;而弃坑玩不下去的&#xff0c;比如人尽皆知的某阴阳师&#xff0c;除了氪金&#xff0c;还肝&#xff0c;而且如果你的脸真的非常的黑&#xff0c;那也是打不过那些0氪金…