微服务之熔断器

熔断器模式可以防止应用程序不断地尝试执行可能会失败的操作,使得应用程序继续执行而不用等待修正错误,或者浪费CPU时间去等到长时间的超时产生。熔断器模式也可以使应用程序能够诊断错误是否已经修正,如果已经修正,应用程序会再次尝试调用操作。

问题描述

假设我们有两个服务serviceA、serviceB,serviceA可以正常对外提供服务,某些API依赖serviceB。

  • 当serviceB异常完全不可用时,serviceA会一直调用serviceB,返回报错信息,健壮一点的程序都会处理这些错误,不至于让服务崩溃。
  • 当serviceB响应变慢,处理请求的能力变低时,这种情况下serviceA一直等待请求完成,最终会导致资源耗尽。

因此我们需要一个机制,当下游服务变得不可用(或者异常增多时),需要切断对下游服务的访问,直接返回错误值或者默认值

我们的熔断器应该要满足以下要求:

  • 自定义回退:尝试从其他来源获取相同的数据如果不可能,请使用自己的缓存值。
  • 快速失败:如果serviceA知道失败serviceB。了,那么就没有必要等待超时并消耗自己的资源它应该尽快返回“知道” serviceB已关闭
  • 不要崩溃:正如我们在这种情况下看到的那样,serviceA不应该崩溃。
  • 自动修复:定期检查是否serviceB再次起作用。
  • 其他API应该有效:所有其他API应该继续工作。

熔断机制

我们使用brakes包来实现熔断机制

'use strict';

const Brakes = require('brakes');
const Promise = require('bluebird');

function promiseCall() {
  return new Promise((resolve, reject) => {
    if (Math.random() > 0.8) {
      console.log('运行成功');
      resolve('正常值');
    } else {
      console.error('运行失败');
      reject(new Error());
    }
  });
}

function fallbackCall() { // 降级方案
  return new Promise(resolve => {
    resolve('降级方案返回错误值或缓存值');
  });
}

function healthCheckCall() { // 健康检查
  return new Promise((resolve, reject) => {
    // this will return 20% true, 80% false
    if (Math.random() < 0.8) {
      console.log('\n健康检查通过');
      resolve();
    } else {
      console.error('\n健康检查失败');
      reject(new Error('health check failed'));
    }
  });
}

const brake = new Brakes(promiseCall, {
  timeout: 150,
  threshold: 0.5, // 成功请求比例阈值
  waitThreshold: 10, // 等待对应请求数量之后统计成功请求比例
  healthCheckInterval: 2000, // 健康检查间隔
});

brake.fallback(fallbackCall);
brake.healthCheck(healthCheckCall);

brake.on('circuitClosed', () => {
  console.log('\n服务恢复');
});
brake.on('circuitOpen', () => {
  console.log('\n服务熔断');
});

const main = async () => {
  for (let i = 0; i < 2000; i++) {
    try {
      const result = await brake.exec();
      await Promise.delay(300);
      console.log(i, result);
    } catch (error) {
      console.error(i, error.message);
    }
  }
};

main().then(console.log).catch(console.error);

执行结果

// 开始时是正常运行的
运行成功
0 '正常值'
运行失败
1 '降级方案返回错误值或缓存值'
运行成功
2 '正常值'
运行失败
3 '降级方案返回错误值或缓存值'
运行失败
4 '降级方案返回错误值或缓存值'
运行失败
5 '降级方案返回错误值或缓存值'
运行失败
6 '降级方案返回错误值或缓存值'
运行失败
7 '降级方案返回错误值或缓存值'
运行失败
8 '降级方案返回错误值或缓存值'
运行失败
9 '降级方案返回错误值或缓存值'
// 服务熔断后, 不再执行程序,直接使用降级方案
服务熔断
10 '正常值'
11 '降级方案返回错误值或缓存值'
12 '降级方案返回错误值或缓存值'
13 '降级方案返回错误值或缓存值'
14 '降级方案返回错误值或缓存值'
15 '降级方案返回错误值或缓存值'
// 健康检查失败时,继续使用降级方案
16 '降级方案返回错误值或缓存值'
17 '降级方案返回错误值或缓存值'
18 '降级方案返回错误值或缓存值'
19 '降级方案返回错误值或缓存值'
20 '降级方案返回错误值或缓存值'
21 '降级方案返回错误值或缓存值'
22 '降级方案返回错误值或缓存值'
// 健康检查通过时,服务恢复

健康检查通过

服务恢复
23 '降级方案返回错误值或缓存值'
运行失败
24 '降级方案返回错误值或缓存值'
运行失败
25 '降级方案返回错误值或缓存值'
运行成功
26 '正常值'
运行失败
27 '降级方案返回错误值或缓存值'

不用人工干预,自动实现服务异常时熔断、服务修复后自动恢复请求

建议在使用的时候直接封装在客户端对外调用逻辑里面,上层调用不需要知道有断路器的存在

One more thing

下游服务的报警需要设置好

Ref

简书-Nodejs 熔断机制

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章