Improved resilience of registry communication.
This commit is contained in:
parent
b0a289e35f
commit
b72a5a8b7a
|
@ -7,7 +7,6 @@ import nl.andrewlalis.aos_server_registry.servlet.dto.ServerStatusUpdate;
|
||||||
import nl.andrewlalis.aos_server_registry.util.Requests;
|
import nl.andrewlalis.aos_server_registry.util.Requests;
|
||||||
import nl.andrewlalis.aos_server_registry.util.Responses;
|
import nl.andrewlalis.aos_server_registry.util.Responses;
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
import javax.servlet.http.HttpServlet;
|
import javax.servlet.http.HttpServlet;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
@ -22,7 +21,7 @@ import java.util.Map;
|
||||||
|
|
||||||
public class ServerInfoServlet extends HttpServlet {
|
public class ServerInfoServlet extends HttpServlet {
|
||||||
@Override
|
@Override
|
||||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
||||||
int page = Requests.getIntParam(req, "page", 0, i -> i >= 0);
|
int page = Requests.getIntParam(req, "page", 0, i -> i >= 0);
|
||||||
int size = Requests.getIntParam(req, "size", 20, i -> i >= 5 && i <= 50);
|
int size = Requests.getIntParam(req, "size", 20, i -> i >= 5 && i <= 50);
|
||||||
String searchQuery = Requests.getStringParam(req, "q", null, s -> !s.isBlank());
|
String searchQuery = Requests.getStringParam(req, "q", null, s -> !s.isBlank());
|
||||||
|
@ -44,7 +43,7 @@ public class ServerInfoServlet extends HttpServlet {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
||||||
var info = Requests.getBody(req, ServerInfoUpdate.class);
|
var info = Requests.getBody(req, ServerInfoUpdate.class);
|
||||||
try {
|
try {
|
||||||
this.saveNewServer(info);
|
this.saveNewServer(info);
|
||||||
|
@ -56,15 +55,9 @@ public class ServerInfoServlet extends HttpServlet {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
||||||
var status = Requests.getBody(req, ServerStatusUpdate.class);
|
var status = Requests.getBody(req, ServerStatusUpdate.class);
|
||||||
try {
|
this.updateServerStatus(status, resp);
|
||||||
this.updateServerStatus(status);
|
|
||||||
Responses.ok(resp, Map.of("message", "Server status updated."));
|
|
||||||
} catch (SQLException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
Responses.internalServerError(resp, "Database error.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<ServerInfoResponse> getData(int size, int page, String searchQuery, String order, String orderDir) throws SQLException {
|
private List<ServerInfoResponse> getData(int size, int page, String searchQuery, String order, String orderDir) throws SQLException {
|
||||||
|
@ -146,18 +139,26 @@ public class ServerInfoServlet extends HttpServlet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateServerStatus(ServerStatusUpdate status) throws SQLException {
|
private void updateServerStatus(ServerStatusUpdate status, HttpServletResponse resp) throws IOException {
|
||||||
var con = DataManager.getInstance().getConnection();
|
try {
|
||||||
PreparedStatement stmt = con.prepareStatement("""
|
var con = DataManager.getInstance().getConnection();
|
||||||
|
PreparedStatement stmt = con.prepareStatement("""
|
||||||
UPDATE servers SET current_players = ?
|
UPDATE servers SET current_players = ?
|
||||||
WHERE name = ? AND address = ?
|
WHERE name = ? AND address = ?
|
||||||
""");
|
""");
|
||||||
stmt.setInt(1, status.currentPlayers());
|
stmt.setInt(1, status.currentPlayers());
|
||||||
stmt.setString(2, status.name());
|
stmt.setString(2, status.name());
|
||||||
stmt.setString(3, status.address());
|
stmt.setString(3, status.address());
|
||||||
int rowCount = stmt.executeUpdate();
|
int rowCount = stmt.executeUpdate();
|
||||||
stmt.close();
|
stmt.close();
|
||||||
if (rowCount != 1) throw new SQLException("Could not update server status.");
|
if (rowCount != 1) {
|
||||||
System.out.println("Updated server status for " + status.name() + " @ " + status.address());
|
Responses.notFound(resp);
|
||||||
|
} else {
|
||||||
|
Responses.ok(resp);
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Responses.internalServerError(resp, "Database error.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,10 @@ import static nl.andrewlalis.aos_server_registry.ServerRegistry.mapper;
|
||||||
* JSON responses.
|
* JSON responses.
|
||||||
*/
|
*/
|
||||||
public class Responses {
|
public class Responses {
|
||||||
|
public static void ok(HttpServletResponse resp) {
|
||||||
|
resp.setStatus(HttpServletResponse.SC_OK);
|
||||||
|
}
|
||||||
|
|
||||||
public static void ok(HttpServletResponse resp, Object body) throws IOException {
|
public static void ok(HttpServletResponse resp, Object body) throws IOException {
|
||||||
resp.setStatus(HttpServletResponse.SC_OK);
|
resp.setStatus(HttpServletResponse.SC_OK);
|
||||||
resp.setContentType("application/json");
|
resp.setContentType("application/json");
|
||||||
|
|
|
@ -2,6 +2,7 @@ module aos_server {
|
||||||
requires java.logging;
|
requires java.logging;
|
||||||
requires aos_core;
|
requires aos_core;
|
||||||
requires java.desktop;
|
requires java.desktop;
|
||||||
|
requires java.net.http;
|
||||||
requires com.fasterxml.jackson.databind;
|
requires com.fasterxml.jackson.databind;
|
||||||
requires com.fasterxml.jackson.dataformat.yaml;
|
requires com.fasterxml.jackson.dataformat.yaml;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
package nl.andrewlalis.aos_server;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.http.HttpClient;
|
||||||
|
import java.net.http.HttpRequest;
|
||||||
|
import java.net.http.HttpResponse;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The registry manager is responsible for keeping the server registry up to
|
||||||
|
* date with this server's information, by sending periodic update HTTP messages.
|
||||||
|
*/
|
||||||
|
public class RegistryManager {
|
||||||
|
private final ScheduledExecutorService executorService;
|
||||||
|
private final Server server;
|
||||||
|
|
||||||
|
private final ObjectMapper mapper;
|
||||||
|
private final HttpClient httpClient;
|
||||||
|
|
||||||
|
public RegistryManager(Server server) {
|
||||||
|
this.server = server;
|
||||||
|
this.mapper = new ObjectMapper();
|
||||||
|
this.executorService = Executors.newScheduledThreadPool(3);
|
||||||
|
this.httpClient = HttpClient.newBuilder()
|
||||||
|
.executor(this.executorService)
|
||||||
|
.connectTimeout(Duration.ofSeconds(3))
|
||||||
|
.build();
|
||||||
|
this.executorService.submit(this::sendInfo);
|
||||||
|
this.executorService.scheduleAtFixedRate(
|
||||||
|
this::sendUpdate,
|
||||||
|
server.getSettings().getRegistrySettings().getUpdateInterval(),
|
||||||
|
server.getSettings().getRegistrySettings().getUpdateInterval(),
|
||||||
|
TimeUnit.SECONDS
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendInfo() {
|
||||||
|
try {
|
||||||
|
Map<String, Object> data = new HashMap<>();
|
||||||
|
data.put("name", this.server.getSettings().getRegistrySettings().getName());
|
||||||
|
data.put("address", this.server.getSettings().getRegistrySettings().getAddress());
|
||||||
|
data.put("description", this.server.getSettings().getRegistrySettings().getDescription());
|
||||||
|
data.put("location", this.server.getSettings().getRegistrySettings().getLocation());
|
||||||
|
data.put("maxPlayers", this.server.getSettings().getMaxPlayers());
|
||||||
|
data.put("currentPlayers", 0);
|
||||||
|
HttpRequest request = HttpRequest.newBuilder()
|
||||||
|
.uri(new URI(this.server.getSettings().getRegistrySettings().getRegistryUri() + "/serverInfo"))
|
||||||
|
.POST(HttpRequest.BodyPublishers.ofByteArray(this.mapper.writeValueAsBytes(data)))
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.build();
|
||||||
|
this.httpClient.sendAsync(request, HttpResponse.BodyHandlers.discarding());
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendUpdate() {
|
||||||
|
try {
|
||||||
|
Map<String, Object> data = new HashMap<>();
|
||||||
|
data.put("name", this.server.getSettings().getRegistrySettings().getName());
|
||||||
|
data.put("address", this.server.getSettings().getRegistrySettings().getAddress());
|
||||||
|
data.put("currentPlayers", server.getPlayerCount());
|
||||||
|
HttpRequest request = HttpRequest.newBuilder()
|
||||||
|
.uri(new URI(this.server.getSettings().getRegistrySettings().getRegistryUri() + "/serverInfo"))
|
||||||
|
.PUT(HttpRequest.BodyPublishers.ofByteArray(this.mapper.writeValueAsBytes(data)))
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.build();
|
||||||
|
this.httpClient.sendAsync(request, responseInfo -> {
|
||||||
|
if (responseInfo.statusCode() == 404) {
|
||||||
|
System.out.println("Received 404 when sending registry update. Re-sending registry info...");
|
||||||
|
this.sendInfo();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void shutdown() {
|
||||||
|
this.executorService.shutdown();
|
||||||
|
try {
|
||||||
|
while (!this.executorService.awaitTermination(3, TimeUnit.SECONDS)) {
|
||||||
|
System.out.println("Waiting for scheduler to terminate.");
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,7 +25,9 @@ import java.net.Socket;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.ThreadLocalRandom;
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class Server {
|
public class Server {
|
||||||
private final ServerSettings settings;
|
private final ServerSettings settings;
|
||||||
|
@ -37,6 +39,7 @@ public class Server {
|
||||||
private final WorldUpdater worldUpdater;
|
private final WorldUpdater worldUpdater;
|
||||||
private final ServerCli cli;
|
private final ServerCli cli;
|
||||||
private final ChatManager chatManager;
|
private final ChatManager chatManager;
|
||||||
|
private RegistryManager registryManager;
|
||||||
|
|
||||||
private volatile boolean running;
|
private volatile boolean running;
|
||||||
|
|
||||||
|
@ -50,6 +53,9 @@ public class Server {
|
||||||
this.initWorld();
|
this.initWorld();
|
||||||
this.worldUpdater = new WorldUpdater(this, this.world);
|
this.worldUpdater = new WorldUpdater(this, this.world);
|
||||||
this.chatManager = new ChatManager(this);
|
this.chatManager = new ChatManager(this);
|
||||||
|
if (settings.getRegistrySettings().isDiscoverable()) {
|
||||||
|
this.registryManager = new RegistryManager(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ServerSettings getSettings() {
|
public ServerSettings getSettings() {
|
||||||
|
@ -230,7 +236,7 @@ public class Server {
|
||||||
handler.send(new Message(Type.SERVER_SHUTDOWN));
|
handler.send(new Message(Type.SERVER_SHUTDOWN));
|
||||||
handler.shutdown();
|
handler.shutdown();
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (Exception e) {
|
||||||
System.err.println("Could not close server socket on shutdown: " + e.getMessage());
|
System.err.println("Could not close server socket on shutdown: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -252,6 +258,10 @@ public class Server {
|
||||||
System.out.println("Stopped CLI interface.");
|
System.out.println("Stopped CLI interface.");
|
||||||
this.dataTransceiver.shutdown();
|
this.dataTransceiver.shutdown();
|
||||||
System.out.println("Stopped data transceiver.");
|
System.out.println("Stopped data transceiver.");
|
||||||
|
if (this.registryManager != null) {
|
||||||
|
this.registryManager.shutdown();
|
||||||
|
System.out.println("Stopped registry communications.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package nl.andrewlalis.aos_server.settings;
|
||||||
public class RegistrySettings {
|
public class RegistrySettings {
|
||||||
private boolean discoverable;
|
private boolean discoverable;
|
||||||
private String registryUri;
|
private String registryUri;
|
||||||
|
private long updateInterval;
|
||||||
private String name;
|
private String name;
|
||||||
private String address;
|
private String address;
|
||||||
private String description;
|
private String description;
|
||||||
|
@ -16,6 +17,10 @@ public class RegistrySettings {
|
||||||
return registryUri;
|
return registryUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getUpdateInterval() {
|
||||||
|
return updateInterval;
|
||||||
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,10 @@ public class ServerSettings {
|
||||||
return ticksPerSecond;
|
return ticksPerSecond;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RegistrySettings getRegistrySettings() {
|
||||||
|
return registrySettings;
|
||||||
|
}
|
||||||
|
|
||||||
public PlayerSettings getPlayerSettings() {
|
public PlayerSettings getPlayerSettings() {
|
||||||
return playerSettings;
|
return playerSettings;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,10 @@ max-players: 32
|
||||||
ticks-per-second: 120
|
ticks-per-second: 120
|
||||||
|
|
||||||
# Information for the public server registry.
|
# Information for the public server registry.
|
||||||
registry-data:
|
registry-settings:
|
||||||
discoverable: true
|
discoverable: true
|
||||||
registry-uri: "http://localhost:8567"
|
registry-uri: "http://localhost:8567"
|
||||||
|
update-interval: 10
|
||||||
name: "Testing Server"
|
name: "Testing Server"
|
||||||
address: "localhost:8035"
|
address: "localhost:8035"
|
||||||
description: "A simple testing server for development."
|
description: "A simple testing server for development."
|
||||||
|
|
Loading…
Reference in New Issue