1

背景

我们有一个调度程序实例组,每个活动虚拟机每秒接收大约 700 个请求。此调度程序位于自动扩展的负载均衡器后面。到目前为止,我们所有的 VM 都是常规 VM,但是我们一直在研究使它们成为抢占式的可能性。

抢占式实例的问题

根据文档,GCP 可以随时终止抢占式实例

让我们假设每个调度程序 VM 都没有状态。它接收请求,处理它并向其他机器发出 HTTP 请求。

在任何给定时间,每个 VM 将同时处理大约 700 个请求,同时从负载均衡器接收数据。

问题

如果我的抢占式 VM 处理 700 个请求,收到要终止的信号会怎样?

好吧,理论上应该有一个关闭脚本,以确保处理这些请求完成然后终止应用程序(干净退出)。这就引出了一个大问题:

  • 但是负载均衡器是否知道我的虚拟机正在关闭?它会继续向终止的虚拟机发送请求吗?

注意事项

如果是,则意味着某些请求将失败,因为一旦应用程序关闭,机器仍处于启动状态,负载均衡器继续向机器发送请求,而不知道应用程序已经关闭。

理想情况下,这些请求将作为失败的请求返回到负载均衡器,并将请求发送到另一台机器。然而,GCP 负载均衡器不够聪明,无法做到这一点,所以他们没有。

如果负载均衡器以某种方式知道该 VM 被选为抢先终止,则无需执行任何特殊操作。

哪一个?

4

2 回答 2

2

但是负载均衡器是否知道我的虚拟机正在关闭?它会继续向终止的虚拟机发送请求吗?

是的,负载均衡器将继续向实例发送请求。

您将需要创建一个关闭脚本并从负载均衡器中删除您的实例。

并不是负载均衡器不够聪明。负载均衡器不知道您的请求是否可以重试。该决定应由客户端/后端逻辑做出。

您的用例不是抢占式实例的好例子。抢占式实例将每 24 小时终止一次。如果您的目标是节省成本,请将长期实例定价的成本与抢先定价的成本进行比较。节省的成本不足以证明工程、测试和 QA 成本的合理性。

架构应该为失败而设计,但我不会故意选择一个会不断失败的架构。在您的情况下,每 24 小时一次。还存在您将无法启动另一个实例来弥补增加的负载的风险。并且存在所有实例都将被终止的风险。

于 2018-12-03T17:36:31.537 回答
2

我们有类似的问题。我们几乎已经通过负载均衡器健康检查解决了这个问题(在非常高的负载条件下存在一些问题)。技巧现在在抢占信号的 10-15 秒内,负载均衡器将在停止向该实例发送新请求时将实例标记为不健康。

解决方案:

  1. 负载均衡器每 3 秒检查一次实例的健康状况,并在第三次健康检查失败后将实例标记为不健康。因此负载均衡器在大约 10 秒内标记实例并停止发送新请求。
  2. ContextCloseEvent (Spring boot)使用或捕获 Java 中的抢占信号Runtime.getRuntime().addShutdownHook()(在我的情况下,JVM 接收到信号需要几秒钟)
  3. 将健康检查设置为失败,即健康检查端点将开始返回 404。
  4. 在关闭块中休眠 15-25 秒以让进程和新请求完成
  5. 释放资源并进行关闭日志记录。

    
    @EventListener
    public void onShutdown(ContextClosedEvent event) {
    
    
    log.warn("shutdown event received {}", event.getSource().toString());
    log.warn("/ping will respond 404, Main thread will sleep for 20 seconds to allow pending tasks to complete");
    
    isShuttingDown = true;
    try {
        Thread.sleep(SLEEP_BEFORE_SHUTDOWN_MILLIS);
    } catch (InterruptedException e) {
        log.error("sleep before shutdown interrupted", e);
    }
    
    log.warn("Shutting down now, daemon threads will continue work");
    releaseResources(); 
    
    log.info("{} {} on {} stopped.", NAME, VERSION, HOSTNAME);
    
    } //health endpoint @RequestMapping(value = "ping", produces = MediaType.TEXT_PLAIN_VALUE) public ResponseEntity ping() { if(isShuttingDown()) { log.warn("health failed - shutting down soon"); return new ResponseEntity(HttpStatus.NOT_FOUND); } return ResponseEntity.ok("pong"); }
于 2019-03-15T11:16:53.697 回答