Spring上下文启动期间的准备情况调查

时间:2018-08-15 09:47:16

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

我们正在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 Stream来监听RabbitMQ队列。 我在上面的示例中添加了示例处理程序。 您对如何“暂停”有什么建议吗?

5 个答案:

答案 0 :(得分:0)

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

答案 1 :(得分:0)

如果您设置足够好的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'的时间),然后在应用程序失败之前,如果{ {1}}。

[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]

答案 2 :(得分:0)

如何添加一个初始化容器,该容器的唯一作用是没有应用程序的数据库迁移。 然后是另一个容器来为应用程序提供服务。但是在部署具有多个副本的应用程序时要小心。如果您使用的是部署,副本还将同时执行initcontainer。 如果需要多个副本,则可能要考虑使用StatefulSet。

答案 3 :(得分:0)

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

答案 4 :(得分:0)

PostConstruct中,不要让上下文忙于完成很长的任务。相反,应将迁移作为完全异步的任务开始,并允许Spring同时构建其余的上下文。在任务结束时,只需将共享Future设置为成功或失败即可。在代理中包装控制器(例如,可以通过AOP进行简化),其中除了运行状况检查之外的每种方法都试图在一个超时时间内从相同的未来中获取价值。如果成功,则迁移完成,所有调用均可用。如果没有,请拒绝通话。您的代理将充当门户,仅允许使用API​​的一部分,而该部分对于在迁移过程中可用至关重要。它的其余部分可以简单地以503进行响应,指示该服务尚未准备好。可能还可以通过测量和平均迁移所需的时间并取平均值,并使用RETRY-AFTER标头返回此值,来改善那些503响应。 使用MessageHandler,您可以做基本上相同的事情。您用handle方法等待将来的结果(允许的消息处理程序无限期地挂起)。设置结果后,此刻将继续进行消息处理。