Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@ Apollo 2.5.0
* [doc: Add rust apollo client link](https://github.com/apolloconfig/apollo/pull/5514)
* [Perf: optimize namespace-related interface](https://github.com/apolloconfig/apollo/pull/5518)
* [Perf: Replace synchronized multimap with concurrent hashmap in NotificationControllerV2 for better performance](https://github.com/apolloconfig/apollo/pull/5532)
* [Feature: Enable graceful shutdown for apollo-adminservice and apollo-configservice](https://github.com/apolloconfig/apollo/pull/5536)
------------------
All issues and pull requests are [here](https://github.com/apolloconfig/apollo/milestone/16?closed=1)
3 changes: 3 additions & 0 deletions apollo-adminservice/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,12 @@ spring:
properties:
hibernate:
metadata_builder_contributor: com.ctrip.framework.apollo.common.jpa.SqlFunctionsMetadataBuilderContributor
lifecycle:
timeout-per-shutdown-phase: ${GRACEFUL_SHUTDOWN_TIMEOUT:30s}

server:
port: 8090
shutdown: graceful

logging:
file:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright 2025 Apollo Authors
*
* Licensed 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.ctrip.framework.apollo.adminservice;

import com.ctrip.framework.apollo.AdminServiceTestConfiguration;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

/**
* Configuration validation test for graceful shutdown feature.
*
* This test verifies that the graceful shutdown configuration is properly loaded
* from application.yml by checking ServerProperties and the web server lifecycle.
*
* Note: This test does NOT verify the actual behavior of graceful shutdown
* (e.g., waiting for in-flight requests). Full behavioral testing requires:
* - Integration tests with real HTTP requests during shutdown
* - Manual testing in staging/production environments
* - Monitoring of shutdown metrics and logs
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = AdminServiceTestConfiguration.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class GracefulShutdownConfigurationTest {

@Autowired
private ServletWebServerApplicationContext webServerAppContext;

@Autowired
private ServerProperties serverProperties;

@Test
public void testGracefulShutdownIsConfigured() {
assertNotNull("WebServer should be available", webServerAppContext);
assertTrue("Server should be running", webServerAppContext.getWebServer().getPort() > 0);

// Verify graceful shutdown is enabled in application.yml
assertEquals("Graceful shutdown should be enabled in application.yml",
"graceful", serverProperties.getShutdown().name().toLowerCase());

// Verify the lifecycle processor exists (indicates graceful shutdown is enabled)
assertNotNull("Lifecycle processor should be present for graceful shutdown",
webServerAppContext.getBean("lifecycleProcessor"));
}
}
3 changes: 3 additions & 0 deletions apollo-adminservice/src/test/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@
spring:
application:
name: apollo-adminservice
lifecycle:
timeout-per-shutdown-phase: ${GRACEFUL_SHUTDOWN_TIMEOUT:30s}

server:
port: ${port:8090}
shutdown: graceful

eureka:
instance:
Expand Down
3 changes: 3 additions & 0 deletions apollo-assembly/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ spring:
properties:
hibernate:
metadata_builder_contributor: com.ctrip.framework.apollo.common.jpa.SqlFunctionsMetadataBuilderContributor
lifecycle:
timeout-per-shutdown-phase: ${GRACEFUL_SHUTDOWN_TIMEOUT:30s}

logging:
file:
Expand Down Expand Up @@ -60,6 +62,7 @@ eureka:
register-with-eureka: false

server:
shutdown: graceful
compression:
enabled: true
tomcat:
Expand Down
3 changes: 3 additions & 0 deletions apollo-configservice/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,12 @@ spring:
properties:
hibernate:
metadata_builder_contributor: com.ctrip.framework.apollo.common.jpa.SqlFunctionsMetadataBuilderContributor
lifecycle:
timeout-per-shutdown-phase: ${GRACEFUL_SHUTDOWN_TIMEOUT:30s}

server:
port: 8080
shutdown: graceful

logging:
file:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright 2025 Apollo Authors
*
* Licensed 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.ctrip.framework.apollo.configservice;

import com.ctrip.framework.apollo.ConfigServiceTestConfiguration;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

/**
* Configuration validation test for graceful shutdown feature.
*
* This test verifies that the graceful shutdown configuration is properly loaded
* from application.yml by checking ServerProperties and the web server lifecycle.
*
* Note: This test does NOT verify the actual behavior of graceful shutdown
* (e.g., waiting for in-flight requests). Full behavioral testing requires:
* - Integration tests with real HTTP requests during shutdown
* - Manual testing in staging/production environments
* - Monitoring of shutdown metrics and logs
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = ConfigServiceTestConfiguration.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class GracefulShutdownConfigurationTest {

@Autowired
private ServletWebServerApplicationContext webServerAppContext;

@Autowired
private ServerProperties serverProperties;

@Test
public void testGracefulShutdownIsConfigured() {
assertNotNull("WebServer should be available", webServerAppContext);
assertTrue("Server should be running", webServerAppContext.getWebServer().getPort() > 0);

// Verify graceful shutdown is enabled in application.yml
assertEquals("Graceful shutdown should be enabled in application.yml",
"graceful", serverProperties.getShutdown().name().toLowerCase());

// Verify the lifecycle processor exists (indicates graceful shutdown is enabled)
assertNotNull("Lifecycle processor should be present for graceful shutdown",
webServerAppContext.getBean("lifecycleProcessor"));
}
}
3 changes: 3 additions & 0 deletions apollo-configservice/src/test/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@
spring:
application:
name: apollo-configservice
lifecycle:
timeout-per-shutdown-phase: ${GRACEFUL_SHUTDOWN_TIMEOUT:30s}

server:
port: ${port:8080}
shutdown: graceful

eureka:
instance:
Expand Down
6 changes: 5 additions & 1 deletion docs/en/deployment/distributed-deployment-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,9 @@ export JAVA_OPTS="-server -Xms6144m -Xmx6144m -Xss256k -XX:MetaspaceSize=128m -X

> Note 4: If the eureka.service.url of ApolloConfigDB.ServerConfig is only configured with the currently starting machine, the eureka registration failure information will be output in the log during the process of starting apollo-configservice, such as `com.sun.jersey .api.client.ClientHandlerException: java.net.ConnectException: Connection refused`. It should be noted that this is the expected situation, because apollo-configservice needs to register the service with the Meta Server (itself), but because it has not yet woken up during the startup process, it will report this error. The retry action will be performed later, so the registration will be normal after the service is up.

> Note 5: If you read this, I believe that you must be someone who reads the documentation carefully, and you are a little bit closer to success. Keep going, you should be able to complete the distributed deployment of Apollo soon! But do you feel that Apollo's distributed deployment steps are a bit cumbersome? Do you have any advice you would like to share with the author? If the answer is yes, please move to [#1424](https://github.com/apolloconfig/apollo/issues/1424) and look forward to your suggestions!
> Note 5: Starting from version 2.5.0, apollo-configservice supports graceful shutdown. When the service receives a stop signal, it will wait for in-flight requests to complete before shutting down, with a default timeout of 30 seconds. This feature is enabled via Spring Boot's `server.shutdown=graceful` and `spring.lifecycle.timeout-per-shutdown-phase=${GRACEFUL_SHUTDOWN_TIMEOUT:30s}` configuration. To adjust the timeout, you can set the `GRACEFUL_SHUTDOWN_TIMEOUT` environment variable (e.g., `60s`, `2m`) or modify the settings in application.yml. In Kubernetes environments, ensure the Pod's `terminationGracePeriodSeconds` is greater than the configured timeout (recommend at least 10 seconds more).

> Note 6: If you read this, I believe that you must be someone who reads the documentation carefully, and you are a little bit closer to success. Keep going, you should be able to complete the distributed deployment of Apollo soon! But do you feel that Apollo's distributed deployment steps are a bit cumbersome? Do you have any advice you would like to share with the author? If the answer is yes, please move to [#1424](https://github.com/apolloconfig/apollo/issues/1424) and look forward to your suggestions!

#### 2.2.2.2 Deploy apollo-adminservice

Expand All @@ -531,6 +533,8 @@ export JAVA_OPTS="-server -Xms2560m -Xmx2560m -Xss256k -XX:MetaspaceSize=128m -X

> Note 3: To adjust the listening port of the service, you can modify the `SERVER_PORT` in scripts/startup.sh.

> Note 4: Starting from version 2.5.0, apollo-adminservice supports graceful shutdown. When the service receives a stop signal, it will wait for in-flight requests to complete before shutting down, with a default timeout of 30 seconds. This feature is enabled via Spring Boot's `server.shutdown=graceful` and `spring.lifecycle.timeout-per-shutdown-phase=${GRACEFUL_SHUTDOWN_TIMEOUT:30s}` configuration. To adjust the timeout, you can set the `GRACEFUL_SHUTDOWN_TIMEOUT` environment variable (e.g., `60s`, `2m`) or modify the settings in application.yml. In Kubernetes environments, ensure the Pod's `terminationGracePeriodSeconds` is greater than the configured timeout (recommend at least 10 seconds more).

#### 2.2.2.3 Deploy apollo-portal

Upload `apollo-portal-x.x.x-github.zip` to the server, unzip it and execute scripts/startup.sh. To stop the service, execute scripts/shutdown.sh.
Expand Down
6 changes: 5 additions & 1 deletion docs/zh/deployment/distributed-deployment-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,9 @@ export JAVA_OPTS="-server -Xms6144m -Xmx6144m -Xss256k -XX:MetaspaceSize=128m -X

> 注4:如果ApolloConfigDB.ServerConfig的eureka.service.url只配了当前正在启动的机器的话,在启动apollo-configservice的过程中会在日志中输出eureka注册失败的信息,如`com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused`。需要注意的是,这个是预期的情况,因为apollo-configservice需要向Meta Server(它自己)注册服务,但是因为在启动过程中,自己还没起来,所以会报这个错。后面会进行重试的动作,所以等自己服务起来后就会注册正常了。

> 注5:如果你看到了这里,相信你一定是一个细心阅读文档的人,而且离成功就差一点点了,继续加油,应该很快就能完成Apollo的分布式部署了!不过你是否有感觉Apollo的分布式部署步骤有点繁琐?是否有啥建议想要和作者说?如果答案是肯定的话,请移步 [#1424](https://github.com/apolloconfig/apollo/issues/1424),期待你的建议!
> 注5:apollo-configservice从2.5.0版本开始支持优雅下线功能。当服务收到停止信号时,会等待正在处理的请求完成后再关闭,默认等待时间为30秒。此功能通过Spring Boot的`server.shutdown=graceful`和`spring.lifecycle.timeout-per-shutdown-phase=${GRACEFUL_SHUTDOWN_TIMEOUT:30s}`配置启用。如需调整超时时间,可以通过环境变量`GRACEFUL_SHUTDOWN_TIMEOUT`设置(如`60s`、`2m`等),或直接修改application.yml中的配置。在Kubernetes环境中,请确保Pod的`terminationGracePeriodSeconds`大于配置的超时时间(建议至少多10秒)。

> 注6:如果你看到了这里,相信你一定是一个细心阅读文档的人,而且离成功就差一点点了,继续加油,应该很快就能完成Apollo的分布式部署了!不过你是否有感觉Apollo的分布式部署步骤有点繁琐?是否有啥建议想要和作者说?如果答案是肯定的话,请移步 [#1424](https://github.com/apolloconfig/apollo/issues/1424),期待你的建议!

#### 2.2.2.2 部署apollo-adminservice
将对应环境的`apollo-adminservice-x.x.x-github.zip`上传到服务器上,解压后执行scripts/startup.sh即可。如需停止服务,执行scripts/shutdown.sh.
Expand All @@ -511,6 +513,8 @@ export JAVA_OPTS="-server -Xms2560m -Xmx2560m -Xss256k -XX:MetaspaceSize=128m -X

> 注3:如要调整服务的监听端口,可以修改scripts/startup.sh中的`SERVER_PORT`。

> 注4:apollo-adminservice从2.5.0版本开始支持优雅下线功能。当服务收到停止信号时,会等待正在处理的请求完成后再关闭,默认等待时间为30秒。此功能通过Spring Boot的`server.shutdown=graceful`和`spring.lifecycle.timeout-per-shutdown-phase=${GRACEFUL_SHUTDOWN_TIMEOUT:30s}`配置启用。如需调整超时时间,可以通过环境变量`GRACEFUL_SHUTDOWN_TIMEOUT`设置(如`60s`、`2m`等),或直接修改application.yml中的配置。在Kubernetes环境中,请确保Pod的`terminationGracePeriodSeconds`大于配置的超时时间(建议至少多10秒)。

#### 2.2.2.3 部署apollo-portal
将`apollo-portal-x.x.x-github.zip`上传到服务器上,解压后执行scripts/startup.sh即可。如需停止服务,执行scripts/shutdown.sh.

Expand Down