├── .npmignore ├── test ├── line.png ├── index.js └── index.html ├── package.json ├── index.js └── README.md /.npmignore: -------------------------------------------------------------------------------- 1 | *.png -------------------------------------------------------------------------------- /test/line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fefeding/ema-timeout/HEAD/test/line.png -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | 2 | const ema = require('../index.js').create({ 3 | Tavg: 60, 4 | Thwm: 250, 5 | Tmax: 500, 6 | N: 90 7 | }); 8 | 9 | for(var i=0;i<100;i++) { 10 | var a = Math.random() * 200; 11 | var e = ema.update(a); 12 | var t = ema.get(); 13 | console.log(a, e, t); 14 | } 15 | 16 | for(var i=0;i<100;i++) { 17 | var a = Math.random() * 200 + 500; 18 | var e = ema.update(a); 19 | var t = ema.get(); 20 | console.log(a, e, t); 21 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ema-timeout", 3 | "version": "1.0.3", 4 | "description": "通过ema算法动态计算延时时间", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "node ./test/index.js" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/jiamao/ema-timeout.git" 12 | }, 13 | "keywords": [ 14 | "ema", 15 | "动态超时" 16 | ], 17 | "author": "jiamao", 18 | "license": "MIT", 19 | "bugs": { 20 | "url": "https://github.com/jiamao/ema-timeout/issues" 21 | }, 22 | "homepage": "https://github.com/jiamao/ema-timeout#readme" 23 | } 24 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * 创建EMA算法对象 4 | * 适应循环执行,程序大部分在等待响应,有柔性处理 5 | * 6 | * @param {Object} options 格式 { 7 | * Tavg: 最低响应时间, 一般用平均响应时间替代 (ms) 8 | * Thwm:超时时间限制, 确保最坏的时候,所有请求能处理。正常时正确处理的成功率满足需求。 (ms) 9 | * Tmax: 最大弹性时间 (ms) 10 | * N: 平滑指数,越小表示平无值越受最近值的影响,太大则对异常响应较慢 11 | * } 12 | */ 13 | exports.create = function(options) { 14 | options = Object.assign({ 15 | Tavg: 60, 16 | Thwm: 250, 17 | Tmax: 500, 18 | N: 80 19 | }, options); 20 | 21 | return { 22 | options: options, 23 | ema: 0, // 当前ema值 24 | r: 2/(options.N + 1), // 平滑指数 25 | /** 26 | * 更新最近延时时间 , 计算当前ema 27 | * @param {*} x 当前延时时间 (ms) 28 | */ 29 | update: function(x) { 30 | var ema = x * this.r + this.ema * (1 - this.r); 31 | return this.ema = ema; 32 | }, 33 | /** 34 | * 获取当前延时时间 (ms) 35 | * @returns {Number} 延时时间(ms) 36 | */ 37 | get: function() { 38 | var tdto = 0; // 当前延时间间 39 | // EMA <= Tavg, 这种情况EMA评估值比Tavg还要少,按耗时越少,给的延时越多的原则来计算,当前给的延时时间为Tmax。 40 | if(this.ema <= this.options.Tavg) tdto = this.options.Tmax; 41 | // EMA >= Thwm, 这种情况下,网络抖动比较严重,直接把延时限制在Thwm上,加快失败,方便进行柔性处理。 42 | else if(this.ema >= this.options.Thwm) { 43 | tdto = this.options.Thwm; 44 | } 45 | // EMA > Tavg, 这种情况EMA估值已经大于平均延时,此时启用动态延时计算 46 | else { 47 | var p = (this.options.Thwm - this.ema) / (this.options.Thwm - this.options.Tavg); // 平无表现相对限制的偏移比例,相当于弹性 48 | tdto = this.options.Thwm + p * (this.options.Tmax - this.options.Thwm); 49 | } 50 | return Math.abs(tdto); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # ema超时算法 3 | 4 | [![NPM version][npm-image]][npm-url] 5 | [![npm download][download-image]][download-url] 6 | 7 | [npm-image]: https://img.shields.io/npm/v/ema-timeout.svg?style=flat-square 8 | [npm-url]: https://npmjs.org/package/ema-timeout 9 | [download-image]: https://img.shields.io/npm/dm/ema-timeout.svg?style=flat-square 10 | [download-url]: https://npmjs.org/package/ema-timeout 11 | 12 | EMA算法本身是多用于金融行业的,是一种基于增加期望的模型。算法的主要思想是综合利用历史上积累到的数据,预测下一个周期内的期望。我们参考了标准的EMA算法,得到了符合互联网业务特点的用于超时控制的EMA算法。具体的思想主要是引入“平均效应”的概念,用平均响应时间代替固定超时时间,只要平均响应时间没有超时即可,而不是要求每次都不能超时。主要几点:总体情况不能超标;平均情况表现越好,弹性越大;平均情况表现越差,弹性越小。这里的弹性指的是允许毛刺情况的最大响应时间。 13 | 14 | - 数据效果展示:[https://jiamao.github.io/ema-timeout/test/index.html](https://jiamao.github.io/ema-timeout/test/index.html) 15 |  16 | 17 | ## Install 18 | 19 | ```bash 20 | $ npm i ema-timeout --save 21 | ``` 22 | 23 | ## Usage 24 | 25 | ```js 26 | const ema = require('ema-timeout'); 27 | 28 | // 因为每个cgi有可能不一样,根据自身业务需求考虑是否要为每个接口create不同的ema计算 29 | const options = { 30 | Tavg: 55, // 最低响应时间, 一般用平均响应时间替代 31 | Thwm: 300, // 超时时间限制, 确保最坏的时候,所有请求能处理。正常时正确处理的成功率满足需求。 32 | Tmax: 500, // 最大弹性时间 33 | N: 90 // 平滑指数,越小表示平均值越受最近值的影响,太大则对异常响应较慢 34 | }; 35 | const emaObj = ema.create(options); 36 | 37 | ``` 38 | ```js 39 | const start = Date.now(); 40 | let tdto = emaObj.get(); // 获取当前超时设置时间 41 | 42 | // 请求接口指定超时时间 43 | // 这里是你业务请求逻辑 44 | try { 45 | const result = await request({ 46 | timeout: tdto 47 | });// 请求接口 48 | 49 | emaObj.update(Date.now() - start); // 这里是更新当前接口延时时间,以备计算后续超时 50 | } 51 | catch(e) { 52 | // 把异常指定为最大超时,这样更好的反应接口健康情况 53 | emaObj.update(Math.max(Date.now() - start, options.Tmax)); 54 | } 55 | 56 | ``` 57 | 58 | ## 适用条件 59 | 60 | 固定业务逻辑,循环执行,如FastCGI/S++。 61 | 62 | 程序大部分时间在等待响应,而不是CPU计算或处理I/O中断。 63 | 64 | 服务是串行处理模式,容易受异常、慢请求阻塞。 65 | 66 | 响应时间不宜波动过大。 67 | 68 | 可以接受有损服务。 69 | 70 | ## License 71 | 72 | [MIT](LICENSE) 73 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |