我们有一个调度程序实例组,每个活动的VM每秒接收大约700个请求。该调度程序位于自动缩放的负载均衡器后面。到目前为止,我们所有的虚拟机都是通用的虚拟机,但是我们一直在研究使它们具有抢占性的可能性。
根据文档,GCP可以在any time终止抢占实例。
让我们假设每个调度程序VM都不处于状态。它接收一个请求,对其进行处理,然后向其他计算机发出HTTP请求。
在任何给定时间,每个虚拟机将同时处理约700个请求,同时从负载均衡器接收数据。
如果我的抢占式VM处理700个请求,收到终止信号,会发生什么情况?
嗯,从理论上讲,应该有一个shutdown script来确保处理完这些请求,然后终止该应用程序(清除出口)。这引出了一个大问题:
如果是,则表示某些请求将失败,因为一旦应用程序关闭,计算机仍处于启动状态,并且负载平衡器继续向该计算机发送请求,而不知道该应用程序已经存在下。
理想情况下,这些请求将作为失败请求返回给负载均衡器,并将其发送到另一台计算机。但是,GCP负载平衡器不够聪明,无法做到这一点。
如果负载平衡器知道该虚拟机已选择提前终止,则无需执行任何特殊操作。
是哪个?
答案 0 :(得分:1)
但是负载平衡器是否知道我的VM正在关闭?会吗 继续向终止VM发送请求?
是的,负载平衡器将继续向实例发送请求。
您将需要创建关闭脚本并将实例从负载平衡器中删除。
这并不是负载均衡器不够智能。负载平衡器不知道您的请求是否可以重试。该决定应由客户端/后端逻辑决定。
您的用例不是抢占式实例的好例子。抢占式实例将每24小时终止一次。如果您的目标是节省成本,请将长期实例定价与抢先定价进行比较。节省下来的钱不足以证明工程,测试和质量检查成本是合理的。
应该为失败而设计体系结构,但是我不会故意选择会不断失败的体系结构。您的情况是每24小时一次。还存在无法启动另一个实例来弥补增加的负载的风险。并且存在所有实例将被终止的风险。
答案 1 :(得分:0)
我们有类似的问题。我们几乎已经通过负载均衡器运行状况检查解决了它(在非常高的负载条件下存在一些问题)。 现在,Trick处于抢占信号的10-15秒之内,负载平衡器会因为停止向该实例发送新请求而将实例标记为不正常。
解决方案:
ContextCloseEvent (Spring boot)
或Runtime.getRuntime().addShutdownHook()
捕获Java中的抢占信号(在我的情况下,JVM接收到信号要花几秒钟的时间)释放资源并执行关机日志记录。
@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");
}