Java Как изменить порт Spring boot сервера в нужный момент времени?
При старте spring boot (v2.4.10) сервера можно различными способами настроить порт этого сервера. Например через командную строку:
-Dserver.port=НУЖНЫЙ_ПОРТ
или
SpringApplication app = new SpringApplication(ThreadSpringServer.class);
app.setDefaultProperties(Collections.singletonMap("server.port", "НУЖНЫЙ_ПОРТ"));
context = app.run();
Или же такой вариант:
SpringApplication app = new SpringApplication(ThreadSpringServer.class);
app.setDefaultProperties(Collections.singletonMap("server.port", String.valueOf(НУЖНЫЙ_ПОРТ)));
context = app.run();
Так же я встречал вариант:
@Component
public class PropertiesCustomizer implements WebServerFactoryCustomizer<ConfigurableWebServerFactory> {
@Override
public void customize(ConfigurableWebServerFactory factory) {
factory.setPort(НОВЫЙ_ПОРТ);
}
}
Но он тоже не подходит, потому что я не могу вызвать этот код в нужный момент времени.
Подобные варианты я тестировал, и они хорошо работают, если надо инициализировать параметры при первом старте программы.
При этом сколько я не искал, не смог найти способ, при котором можно выполнить изменение порта сервера после, того как сервер уже был запущен. Например, я запустил сервер с портом 3333, но потом мне нужно, чтобы он слушал не этот 3333, а 4444. Тем самым, чтобы можно было 3333 изменить на 4444 в процессе работы приложения, а не только при инициализации или старте.
Подскажите пожалуйста, если ли способ организовать замену порта в процессе работы приложения? Может быть уже есть готовая реализация данного функционала внутри какого-то метода, чтобы можно было вызвать этот метод в нужный момент времени. Заранее спасибо большое.
Ответы (1 шт):
Spring и Spring Boot главным образом покрывают очень стандартные варианты использования тех или иных вещей.
Смена порта веб-сервера — это весьма нетипичный случай. И обычно реализации веб-серверов не предусматривают механизм смены порта "на лету". То есть на самом деле нужно запусть новый экземпляр сервера на новом порту, погасить старый, если это необходимо. При этом веб-сервер придется вынести из жизненного цикла Spring.
То есть план примерно такой:
- отказываетесь от web-плюшек Spring Boot
- делаете класс-обертку, который будет управлять веб-серверами и сможет быть спринговым бином.
- сами выбираете и конфигурируете сервер (тут все индивидуально), скармливаете ему обработчики, которые, могут жить в спринге (@Component)
- делаете у класса-обретки метод
changePort(int), который погасит веб-сервер и запустит новый.
Очень грубо и верхнеуровнево это может выглядеть так:
@Component
public class WebServerWrapper {
public static int DEFAULT_PORT = 3333;
volatile Server serverInstance; // Server это какая-то реализация веб-сервера (Undertow, Jetty, Tomcat, etc)
@PostConstruct
public void init() {
serverInstance = configureInstance(DEFAULT_PORT);
serverInstance.start();
}
@PreDestroy
public void shutdown() {
serverInstance.stop();
}
public changePort(int newPort) {
Server newInstance = configureInstance(newPort);
newInstance.start();
serverInstance.stop();
serverInstance = newInstance;
}
// Здесь вы полностью настраиваете новый экземпляр сервера.
// Обычно для этого сервер предоставляет какие-то фабрики или билдеры.
private Server configureInstance(int port) {
Server server = new .....;
List<Object> handlers = findHandlers();
// передаете в server обработчики запросов
return server;
}
// Здесь вы находите и возвращаете обработчики запросов
public List<Object> findHandlers() { ... }
}
Пример ручной конфигурации связки Undertow + JAXRS на одном из моих проектов:
@Bean(name = "apiUndertow", destroyMethod = "stop")
public UndertowJaxrsServer getUndertowJaxrsServer(
@Autowired SomeHandler1 handler1,
@Autowired SomeHandler2 handler2,
@Autowired SomeHandler3 handler3,
@Autowired SomeHandler4 handler4) {
Application app = new Application() {
@Override
public Set<Class<?>> getClasses() {
final Set<Class<?>> clazzes = new HashSet<Class<?>>();
return clazzes;
}
@Override
public Set<Object> getSingletons() {
HashSet<Object> objects = Sets.newHashSet(
handler1,
handler2,
handler3,
return objects;
}
};
Undertow.Builder builder = Undertow.builder()
.addHttpListener(apiPort, apiHost)
.setServerOption(Options.WORKER_NAME, "XNIO-API")
.setServerOption(UndertowOptions.ALWAYS_SET_DATE, true)
.setServerOption(UndertowOptions.ENABLE_STATISTICS, false)
.setServerOption(UndertowOptions.RECORD_REQUEST_START_TIME, false);
UndertowJaxrsServer resteasyToUndertowIntegration = new UndertowJaxrsServer();
resteasyToUndertowIntegration.deploy(app);
resteasyToUndertowIntegration.start(builder);
return resteasyToUndertowIntegration;
}