Fix servers not responding to SIGTERM and SIGINT

This commit is contained in:
Electroid 2017-07-25 01:12:06 -07:00
parent a3ddeaac9a
commit 64b19191db
4 changed files with 62 additions and 9 deletions

View File

@ -1,5 +1,7 @@
package tc.oc.commons.core.restart;
import javax.annotation.Nullable;
import java.time.Duration;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Logger;
@ -39,17 +41,23 @@ public class RequestRestartEvent {
}
public Deferral defer(String deferrerName) {
return defer(deferrerName, null);
}
public Deferral defer(String deferrerName, @Nullable Duration predictedDelay) {
logger.info("Restart deferred by " + deferrerName);
Deferral deferral = new Deferral(deferrerName);
Deferral deferral = new Deferral(deferrerName, predictedDelay);
deferrals.add(deferral);
return deferral;
}
public class Deferral {
private final String deferrerName;
private final Duration predictedDelay;
public Deferral(String deferrerName) {
public Deferral(String deferrerName, @Nullable Duration predictedDelay) {
this.deferrerName = deferrerName;
this.predictedDelay = predictedDelay;
}
public RequestRestartEvent request() {
@ -60,6 +68,10 @@ public class RequestRestartEvent {
return deferrerName;
}
public Duration predictedDelay() {
return predictedDelay;
}
/**
* Allow the deferred restart to proceed. After this method is called,
* this object becomes useless and can be discarded.

View File

@ -4,6 +4,9 @@ import javax.annotation.Nullable;
import javax.inject.Inject;
import java.time.Duration;
import java.util.Set;
import com.google.common.collect.Sets;
import tc.oc.commons.core.configuration.ConfigUtils;
import tc.oc.commons.core.exception.ExceptionHandler;
import tc.oc.minecraft.api.configuration.Configuration;
@ -55,4 +58,11 @@ public class RestartConfiguration {
public int kickLimit() {
return config.getInt("kick-limit", Integer.MAX_VALUE);
}
/**
* Restart the server when any of the given stop signals are received from the system.
*/
public Set<String> stopSignals() {
return ConfigUtils.getStringSet(config, "stop-signals", Sets.newHashSet("INT", "TERM"));
}
}

View File

@ -2,6 +2,7 @@ package tc.oc.commons.core.restart;
import java.time.Duration;
import java.time.Instant;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
import java.util.Set;
@ -17,6 +18,7 @@ import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import sun.misc.Signal;
import tc.oc.api.docs.Server;
import tc.oc.api.docs.virtual.ServerDoc;
import tc.oc.api.minecraft.MinecraftService;
@ -60,6 +62,7 @@ public class RestartManager implements PluginFacet, Tickable {
this.eventBus = eventBus;
this.threads = threads;
this.startTime = Instant.now();
onSignal(this.config.stopSignals());
}
@Override
@ -160,6 +163,15 @@ public class RestartManager implements PluginFacet, Tickable {
}
}
private void requestRestartInternal(Instant time, String reason, int priority) {
logger.info("Restart requested at " + time +
", with " + priority +
" priority, because \"" + reason + '"');
currentRequest = new RequestRestartEvent(logger, reason, priority, this::restartIfRequested);
eventBus.post(currentRequest);
restartIfRequested();
}
public ListenableFuture<?> cancelRestart() {
if(this.isRestartRequested()) {
return minecraftService.updateLocalServer(new ServerDoc.Restart() {
@ -202,12 +214,7 @@ public class RestartManager implements PluginFacet, Tickable {
}
if(newTime != null) {
logger.info("Restart requested at " + newTime +
", with " + newPriority +
" priority, because \"" + newReason + '"');
currentRequest = new RequestRestartEvent(logger, newReason, newPriority, this::restartIfRequested);
eventBus.post(currentRequest);
restartIfRequested();
requestRestartInternal(newTime, newReason, newPriority);
}
}
@ -236,4 +243,28 @@ public class RestartManager implements PluginFacet, Tickable {
return false;
}
}
private void onSignal(Collection<String> signals) {
signals.stream()
.map(Signal::new)
.forEach(signal -> Signal.handle(signal, s -> {
requestRestartInternal(Instant.now(), "Received signal " + s.getName() + " (" + s.getNumber() + ") from system", Integer.MAX_VALUE);
try {
Thread.sleep(currentRequest.deferrals()
.stream()
.filter(deferral -> deferral.predictedDelay() != null)
.map(RequestRestartEvent.Deferral::predictedDelay)
.findFirst()
.orElse(Duration.ZERO)
.toMillis() + 1L);
} catch(InterruptedException e) {
if(!minecraftServer.isStopping()) {
logger.severe(s.getName() + " signal is unable to wait for restart");
}
}
if(!restartIfRequested() && !minecraftServer.isStopping()) {
minecraftServer.stop();
}
}));
}
}

View File

@ -142,7 +142,7 @@ public class RestartListener implements PluginFacet, Listener {
public void onRequestRestart(RequestRestartEvent event) {
if(!server.isSuspended()) {
logger.info("Deferring restart");
deferral = event.defer(getClass().getName());
deferral = event.defer(getClass().getName(), config.time());
checkCountdown(mm.needCurrentMatch());
}
}