使用 Node 和 Heroku 进行负载均衡

Load Balancing with Node and Heroku

我有一个网络应用程序可以接受来自 ios 应用程序的 api 请求。我的网络应用程序托管在 Heroku 上,使用他们的免费测功机,每个请求能够处理 512 MB 的数据。因为节点是一个单线程应用程序,所以一旦我们开始从 ios 端到 Web 服务器获得更高级别的流量,这将是一个问题。我也不是世界上最富有的人,所以我想知道创建另一个免费的 heroku 应用程序并使用循环法来平衡从 ios 应用程序收到的负载是否明智?

我只需要指出正确的方向。垂直扩展在财务上并不是一个真正的选择。

你当然可以,我的意思是,以编程方式 - 但那会绕过 Heroku's TOS:

4.4 You may not develop multiple Applications to simulate or act as a single Application or otherwise access the Heroku Services in a manner intended to avoid incurring fees.

现在,我不确定:

Because node is a single threaded application this will be a problem once we start getting higher levels of traffic from the ios end to the web server.

有一些线程在讨论这个问题,并给出了一些有趣的答案:

Clustering Node JS in Heavy Traffic Production Environment

How to decide when to use Node.js?

此外,他们 link 观看了该视频,介绍了 Node.js,其中讨论了一些基准测试:

Introduction of Node JS by Ryan Dahl

正如 Daniel 所提到的,它违反了 Heroku 规则。话虽如此,可能还有其他服务可以让您这样做。 解决此问题的一种方法是将集群模块与 ZeroMQ (you need to have ZeroMQ installed before using the module - see module description).

一起使用
var cluster = require('cluster');
var zmq = require('zmq');

var ROUTER_SOCKET = 'tcp://127.0.0.1:5555';
var DEALER_SOCKET = 'tcp://127.0.0.1:7777';

if (cluster.isMaster) {
  // this is the main process - create Router and Dealer sockets
  var router = zmq.socket('router').bind(ROUTER_SOCKET);
  var dealer = zmq.socket('dealer').bind(DEALER_SOCKET);

  // forward messages between router and dealer
  router.on('message', function() {
    var frames = Array.prototype.slice.cal(arguments);
    dealer.send(frames);
  });

  dealer.on('message', function() {
    var frames = Array.prototype.slice.cal(arguments);
    router.send(frames);
  });

  // listen for workers processes to come online
  cluster.on('online', function() {
    // do something with a new worker, maybe keep an array of workers
  });

  // fork worker processes
  for (var i = 0, i < 100; i++) {
    cluster.fork();
  }
} else {
  // worker process - connect to Dealer
  let responder = zmq.socket('rep').connect(DEALER_SOCKET);

  responder.on('message', function(data) {
    // do something with incomming data
  })
}

这只是为您指明正确的方向。如果您考虑一下,您可以创建一个带有参数的脚本,该参数将告诉它它是主进程还是工作进程。然后在主服务器上 运行 它是原样,在其他服务器上 运行 它使用 worker 标志,这将强制它连接到主要经销商。

现在您的主应用需要将请求发送到路由器,稍后将转发到工作进程:

var zmq = require('zmq');
var requester = zmq.socket('req');

var ROUTER_SOCKET = 'tcp://127.0.0.1:5555';

// handle replies - for example completion status from the worker processes
requester.on('message', function(data) {
  // do something with the replay
});

requester.connect(ROUTER_SOCKET);

// send requests to the router
requester.send({
  // some object describing the task
});

所以首先,正如其他回复所指出的那样,运行复制您的应用程序以避免 Heroku 的限制违反了他们的服务条款,这可能不是一个好主意。

不过,也有一些好消息。对于初学者(来自 Heroku's docs):

The dyno manager will restart your dyno and log an R15 error if the memory usage of a:

  • free, hobby or standard-1x dyno reaches 2.5GB, five times its quota.

据我了解,尽管您的测功机有 512mb 的 实际 RAM,但在实际重新启动之前它会换出 5 倍。所以你 可以 超过 512mb(只要你愿意为交换到磁盘支付性能损失,这可能很严重)。

除此之外,Heroku 按秒计费,并允许您根据需要上下调整测功机编队。这在您自己的应用程序中通过点击 Heroku API 很容易实现 – 我看到您已经用 NodeJS 标记了它,所以您可能想查看:

这两个模块都允许您按比例放大和缩小测功机的编队 — 通过简单的启发式算法(例如,总是有一个备用的 1X 测功机 运行ning),您可以在进行时增加容量处理请求,并在 api 请求未 运行ning 时摆脱备用容量。鉴于您按秒计费,这最终可能会非常便宜; 1X dynos 可以在 小时 到 运行 之间计算出 5¢。如果您最终 运行 每天使用几个小时的额外测功机,这对您来说是非常非常小的成本。

最后:还有第 3 方服务,例如 Adept and Hirefire(来自 Google 的两个随机示例,我相信还有更多)可以让您在某种程度上实现自动化,但是我对他们没有任何经验。

我是 Heroku 的 Node.js 平台所有者。

您可能进行了一些过早的优化。 Node.js,在我们最小的 1X 大小(512MB RAM)上,每分钟可以处理数百个并发连接和数千个请求。

如果您的 iOS 应用始终将其最大化,可能是时候考虑获利了!