diff options
author | Anton Tananaev <anton.tananaev@gmail.com> | 2020-02-22 16:59:43 -0800 |
---|---|---|
committer | Anton Tananaev <anton.tananaev@gmail.com> | 2020-02-22 16:59:43 -0800 |
commit | 8d6e45331cb4ba86faaabfe7d1f9e7ccfd6e824d (patch) | |
tree | f6dcc78348c96a2785685dac603b5058304b62f6 | |
parent | deb519ebd6798450509afaf4067e140edd7eb0d0 (diff) | |
download | trackermap-server-8d6e45331cb4ba86faaabfe7d1f9e7ccfd6e824d.tar.gz trackermap-server-8d6e45331cb4ba86faaabfe7d1f9e7ccfd6e824d.tar.bz2 trackermap-server-8d6e45331cb4ba86faaabfe7d1f9e7ccfd6e824d.zip |
Support health monitoring
-rw-r--r-- | setup/default.xml | 1 | ||||
-rw-r--r-- | setup/traccar.service | 3 | ||||
-rw-r--r-- | src/main/java/org/traccar/Main.java | 59 | ||||
-rw-r--r-- | src/main/java/org/traccar/api/HealthCheckService.java | 90 |
4 files changed, 130 insertions, 23 deletions
diff --git a/setup/default.xml b/setup/default.xml index 1d82ad926..561b57a57 100644 --- a/setup/default.xml +++ b/setup/default.xml @@ -14,6 +14,7 @@ <entry key='web.port'>8082</entry> <entry key='web.path'>./web</entry> <entry key='web.cacheControl'>max-age=3600,public</entry> + <entry key='web.healthCheck'>true</entry> <entry key='geocoder.enable'>false</entry> <entry key='geocoder.type'>google</entry> diff --git a/setup/traccar.service b/setup/traccar.service index fe746dda8..6ae24ab8a 100644 --- a/setup/traccar.service +++ b/setup/traccar.service @@ -8,6 +8,9 @@ WorkingDirectory=/opt/traccar ExecStart=/opt/traccar/jre/bin/java -jar tracker-server.jar conf/traccar.xml SyslogIdentifier=traccar SuccessExitStatus=143 +WatchdogSec=100 +Restart=on-failure +RestartSec=10 [Install] WantedBy=multi-user.target diff --git a/src/main/java/org/traccar/Main.java b/src/main/java/org/traccar/Main.java index 6ebd1d399..f5690b26d 100644 --- a/src/main/java/org/traccar/Main.java +++ b/src/main/java/org/traccar/Main.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2020 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ import com.google.inject.Guice; import com.google.inject.Injector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.traccar.api.HealthCheckService; import java.lang.management.ManagementFactory; import java.lang.management.MemoryMXBean; @@ -105,6 +106,27 @@ public final class Main { } } + private static void scheduleHealthCheck() { + HealthCheckService service = new HealthCheckService(); + if (service.isEnabled()) { + new Timer().scheduleAtFixedRate( + service.createTask(), service.getPeriod(), service.getPeriod()); + } + } + + private static void scheduleDatabaseCleanup() { + new Timer().scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + try { + Context.getDataManager().clearHistory(); + } catch (SQLException error) { + LOGGER.warn("Clear history error", error); + } + } + }, 0, CLEAN_PERIOD); + } + public static void run(String configFile) { try { Context.init(configFile); @@ -118,35 +140,26 @@ public final class Main { Context.getWebServer().start(); } - new Timer().scheduleAtFixedRate(new TimerTask() { + new Timer().schedule(new TimerTask() { @Override public void run() { - try { - Context.getDataManager().clearHistory(); - } catch (SQLException error) { - LOGGER.warn("Clear history error", error); - } + Context.getWebServer().stop(); } - }, 0, CLEAN_PERIOD); + }, 10 * 60 * 1000); - Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { - @Override - public void uncaughtException(Thread t, Throwable e) { - LOGGER.error("Thread exception", e); - } - }); + scheduleHealthCheck(); + scheduleDatabaseCleanup(); - Runtime.getRuntime().addShutdownHook(new Thread() { - @Override - public void run() { - LOGGER.info("Shutting down server..."); + Thread.setDefaultUncaughtExceptionHandler((t, e) -> LOGGER.error("Thread exception", e)); + + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + LOGGER.info("Shutting down server..."); - if (Context.getWebServer() != null) { - Context.getWebServer().stop(); - } - Context.getServerManager().stop(); + if (Context.getWebServer() != null) { + Context.getWebServer().stop(); } - }); + Context.getServerManager().stop(); + })); } catch (Exception e) { LOGGER.error("Main method error", e); throw new RuntimeException(e); diff --git a/src/main/java/org/traccar/api/HealthCheckService.java b/src/main/java/org/traccar/api/HealthCheckService.java new file mode 100644 index 000000000..cd2856dfa --- /dev/null +++ b/src/main/java/org/traccar/api/HealthCheckService.java @@ -0,0 +1,90 @@ +/* + * Copyright 2020 Anton Tananaev (anton@traccar.org) + * + * 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 org.traccar.api; + +import com.sun.jna.Library; +import com.sun.jna.Native; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traccar.Context; + +import java.util.TimerTask; + +public class HealthCheckService { + + private static final Logger LOGGER = LoggerFactory.getLogger(HealthCheckService.class); + + private SystemD systemD; + + private boolean enabled; + private long period; + + public HealthCheckService() { + if (Context.getConfig().getBoolean("web.healthCheck") + && System.getProperty("os.name").toLowerCase().startsWith("linux")) { + try { + systemD = Native.load("systemd", SystemD.class); + String watchdogTimer = System.getenv("WATCHDOG_USEC"); + if (watchdogTimer != null && !watchdogTimer.isEmpty()) { + period = Long.parseLong(watchdogTimer) / 1000 * 4 / 5; + } + if (period > 0) { + LOGGER.info("Health check enabled with period {}", period); + enabled = true; + } + } catch (UnsatisfiedLinkError e) { + LOGGER.warn("No systemd support", e); + } + } + } + + public boolean isEnabled() { + return enabled; + } + + public long getPeriod() { + return period; + } + + private String getUrl() { + String address = Context.getConfig().getString("web.address", "localhost"); + int port = Context.getConfig().getInteger("web.port", 8082); + return "http://" + address + ":" + port + "/api/server"; + } + + public TimerTask createTask() { + return new TimerTask() { + @Override + public void run() { + LOGGER.debug("Health check running"); + int status = Context.getClient().target(getUrl()).request().get().getStatus(); + if (status == 200) { + int result = systemD.sd_notify(0, "WATCHDOG=1"); + if (result < 0) { + LOGGER.warn("Health check notify error {}", result); + } + } else { + LOGGER.warn("Health check failed with status {}", status); + } + } + }; + } + + interface SystemD extends Library { + int sd_notify(int unset_environment, String state); + } + +} |