雪崩效应
当一个服务已经负载不了当前的请求压力的时候,应该主动暂停或者拒绝新到来的服务请求,以保证自身存活,以及防止雪崩效应。
我们举一个例子来说明雪崩效应。
比如说有一个erlang的进程:
- 突然收到大量请求,请求数远大于该进程的处理能力。
- 如果这个时候这个进程决定服务所有请求,就需要将所有请求buffer在内存中,然后一个个处理(或者好几个并行处理)。
- 这个时候,由于大量请求占用了大量的内存,erlang的gc开始显得吃力。
- 为了服务其中的某个请求,需要申请新的内存。
- 但是由于buffer住了大量请求,不够内存分配给请求处理。
这个时候erlang就陷入了一个GC与内存申请的死循环:要处理完这个请求就得申请内存,要申请内存就得GC,要GC就得处理完这个请求(否则都是有用的内存),loop forever,然后因为oom crash。
以前我对服务降级的认识停留在该节点压力过大不可用,需要减少请求以保证服务不会出现crash,而没有想过这个降级会给集群提供一个更加优越的性能表现。这里分享下我遇到的一个涉及服务降级的坑。
先简单介绍下公司的集群架构。我们的集群大概就是一个接入集群,后接一个rabbitmq集群,通过rabbitmq,与后端的逻辑服务器实现异步请求。大概如下所示:
front < ——— > rabbit mq < ———> logic cluster < ———> storage
为了方便测试性能瓶颈,我们测试的时候,每个点都是只有一个进程,并且在同一台机器上运行。照说在一个机器上,cpu分配如果均匀的话,逻辑服务器应该是可以hold住所有从front过来的请求,哪怕再多也没关系。另外逻辑服务器由于从mq拿消息来处理,理论来说应该是有多少处理多少,不存在压力过大的问题,但是实际测试发现,逻辑处理服务器不断oom。
仔细分析了代码后,我们发现逻辑服务处理mq过来的数据有问题,逻辑服务器拿mq数据并且分发的逻辑如下
mq -> mq client -> mq proxy -> mq receiver -> [workers]
通过erlang的线上分析工具,我们最后定位到大量内存被使用到了mq proxy 和 mq receiver上,他们的的erlang msgq堆积大量数据,而上层又处理不过来,出现雪崩,于是导致了oom。
进一步检查,发现是 mq proxy上对内存检测的速度过慢,检测之后还要上传消息给mq client,告知其停止订阅,这个过程过于缓慢,导致了消息堆积。