程序员专栏互联网科技

企业级别Redis监控,细化到每个项目实例

2020-12-02  本文已影响0人  享学课堂

享学课堂特邀作者:老顾
转载请声明出处,谢谢!!!

前言

文章中还提到了几点问题:

今天老顾这篇文章就重点介绍如何发现异常,为什么从这个点去讲,因为这个点是前期的基础,只有具备发现一些问题,才能够有后续行为。

redis监控

在我们现在公司redis监控中,是对整个redis集群进行监控,如:内存使用情况,客户端连接数,有多少keys等从整体集群纬度上面去监控redis的使用情况。当然这些监控也是非常重要的。

现在常规的方案就是利用Prometheus采集数据,并用Grafana进行展示,如下图

null

可以这些只是个从整体纬度上面的监控,因为我们企业很多项目都在用一个公共的redis集群,那如何监控在每个项目,以及每个项目实例的对公共redis集群的请求情况呢?

公共Redis集群监控需求

我们先看下面的图:

null

现在公司很多都是微服务架构了,会涉及到会有很多服务实例了。上图中就是多个项目,多个服务实例在请求公用redis集群。

这种情况下,我们需要一个底线,就是一定要保证我们的公共redis集群是稳定的,一旦不稳定会导致我们所有的项目服务不稳定

但是这种架构会出现研发风险,因为在真实的开发过程中,我们是没法保证我们所有的开发人员不会出错,万一有个开发人员,在写一些业务时,用到了redis组件,写了个死循环导致了我们redis请求猛增,导致redis不稳定。或者对redis进行滥用,什么业务都放到redis中,导致内存崩溃。在这种情况下,我们都没法知道是哪个项目,哪个实例导致的。我们的运维会处在排查问题的混乱之中。那我们能不能提前设防呢?

我们整理一下需求:

1)我们是否可以提前分配某个项目占用redis内存的大小,指定每个实例的单位时间最大请求数,最大容忍的异常数。

2)实时监控项目占用的redis内存情况

3)实时监控项目中每个实例的请求情况

4)一旦项目占用redis内存大于分配的,就报警;甚至可以自动让这个项目进入黑名单

5)一旦项目中的实例的请求数,异常数,大于分配的,就报警;甚至可以自动让这个项目进入黑名单

解决方案

既然需求过来,那我们作为开发人员,就要项目如何设计我们的方案。因为需求还是比较多的,我们来梳理一下这些需求业务流程:

null

项目分配规则其实是比较简单的,只要设计一下mysql,以及开发一个web服务,在界面进行操作;就是一些基本的CRUD。这边老顾就不讲了。

监控是这篇文章的重点,后面涉及到的监控后的策略,老顾放到后续文章里面介绍。

监控项目内存使用情况

如果要监控整个redis的内存使用情况,是比较简单的,上面就介绍了Prometheus+Grafana的方案,当然还可以配合alert报警策略做一些后续流程

基本原理就是利用redis自身的info命令,以及info stats等命令输出了一些redis服务性能指标

那我们如何细化到监控每个项目的内存占用情况呢?

小伙伴们是否还记得redis有个持久化的概念,会把一些缓存数据持久化到dump.rdb中,这个文件就存储了每个key和value的值,以及slot的占用情况。不过这个文件是二进制的,小伙伴们不能直接打开。

我们的方案就是来解析dump.rdb文件,分析项目内存的使用情况,因为我们会规定每个项目都会有唯一的项目名称,会对key有个固定的格式【项目名称:key名称】

projectName1:userprojectName2:order

即每个key前面都会带上项目名称。这样有利于我们在解析dump.rdb后,通过项目名称前缀进行统计项目使用的内存大小。

原理小伙伴们应该知道了吧,但是怎么去解析dump.rdb文件呢,难度太大了吧;还好有开源社区的贡献,我们不需要重复造轮子。老顾采用了go语言解析dump.rdb的工具。

git地址为:

https://github.com/sripathikrishnan/redis-rdb-tools https://gitee.com/gujiachun/rdr(这个是老顾用的,上面的工具也可以考虑,功能更多)

那dump.rdb文件怎么获取到呢?redis配置文件中有对应的配置,当然我们解析dump.rdb这个操作要在redis从节点做,相对安全可靠,也可以覆盖所有业务的 Redis 集群。

整体流程如下:

null

这个开源的工具需要一些go语言相关的知识,当然老顾已经打包了一个bin,可以直接用

执行命令

./macos_start dump dump.rdb >> 111.txt

我们可以看到导出来的数据,key表示设置的可以,Bytes就是这个key存储的value的占用的字节数,Type表示这对key-value的类型。

null

我们可以设置定时任务去执行dump操作,当然如果小伙伴会go语言的,可以在这个项目中自行添加业务。这样我们就可以获取到所有key占用内存的情况,再通过分析key的项目前缀,这样就可以算出来项目占用内存的情况。

小伙伴们会问,dump.rdb文件很大,那解析需要要多久啊。老顾尝试了一下,80几万的key,dump文件300M左右,用这个工具分析只要 5秒;还是蛮强大的,Go语言实现

实例请求监控

上面我们已经解决了项目内存的占用情况,这边我们在解决一下如何监控每个服务实例的请求情况,需要知道请求数,成功数,异常数,最大耗时,最小耗时,平均耗时指标

这块是参考了限流降级框架Sentinel的源代码中,实时统计请求指标。

Sentinel是以Bucket(桶)为单位记录一段时间内的请求总数、异常总数、总耗时的,而一个Bucket可以是记录一秒内的数据,也可以是10毫秒内的数据,我们称这个时间区间为Bucket的统计单位,是由使用者自定义的:

null

Bucket存储一段时间内的请求数、异常数等这些数据用的是一个LongAdder数组,LongAdder保证了数据修改的原子性,数组的每个元素分别代表一段时间内的请求总数、异常数、总耗时。

null

用枚举类型MetricEvent的ordinal作为下标。LongAdder被我替换为j.u.c包下的atomic类了。

null

当需要获取Bucket记录的总的成功请求数、或者异常总数、或者总的请求处理耗时时,可以通过MetricEvent从LongAdder数组中获取对应的LongAdder,调用sum方法。

null

当需要往Bucket添加1个请求、或者一个异常,或者处理请求的耗时时,可以通过MetricEvent从LongAdder数组中获取对应的LongAdder,调用add方法。

null

有了Bucket之后,假设我们需要让Bucket存储一秒钟的数据,这样我们就能够知道每秒处理成功的请求数(成功QPS)、每秒处理失败的请求数(失败QPS),以及处理每个成功请求的平均耗时(avg RT)。但是我们如何才能确保Bucket存储的就是精确到1秒的数据呢?最low的做法就是启一个定时任务每秒创建一个Bucket,但统计出来的数据误差绝对很大。

而Sentinel是这样实现的。它定义一个Bucket数组,根据时间戳来定位到数组下标。假设我们需要统计每1秒处理的请求数等数据,且只需要保存最近一分钟的数据。那么Bucket数组的大小就可以设置为60,每个Bucket的windowLengthInMs窗口大小就是1000毫秒(1秒)。

null

由于每个Bucket存储的是1秒的数据,那么就可以将当前时间戳去掉毫秒部分,就能得到当前的秒数,假设Bucket数组的大小是无限大的,那么得到的秒数就是当前要获取的Bucket所在的数组下标。

但我们不能无限的存储Bucket,一秒一个Bucket得要多大的内存才能存一天的数据。所以,当我们只需要保留一分钟的数据时,Bucket数组的大小就是60,将得到的秒数与数组长度取余数,就得到当前Bucket所在的数组下标。这个数组是循环使用的,永远只保存最近1分钟的数据。

null null

取余数就是循环利用数组。如果想要获取连续的一分钟的Bucket数据,就不能简单的从头开始遍历数组,而是指定一个开始时间和结束时间, 从开始时间戳开始计算Bucket存放的数组下标,然后循环每次将开始时间戳加上1秒,直到开始时间等于结束时间。

整体原理如上,还是比较挠的,直接上案例代码,看效果如何。

请求案例

null

上面是业务中加入监控埋点,下面我们直接输出

null

小伙伴们看到了FlowHelper这个类,就在老顾的开源项目中;

git地址:https://gitee.com/gujiachun/rb-qps-helper

通过这样的方式,我们就可以改造我们的redis请求client方法了,进行监控埋点就行了。

现在还剩下一个问题,就是我们监控到的数据如何给监控平台?

如何上报监控数据

一开始老顾想着用定时任务的方式,提交给监控平台;最后想了想,那监控平台地址是什么呢?太耦合了。然后想到了我们spring boot中就有一些监控指标。

通过访问http://localhost:1100/actuator/metrics就可以看到监控的指标,再通过http://localhost:1100/actuator/prometheus直接可以看到指标数据。

null

我们也是可以参考一下的,我们每个实例只要暴露出来,让prometheus自己来拿,而且又是标准流程。具体实现:

引入依赖

null

实现MeterBinder接口

null

在利用Gauge 监控类实现就ok了

null

这样就可以把我们redis实例的请求指标,展现在http://localhost:1100/actuator/prometheus中了

null

是不是很酷啊?

总结

这篇文章的知识点很多,想要完成我们文章开头的需求,需要这些知识一起组合起来,就能够实现监控的目标了;当然我们需要搭建一个DashBoard界面查看监控数据

我们看每个项目的内存占比请求,以及有没有达到阀值,以及对哪些Key占用比较多的内存,进行排名。也可以监控到每个实例的请求情况,请求数排名,异常数排名等。

可以极大的帮助我们运维和开发人员更多的了解我们的redis使用情况。文章里面只是介绍了核心思想。老顾已经全部实现了,小伙伴们需要源码,可以联系老顾。

img
上一篇下一篇

猜你喜欢

热点阅读