| 导语 过载保护对于to c业务的服务来讲是很是重要的。试想一下,若是有一个运营活动或者特殊的日子致使流量暴增,用户每秒请求数远远超出了咱们的服务所能处理的最大值,假如咱们没有作过载保护,很快这个服务所能利用的资源将会被耗尽,没法处理任何请求,若是这个服务所在机器的其余服务没有和这个服务资源隔离,也会影响到其余正常的服务,那么一次严重的事故就发生了。node
什么是过载? 简单来讲,就是当前负载超过了系统的最大处理能力,如:git
系统实际每秒能处理的请求量为1000个,但实际每秒的请求量却远大于1000个,能够断定系统过载。github
对于web应用,就是对请求量的控制问题,控制请求量不要超过系统的最大处理能力。web
直接的作法就是控制当前服务同时处理的请求数,这个很好控制,请求来一个就计数+1, 处理完请求回包就减1,若是当前在处理的请求总数超过了设置的最大值,那么就直接回包503错误。 nodejs koa 代码实现:算法
// 写一个中间件 overloadProtection.js
const maxCount = 1000;
let totalCount = 0;
moudle.exports = async(ctx, next) => {
totalCount += 1;
// TODO 这里能够加一些监控上报
if (totalCount > maxCount) { // 直接丢弃
ctx.status = 503;
ctx.body = 'Service heavy load!!!';
return;
}
await next();
totalCount -= 1;
}
复制代码
而后koa 服务在最开始的时候使用这个中间件便可。api
这种方法有个前提条件: 必需要知道这个服务同时能处理的最大请求数(maxCount),通常状况下这个服务会有多个api,处理每一个api请求所消耗的资源可能不同,咱们很难模拟真实场景获得准确的maxCount。你若是想说咱们能够限制每一个接口的最大请求数啊,并不必定要限制这个服务的最大请求数,那你就要考虑到一种极端场景,全部的接口都达到最大请求数的时候你的服务可否抗住,这可能须要更高性能或更多的服务器才能知足这种场景。bash
在web服务的场景,CPU是服务最主要的瓶颈,因此咱们能够在处理请求以前首先判断下当前CPU负载是否达到了咱们设置的CPU最大使用率,而后丢弃或者随机丢弃。 咱们须要的是当前服务的进程占用的cpu百分比,github上找到一个 node-usage 。 稍微改造一些,思路以下: 首先node-usage,若是实时获取当前进程cpu利用率,会增长系统的负载,咱们能够本身控制采样cpu使用率的评率,获得cpu利用率。服务器
nodejs koa 代码实现:dom
// 写一个中间件 overloadProtection.js
const usage = require('usage');
const pid = process.pid; // 当前node服务的进程pid
let sampleCpuInterval = 1000; // 采样cpu使用率的频率
let cpuUsedLimit = 85; // cpu使用率上限
let getCupUsed = function getCupUsed() {
setInterval(() => {
usage.lookup(pid, {keepHistory: true}, (err, result) => {
if (result) {
global.cpuUsed = result.cpu || 0;
}
})
}, sampleCpuInterval);
};
getCupUsed();
module.exports = async(ctx, next) => {
// TODO 这里能够加一些监控上报
if (cpuUsedLimit && global.cpuUsed > cpuUsedLimit) {
// 这里是随机丢弃的算法,能够思考下为何这么作
let rejectRate = Math.pow((global.cpuUsed - cpuUsedLimit) / (100 - cpuUsedLimit), 2);
if (Math.random() > rejectRate) {
ctx.status = 503;
ctx.body = 'Service heavy load!!!';
return;
}
}
await next();
};
复制代码
压测很顺利, 可是若是请求量特别特别大的时候,即便node服务没有作任何逻辑处理,只是简单回包cpu都能耗尽的场景下,咱们要考虑在上层或系统层作过载保护。koa
github上找到一个overload-protection,使用很是容易。可是我本身压测后发下有很大问题。我我的理解做者意图以下: 处理请求以前,先设置一个setInterval, 经过计算setInterval里的回调函数执行延时来预估当前cpu负载。能够预见的是延迟越高,cpu负载越高。延迟在某个值时多是咱们指望限制的cpu最大使用率。 咱们看下源码
var xtend = require('xtend')
var EE = require('events').EventEmitter
var defaults = {
limit: 42, // 默认延迟是42,
sampleInterval: 5
}
function loopbench (opts) {
opts = xtend(defaults, opts)
var timer = setInterval(checkLoopDelay, opts.sampleInterval)
timer.unref() // 参考 https://zhuanlan.zhihu.com/p/38091559
var result = new EE(); // EventEmitter实例
result.delay = 0
result.sampleInterval = opts.sampleInterval
result.limit = opts.limit
result.stop = clearInterval.bind(null, timer)
var last = now()
return result
function checkLoopDelay () {
var toCheck = now()
var overLimit = result.overLimit
result.delay = toCheck - last - result.sampleInterval // 实际执行的延时,系统负载越高(越忙)延时越大
last = toCheck
result.overLimit = result.delay > result.limit
if (overLimit && !result.overLimit) {
result.emit('unload'); // 由过载变成正常,外面监听这个事件而后告诉服务变成正常状态了,正常处理请求
} else if (!overLimit && result.overLimit) {
result.emit('load'); // 由正常变为过载,外面监听这个事件而后告诉服务变成过载状态了,要丢弃请求
}
}
function now () {
var ts = process.hrtime()
return (ts[0] * 1e3) + (ts[1] / 1e6)
}
}
复制代码
可是我持续压测了几分钟,cpu全层使用率是98%-100%,并无触发到它的过载机制,我试着打印了下 result.delay 发现特别小:
可是我以为做者的是思想是能够借鉴的,能够用于其余的应用场景。
上面描述了过载保护的三种实践方法,前两种均可以应用于特定的场景下,第三种方法实践失败。我的认为第二种经过判断cpu实时使用率的方法,更具备通用性。
本文首发在掘金转载请注明原做者,若是你以为这篇文章对你有帮助或启发,也能够来请我喝咖啡。 利益相关:本篇文章全部涉及到的软件均为笔者平常所用工具,无任何广告费用。