Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cherry pick health check management fix from 2.6.0 #356

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,15 @@ public class HealthCheckConstants {
* default {@literal com.alipay.sofa.isle.spring.health.SofaModuleHealthChecker} retry time interval value.
*/
public static final long SOFABOOT_MODULE_CHECK_RETRY_DEFAULT_INTERVAL = 1000;

/**
* health check not ready result key
*/
public static final String SOFABOOT_HEALTH_CHECK_NOT_READY_KEY = "HEALTH-CHECK-NOT-READY";

/**
* health check not ready result
*/
public static final String SOFABOOT_HEALTH_CHECK_NOT_READY_MSG = "App is still in startup process, please try later!";

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@
import com.alipay.sofa.healthcheck.service.SofaBootReadinessCheckEndpoint;
import com.alipay.sofa.healthcheck.startup.ReadinessCheckListener;
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint;
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorProperties;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
import org.springframework.boot.actuate.health.HealthStatusHttpMapper;
import org.springframework.boot.actuate.health.Status;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
Expand Down Expand Up @@ -72,12 +77,26 @@ public SofaBootReadinessCheckEndpoint sofaBootReadinessCheckEndpoint() {
}

@ConditionalOnClass(Endpoint.class)
@AutoConfigureBefore(name = "org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration")
public static class ReadinessCheckExtensionConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
public ReadinessEndpointWebExtension readinessEndpointWebExtension() {
return new ReadinessEndpointWebExtension();
}

@Bean
@ConditionalOnMissingBean
public HealthStatusHttpMapper createHealthStatusHttpMapper(HealthIndicatorProperties healthIndicatorProperties) {
HealthStatusHttpMapper statusHttpMapper = new HealthStatusHttpMapper();
if (healthIndicatorProperties.getHttpMapping() != null) {
statusHttpMapper.addStatusMapping(healthIndicatorProperties.getHttpMapping());
}
statusHttpMapper.addStatusMapping(Status.UNKNOWN,
WebEndpointResponse.STATUS_INTERNAL_SERVER_ERROR);
return statusHttpMapper;
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@
*/
package com.alipay.sofa.healthcheck.service;

import com.alipay.sofa.healthcheck.configuration.HealthCheckConstants;
import com.alipay.sofa.healthcheck.core.HealthCheckerProcessor;
import com.alipay.sofa.healthcheck.startup.ReadinessCheckListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.util.Assert;

import java.util.HashMap;
import java.util.Map;
Expand All @@ -38,8 +41,14 @@ public class SofaBootHealthIndicator implements HealthIndicator {
@Autowired
private HealthCheckerProcessor healthCheckerProcessor;

@Autowired
private ReadinessCheckListener readinessCheckListener;

@Override
public Health health() {
Assert.isTrue(readinessCheckListener.isReadinessCheckFinish(),
HealthCheckConstants.SOFABOOT_HEALTH_CHECK_NOT_READY_MSG);

Map<String, Health> healths = new HashMap<>();
boolean checkSuccessful = healthCheckerProcessor.livenessHealthCheck(healths);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
package com.alipay.sofa.healthcheck.service;

import com.alipay.sofa.healthcheck.configuration.HealthCheckConstants;
import com.alipay.sofa.healthcheck.startup.ReadinessCheckListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
Expand Down Expand Up @@ -46,34 +47,44 @@ public class SofaBootReadinessCheckEndpoint {

@ReadOperation
public Health health() {
boolean healthCheckerStatus = readinessCheckListener.getHealthCheckerStatus();
Map<String, Health> healthCheckerDetails = readinessCheckListener.getHealthCheckerDetails();
Map<String, Health> healthIndicatorDetails = readinessCheckListener
.getHealthIndicatorDetails();

boolean afterReadinessCheckCallbackStatus = readinessCheckListener
.getHealthCallbackStatus();
Map<String, Health> afterReadinessCheckCallbackDetails = readinessCheckListener
.getHealthCallbackDetails();

Builder builder;
Map<String, Health> healths = new HashMap<>();
if (healthCheckerStatus && afterReadinessCheckCallbackStatus) {
builder = Health.up();
if (!readinessCheckListener.isReadinessCheckFinish()) {
healths.put(
HealthCheckConstants.SOFABOOT_HEALTH_CHECK_NOT_READY_KEY,
Health
.unknown()
.withDetail(HealthCheckConstants.SOFABOOT_HEALTH_CHECK_NOT_READY_KEY,
HealthCheckConstants.SOFABOOT_HEALTH_CHECK_NOT_READY_MSG).build());
} else {
builder = Health.down();
}
if (!CollectionUtils.isEmpty(healthCheckerDetails)) {
builder = builder.withDetail("HealthChecker", healthCheckerDetails);
}
if (!CollectionUtils.isEmpty(afterReadinessCheckCallbackDetails)) {
builder = builder.withDetail("ReadinessCheckCallback",
afterReadinessCheckCallbackDetails);
}
healths.put("SOFABootReadinessHealthCheckInfo", builder.build());
boolean healthCheckerStatus = readinessCheckListener.getHealthCheckerStatus();
Map<String, Health> healthCheckerDetails = readinessCheckListener
.getHealthCheckerDetails();
Map<String, Health> healthIndicatorDetails = readinessCheckListener
.getHealthIndicatorDetails();

boolean afterReadinessCheckCallbackStatus = readinessCheckListener
.getHealthCallbackStatus();
Map<String, Health> afterReadinessCheckCallbackDetails = readinessCheckListener
.getHealthCallbackDetails();

// HealthIndicator
healths.putAll(healthIndicatorDetails);
Builder builder;
if (healthCheckerStatus && afterReadinessCheckCallbackStatus) {
builder = Health.up();
} else {
builder = Health.down();
}
if (!CollectionUtils.isEmpty(healthCheckerDetails)) {
builder = builder.withDetail("HealthChecker", healthCheckerDetails);
}
if (!CollectionUtils.isEmpty(afterReadinessCheckCallbackDetails)) {
builder = builder.withDetail("ReadinessCheckCallback",
afterReadinessCheckCallbackDetails);
}
healths.put("SOFABootReadinessHealthCheckInfo", builder.build());

// HealthIndicator
healths.putAll(healthIndicatorDetails);
}
return this.healthAggregator.aggregate(healths);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@
import com.alipay.sofa.healthcheck.core.*;
import com.alipay.sofa.healthcheck.log.SofaBootHealthCheckLoggerFactory;
import org.slf4j.Logger;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.health.Health;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.PriorityOrdered;
Expand All @@ -36,11 +39,12 @@
* @author liangen
* @author qilong.zql
*/
public class ReadinessCheckListener implements PriorityOrdered,
public class ReadinessCheckListener implements ApplicationContextAware, PriorityOrdered,
ApplicationListener<ContextRefreshedEvent> {

private static Logger logger = SofaBootHealthCheckLoggerFactory
.getLogger(ReadinessCheckListener.class);
private ApplicationContext applicationContext;

@Autowired
private Environment environment;
Expand All @@ -63,8 +67,14 @@ public class ReadinessCheckListener implements PriorityOrdered,
private Map<String, Health> healthIndicatorDetails = new HashMap<>();

private boolean healthCallbackStatus = true;
private boolean readinessCheckFinish = false;

private Map<String, Health> healthCallbackDetails = new HashMap<>();
@Override
public void setApplicationContext(ApplicationContext cxt) throws BeansException {
applicationContext = cxt;
}

private Map<String, Health> healthCallbackDetails = new HashMap<>();

@Override
public int getOrder() {
Expand All @@ -73,10 +83,13 @@ public int getOrder() {

@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
healthCheckerProcessor.init();
healthIndicatorProcessor.init();
afterReadinessCheckCallbackProcessor.init();
readinessHealthCheck();
if (applicationContext.equals(event.getApplicationContext())) {
healthCheckerProcessor.init();
healthIndicatorProcessor.init();
afterReadinessCheckCallbackProcessor.init();
readinessHealthCheck();
readinessCheckFinish = true;
}
}

/**
Expand Down Expand Up @@ -149,4 +162,8 @@ public boolean getHealthCallbackStatus() {
public Map<String, Health> getHealthCallbackDetails() {
return healthCallbackDetails;
}

public boolean isReadinessCheckFinish() {
return readinessCheckFinish;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alipay.sofa.healthcheck.management;

import com.alipay.sofa.healthcheck.configuration.HealthCheckConstants;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
*
* @author ruoshan
* @since 2.6.0
*/
@TestPropertySource(properties = "management.server.port:8888")
@SpringBootApplication
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class HealthCheckManagementTest {

@Autowired
private HighOrderApplicationListener highOrderApplicationListener;

@Test
public void testHealthCheckNotReadyReadiness() {
ResponseEntity<String> responseEntity = highOrderApplicationListener
.getReadinessCheckResponse();
Assert.assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode());
Assert.assertTrue(responseEntity.getBody().contains(
HealthCheckConstants.SOFABOOT_HEALTH_CHECK_NOT_READY_MSG));
}

@Test
public void testHealthCheckNotReadyLiveness() {
ResponseEntity<String> responseEntity = highOrderApplicationListener
.getLivenessCheckResponse();
Assert.assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode());
Assert.assertTrue(responseEntity.getBody().contains(
HealthCheckConstants.SOFABOOT_HEALTH_CHECK_NOT_READY_MSG));

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alipay.sofa.healthcheck.management;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.PriorityOrdered;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;

/**
*
* @author ruoshan
* @since 2.6.0
*/
@Component
public class HighOrderApplicationListener implements ApplicationListener<ContextRefreshedEvent>,
PriorityOrdered {

@Autowired
private TestRestTemplate testRestTemplate;

@Value("${management.server.port}")
private String managementPort;

@Autowired
private ApplicationContext applicationContext;

private ResponseEntity<String> readinessCheckResponse;

private ResponseEntity<String> livenessCheckResponse;

@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
// only listen to root application
if (!applicationContext.equals(contextRefreshedEvent.getApplicationContext())) {
return;
}

System.out.println(contextRefreshedEvent.getApplicationContext().getEnvironment()
.getProperty("management.server.port"));

readinessCheckResponse = testRestTemplate.getForEntity("http://localhost:" + managementPort
+ "/actuator/readiness",
String.class);

livenessCheckResponse = testRestTemplate.getForEntity("http://localhost:" + managementPort
+ "/actuator/health", String.class);

}

@Override
public int getOrder() {
return PriorityOrdered.HIGHEST_PRECEDENCE;
}

public ResponseEntity<String> getReadinessCheckResponse() {
return readinessCheckResponse;
}

public ResponseEntity<String> getLivenessCheckResponse() {
return livenessCheckResponse;
}
}