Skip to content

Commit

Permalink
Healthcheck management fix.
Browse files Browse the repository at this point in the history
  • Loading branch information
straybirdzls authored and caojie09 committed Jan 9, 2019
1 parent ca08e52 commit fac6c51
Show file tree
Hide file tree
Showing 8 changed files with 230 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,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 @@ -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.AbstractEndpoint;
Expand Down Expand Up @@ -50,32 +51,44 @@ public SofaBootReadinessCheckEndpoint(String id, boolean sensitive) {

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

boolean afterHealthCheckCallbackStatus = readinessCheckListener.getHealthCallbackStatus();
Map<String, Health> afterHealthCheckCallbackDetails = readinessCheckListener
.getHealthCallbackDetails();

Builder builder;
Map<String, Health> healths = new HashMap<>();
if (healthCheckerStatus && afterHealthCheckCallbackStatus) {
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(afterHealthCheckCallbackDetails)) {
builder = builder.withDetail("ReadinessCheckCallback", afterHealthCheckCallbackDetails);
}
healths.put("SOFABootReadinessHealthCheckInfo", builder.build());
boolean healthCheckerStatus = readinessCheckListener.getHealthCheckerStatus();
Map<String, Health> healthCheckerDetails = readinessCheckListener
.getHealthCheckerDetails();
Map<String, Health> healthIndicatorDetails = readinessCheckListener
.getHealthIndicatorDetails();

boolean afterHealthCheckCallbackStatus = readinessCheckListener
.getHealthCallbackStatus();
Map<String, Health> afterHealthCheckCallbackDetails = readinessCheckListener
.getHealthCallbackDetails();

// HealthIndicator
healths.putAll(healthIndicatorDetails);
Builder builder;
if (healthCheckerStatus && afterHealthCheckCallbackStatus) {
builder = Health.up();
} else {
builder = Health.down();
}
if (!CollectionUtils.isEmpty(healthCheckerDetails)) {
builder = builder.withDetail("HealthChecker", healthCheckerDetails);
}
if (!CollectionUtils.isEmpty(afterHealthCheckCallbackDetails)) {
builder = builder.withDetail("ReadinessCheckCallback",
afterHealthCheckCallbackDetails);
}
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 @@ -84,6 +84,7 @@ private HttpStatus getStatus(Health health) {
private void setupDefaultStatusMapping() {
addStatusMapping(Status.DOWN, HttpStatus.SERVICE_UNAVAILABLE);
addStatusMapping(Status.OUT_OF_SERVICE, HttpStatus.SERVICE_UNAVAILABLE);
addStatusMapping(Status.UNKNOWN, HttpStatus.INTERNAL_SERVER_ERROR);
}

private void addStatusMapping(Status status, HttpStatus httpStatus) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ public class ReadinessCheckListener implements ApplicationContextAware, Environm

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

private boolean readinessCheckFinish = false;

@Override
public void setApplicationContext(ApplicationContext cxt) throws BeansException {
applicationContext = cxt;
Expand All @@ -88,11 +90,14 @@ public int getOrder() {

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

/**
Expand Down Expand Up @@ -173,4 +178,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("/config/application-management.properties")
@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,78 @@
/*
* 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.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;
}

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

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

}

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

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

public ResponseEntity<String> getLivenessCheckResponse() {
return livenessCheckResponse;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#
# 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.
#

management.port=8888

0 comments on commit fac6c51

Please sign in to comment.