【问题标题】:Readiness probe during Spring context startupSpring 上下文启动期间的就绪探测
【发布时间】:2019-01-22 04:46:04
【问题描述】:

我们正在 OpenShift 中部署我们的 Spring Boot 应用程序。

目前,我们正在尝试在完全设置 webcontext 之前运行一个可能需要长时间运行的任务(数据库迁移)。 在迁移完全运行之前,应用程序不接受 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 Stream 来监听 RabbitMQ 队列。 我在上面的示例中添加了一个示例处理程序。 你对如何“暂停”有什么建议吗?

【问题讨论】:

    标签: spring spring-boot kubernetes openshift spring-boot-actuator


    【解决方案1】:

    知道迁移状态的 servletfilter 怎么样?这样,您应该能够处理任何入站请求并根据您的喜好返回响应代码。在系统完全启动之前,也不需要阻止任何请求处理程序。

    【讨论】:

    • 好主意!如上所述,更大的问题是消息监听器,我需要以某种方式暂停。
    • 在不知道流式 API 的情况下,我相信应该有与其他世界类似的可能性。我刚刚在这个 github 问题中阅读了云流中的拦截器:github.com/spring-cloud/spring-cloud-stream/issues/1000
    【解决方案2】:

    我认为如果您设置足够好的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]

    【讨论】:

    • 对!这是我们最初的想法。这里的问题是如果迁移运行时间稍长会发生什么? ;) 找到一个合理的阈值会很困难。
    • [kubernetes.io/docs/tasks/configure-pod-container/…],您可以使用periodSecondsfailureThreshold控制意外的工作时间。
    【解决方案3】:

    如何添加一个初始化容器,它的唯一作用是没有应用程序的数据库迁移东西。 然后另一个容器为应用程序提供服务。但是在部署具有 1 个以上副本的应用程序时要小心。如果您使用 Deployment,副本也会同时执行 initcontainer。 如果您需要多个副本,您可能需要考虑使用 StatefulSets。

    【讨论】:

      【解决方案4】:

      最好通过切换到重新创建部署策略并将迁移作为中间生命周期挂钩来处理此类数据库迁移。那时,您的应用程序没有任何实例正在运行,因此可以安全地完成。如果您不能停机,那么您需要让应用程序能够在迁移时针对您的数据库副本切换到某种脱机或只读模式。

      【讨论】:

        【解决方案5】:

        不要让上下文忙于在 PostConstruct 中执行长任务。而是将迁移作为完全异步任务开始,并允许 Spring 同时构建其余的上下文。在任务结束时只需设置一些共享Future 成功或失败。将控制器包装在代理中(例如,可以通过 AOP 促进),其中除了健康检查之外的每个方法都尝试在超时内从同一个未来获取值。如果成功,则迁移完成,所有调用都可用。如果没有,拒绝来电。您的代理将充当一个门,允许仅使用在迁移进行时可用的关键 API 的一部分。它的其余部分可能会简单地以 503 响应,指示服务尚未准备好。潜在地,也可以通过测量和平均迁移通常花费的时间并使用 RETRY-AFTER 标头返回此值来改进这些 503 响应。 使用MessageHandler,您基本上可以做同样的事情。您在 handle 方法中等待未来的结果(如果允许消息处理程序无限期挂起)。一旦设置了结果,它将从那一刻开始进行消息处理。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-11-02
          • 1970-01-01
          • 1970-01-01
          • 2019-12-05
          • 2019-05-21
          • 1970-01-01
          • 2019-11-21
          • 1970-01-01
          相关资源
          最近更新 更多