Skip to content

Commit

Permalink
feat/manual.readiness.callback (#766)
Browse files Browse the repository at this point in the history
  • Loading branch information
alaneuler authored Nov 18, 2020
1 parent 636c398 commit 0e6f10b
Show file tree
Hide file tree
Showing 12 changed files with 495 additions and 18 deletions.
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
<configuration>
<skip>true</skip>
</configuration>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
*/
package com.alipay.sofa.boot.actuator.autoconfigure.health;

import com.alipay.sofa.boot.actuator.health.ManualReadinessCallbackEndPoint;
import com.alipay.sofa.boot.constant.SofaBootConstants;
import com.alipay.sofa.healthcheck.HealthCheckProperties;
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint;
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
Expand Down Expand Up @@ -61,6 +63,14 @@ public ReadinessCheckListener readinessCheckListener() {
return new ReadinessCheckListener();
}

@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint(endpoint = ManualReadinessCallbackEndPoint.class)
@ConditionalOnProperty(prefix = SofaBootConstants.PREFIX, name = "manualReadinessCallback", havingValue = "true")
public ManualReadinessCallbackEndPoint manualReadinessCallbackEndPoint() {
return new ManualReadinessCallbackEndPoint();
}

@Bean
public HealthCheckerProcessor healthCheckerProcessor() {
return new HealthCheckerProcessor();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* 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.actuator.autoconfigure.test;

import com.alipay.sofa.healthcheck.ReadinessCheckListener;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;

/**
* @author <a href="mailto:[email protected]">Alaneuler</a>
* Created on 2020/11/18
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = EmptyConfiguration.class)
@RunWith(SpringRunner.class)
public class EndPointTest {
@Autowired
private TestRestTemplate restTemplate;

@Test
public void test() {
ResponseEntity<ReadinessCheckListener.ManualReadinessCallbackResult> response = restTemplate
.getForEntity("/actuator/triggerReadinessCallback",
ReadinessCheckListener.ManualReadinessCallbackResult.class);
Assert.assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* 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.actuator.autoconfigure.test;

import com.alipay.sofa.healthcheck.ReadinessCheckListener;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;

/**
* @author <a href="mailto:[email protected]">Alaneuler</a>
* Created on 2020/11/18
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = EmptyConfiguration.class, properties = "com.alipay.sofa.boot.manualReadinessCallback=true")
@RunWith(SpringRunner.class)
public class ManualEndPointTest {
@Autowired
private TestRestTemplate restTemplate;

@Test
public void test() {
ResponseEntity<ReadinessCheckListener.ManualReadinessCallbackResult> response = restTemplate
.getForEntity("/actuator/triggerReadinessCallback",
ReadinessCheckListener.ManualReadinessCallbackResult.class);
Assert.assertEquals(HttpStatus.OK, response.getStatusCode());
Assert.assertNotNull(response.getBody());
Assert.assertTrue(response.getBody().isSuccess());
Assert.assertTrue(response.getBody().getDetails().contains("invoked successfully"));

response = restTemplate.getForEntity("/actuator/triggerReadinessCallback",
ReadinessCheckListener.ManualReadinessCallbackResult.class);
Assert.assertEquals(HttpStatus.OK, response.getStatusCode());
Assert.assertNotNull(response.getBody());
Assert.assertFalse(response.getBody().isSuccess());
Assert.assertTrue(response.getBody().getDetails().contains("already triggered"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* 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.boot.actuator.health;

import com.alipay.sofa.healthcheck.ReadinessCheckListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;

/**
* @author <a href="mailto:[email protected]">Alaneuler</a>
* Created on 2020/11/17
*/
@Endpoint(id = "triggerReadinessCallback")
public class ManualReadinessCallbackEndPoint {
@Autowired
private ReadinessCheckListener readinessCheckListener;

@ReadOperation
public ReadinessCheckListener.ManualReadinessCallbackResult trigger() {
return readinessCheckListener.triggerReadinessCallback();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

import org.slf4j.Logger;
import org.springframework.beans.BeansException;
Expand Down Expand Up @@ -45,10 +46,10 @@
*/
public class ReadinessCheckListener implements ApplicationContextAware, PriorityOrdered,
ApplicationListener<ContextRefreshedEvent>, InitializingBean {
private static Logger logger = HealthCheckLoggerFactory
.getLogger(ReadinessCheckListener.class);
private static Logger logger = HealthCheckLoggerFactory
.getLogger(ReadinessCheckListener.class);

private final HealthAggregator healthAggregator = new OrderedHealthAggregator();
private final HealthAggregator healthAggregator = new OrderedHealthAggregator();

private ApplicationContext applicationContext;

Expand All @@ -64,17 +65,19 @@ public class ReadinessCheckListener implements ApplicationContextAware, Priority
@Autowired
private AfterReadinessCheckCallbackProcessor afterReadinessCheckCallbackProcessor;

private boolean healthCheckerStatus = true;
private boolean healthCheckerStatus = true;

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

private boolean healthIndicatorStatus = true;
private boolean healthIndicatorStatus = true;

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

private boolean healthCallbackStatus = true;
private boolean readinessCheckFinish = false;
private boolean healthCheckerInsulator = false;
private boolean healthCallbackStatus = true;
private boolean readinessCheckFinish = false;
private boolean healthCheckerInsulator = false;
private AtomicBoolean readinessCallbackTriggered = new AtomicBoolean(
false);

@Override
public void afterPropertiesSet() throws Exception {
Expand Down Expand Up @@ -132,10 +135,21 @@ public void readinessHealthCheck() {
}
}

if (healthCheckerStatus && healthIndicatorStatus) {
healthCallbackStatus = afterReadinessCheckCallbackProcessor
.afterReadinessCheckCallback(healthCallbackDetails);
// TODO: fix classloader (key) bug in SofaRuntimeConfigurationProperties
if ("false".equals(applicationContext.getEnvironment().getProperty(
SofaBootConstants.PREFIX + ".manualReadinessCallback", "false"))) {
if (healthCheckerStatus && healthIndicatorStatus) {
readinessCallbackTriggered.set(true);
logger.info("Invoking all readiness check callbacks...");
healthCallbackStatus = afterReadinessCheckCallbackProcessor
.afterReadinessCheckCallback(healthCallbackDetails);
}
} else {
logger
.info("Manual readiness callback is set to true, skip normal readiness callback. "
+ "You can trigger all readiness callbacks through URL: actuator/triggerReadinessCallback");
}

if (healthCheckerStatus && healthIndicatorStatus && healthCallbackStatus) {
logger.info("Readiness check result: success");
} else {
Expand All @@ -147,6 +161,24 @@ public void readinessHealthCheck() {
}
}

public ManualReadinessCallbackResult triggerReadinessCallback() {
if (healthCheckerStatus && healthIndicatorStatus) {
if (readinessCallbackTriggered.compareAndSet(false, true)) {
logger.info("Invoking all readiness callbacks...");
healthCallbackStatus = afterReadinessCheckCallbackProcessor
.afterReadinessCheckCallback(healthCallbackDetails);
return new ManualReadinessCallbackResult(true,
"Readiness callbacks invoked successfully with result: " + healthCallbackStatus);
} else {
logger.warn("Readiness callbacks are already triggered! Action skipped.");
return ManualReadinessCallbackResult.SKIPPED;
}
} else {
logger.warn("Health checker or indicator failed, skip all readiness callbacks!");
return ManualReadinessCallbackResult.STAGE_ONE_FAILED;
}
}

public Health aggregateReadinessHealth() {
Map<String, Health> healths = new HashMap<>();
if (!isReadinessCheckFinish()) {
Expand Down Expand Up @@ -230,4 +262,44 @@ public Map<String, Health> getHealthCallbackDetails() {
public boolean isReadinessCheckFinish() {
return readinessCheckFinish;
}

public AtomicBoolean getReadinessCallbackTriggered() {
return readinessCallbackTriggered;
}

public static class ManualReadinessCallbackResult {
public static ManualReadinessCallbackResult STAGE_ONE_FAILED = new ManualReadinessCallbackResult(
false,
"Health checker or indicator failed.");
public static ManualReadinessCallbackResult SKIPPED = new ManualReadinessCallbackResult(
false,
"Readiness callbacks are already triggered.");

private boolean success;
private String details;

public ManualReadinessCallbackResult() {
}

public ManualReadinessCallbackResult(boolean success, String details) {
this.success = success;
this.details = details;
}

public boolean isSuccess() {
return success;
}

public void setSuccess(boolean success) {
this.success = success;
}

public String getDetails() {
return details;
}

public void setDetails(String details) {
this.details = details;
}
}
}
Loading

0 comments on commit 0e6f10b

Please sign in to comment.