我们正在OpenShift中部署我们的Spring boot应用程序。
目前,我们正在尝试在完全设置 Web 上下文之前运行一个可能长时间运行的任务(数据库迁移(。 尤其重要的是,在迁移完全运行之前,应用不接受 REST 请求或处理消息。 请参阅以下最小示例:
// DemoApplication.java
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
// MigrationConfig.java
@Configuration
@Slf4j
public class MigrationConfig {
@PostConstruct
public void run() throws InterruptedException {
log.info("Migration...");
// long running task
Thread.sleep(10000);
log.info("...Migration");
}
}
// Controller.java
@RestController
public class Controller {
@GetMapping("/test")
public String test() {
return "test";
}
}
// MessageHandler.java
@EnableBinding(Sink.class)
public class MessageHandler {
@StreamListener(Sink.INPUT)
public void handle(String message) {
System.out.println("Received: " + message);
}
}
到目前为止,这工作正常:在应用程序响应请求之前处理自动配置类。 然而,我们担心的是 OpenShifts 就绪性探测:目前我们使用执行器运行状况端点来检查应用程序是否已启动并运行。 如果迁移需要很长时间,OpenShift 可能会停止容器,这可能会使我们在数据库中处于不一致的状态。
有没有人知道我们如何传达应用程序正在启动,但阻止 REST 控制器或消息处理程序运行?
编辑
有多种方法可以阻止传入的REST请求,@martin-frey建议使用servletfilter。
对我们来说,更大的问题是流侦听器。我们使用 Spring Cloud 流来监听 RabbitMQ 队列。 我在上面的示例中添加了一个示例处理程序。 您对如何"暂停"它有什么建议吗?
知道迁移状态的 servletfilter 怎么样?这样,您应该能够处理任何入站请求并返回您喜欢的响应代码。此外,在系统完全启动之前,无需阻止任何请求处理程序。
我认为如果您设置了足够好的应用程序初始化initialDelaySeconds
,它可以运行您的应用程序 pod 而不受影响。0][1]
readinessProbe:
httpGet:
path: /_status/healthz
port: 8080
initialDelaySeconds: 10120
timeoutSeconds: 3
periodSeconds: 30
failureThreshold: 100
successThreshold: 1
此外,我建议设置具有相同条件的liveness probes
(但比readiness probes
'值更长的时间(,然后如果应用程序在initialDelaySeconds
之前失败,您可以实现 pod 的自动恢复。
[0] [ https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#define-readiness-probes ]
[1] [ https://docs.openshift.com/container-platform/latest/dev_guide/application_health.html ]
如何添加一个 init 容器,该容器的唯一角色是没有应用程序的数据库迁移内容。 然后是另一个容器来为应用程序提供服务。 但是,在部署具有 1 个以上副本的应用程序时要小心。 如果使用部署,副本也将同时执行 initcontainer。 如果需要多个副本,则可能需要改用 StatefulSets。
此类数据库迁移最好通过切换到重新创建部署策略并将迁移作为生命周期中期挂钩来处理。此时,没有正在运行的应用程序实例,因此可以安全地完成。如果无法停机,则需要在执行迁移时,需要能够针对数据库副本将应用程序切换到某种脱机或只读/仅模式。
不要让上下文忙于在PostConstruct
中执行长时间的任务。相反,迁移作为完全异步的任务开始,并允许Spring同时构建上下文的其余部分。在任务结束时,只需设置一些成功或失败的共享Future
。将控制器包装在代理中(例如,可以通过 AOP 促进(,其中除运行状况检查之外的每个方法都试图在超时内从相同的未来获取值。如果成功,则迁移完成,所有调用都可用。如果没有,请拒绝呼叫。您的代理将充当一个门,允许仅使用在迁移过程中可用的 API 部分。其余部分可能只是用 503 响应,表示服务尚未准备就绪。通过测量和平均迁移通常需要的时间并使用 RETRY-AFTER 标头返回此值,还可以改进这些 503 响应。 有了MessageHandler
,你基本上可以做同样的事情。在handle
方法中等待将来的结果(前提是允许消息处理程序无限期挂起(。设置结果后,它将从那一刻开始进行消息处理。