Skip to content

Commit

Permalink
feat: HTTP, added new global configuration to specify the number of I…
Browse files Browse the repository at this point in the history
…O thread for the HTTP server (arcadedb.server.httpsIoThreads)
  • Loading branch information
lvca committed Oct 25, 2023
1 parent 01eb6ca commit fff96e1
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 59 deletions.
14 changes: 12 additions & 2 deletions engine/src/main/java/com/arcadedb/GlobalConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,14 @@ public void run() {
String.class, "default", new Callable<>() {
@Override
public Object call(final Object value) {
final int cores = Runtime.getRuntime().availableProcessors();

final String v = value.toString();
if (v.equalsIgnoreCase("default")) {
// NOT MUCH TO DO HERE, THIS IS THE DEFAULT OPTION
} else if (v.equalsIgnoreCase("high-performance")) {
ASYNC_OPERATIONS_QUEUE_IMPL.setValue("fast");

final int cores = Runtime.getRuntime().availableProcessors();
if (cores > 1)
// USE ONLY HALF OF THE CORES MINUS ONE
ASYNC_WORKER_THREADS.setValue((cores / 2) - 1);
Expand All @@ -102,10 +103,12 @@ public Object call(final Object value) {
SQL_STATEMENT_CACHE.setValue(16);
HA_REPLICATION_QUEUE_SIZE.setValue(8);
ASYNC_OPERATIONS_QUEUE_IMPL.setValue("standard");
SERVER_HTTP_IO_THREADS.setValue(cores > 8 ? 4 : 2);

} else if (v.equalsIgnoreCase("low-cpu")) {
ASYNC_WORKER_THREADS.setValue(1);
ASYNC_OPERATIONS_QUEUE_IMPL.setValue("standard");
SERVER_HTTP_IO_THREADS.setValue(cores > 8 ? 4 : 2);
} else
throw new IllegalArgumentException("Profile '" + v + "' not available");

Expand Down Expand Up @@ -324,6 +327,10 @@ public Object call(final Object value) {
"TCP/IP port number used for incoming HTTPS connections. Specify a single port or a range `<from-<to>`. Default is 2490-2499 to accept a range of ports in case they are occupied.",
String.class, "2490-2499"),

SERVER_HTTP_IO_THREADS("arcadedb.server.httpsIoThreads", SCOPE.SERVER,
"Number of threads to use in the HTTP servers. The default number for most of the use cases is 2 threads per cpus (or 1 per virtual core)",
Integer.class, 0, null, (value) -> Runtime.getRuntime().availableProcessors()),

SERVER_HTTP_TX_EXPIRE_TIMEOUT("arcadedb.server.httpTxExpireTimeout", SCOPE.SERVER,
"Timeout in seconds for a HTTP transaction to expire. This timeout is computed from the latest command against the transaction",
Long.class, 30),
Expand Down Expand Up @@ -485,7 +492,10 @@ public static void resetAll() {
* Reset the configuration to the default value.
*/
public void reset() {
value = defValue;
if (callbackIfNoSet != null)
value = callbackIfNoSet.call(null);
else
value = defValue;
}

public static void dumpConfiguration(final PrintStream out) {
Expand Down
92 changes: 37 additions & 55 deletions server/src/main/java/com/arcadedb/server/http/HttpServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,18 +69,19 @@
import static io.undertow.UndertowOptions.SHUTDOWN_TIMEOUT;

public class HttpServer implements ServerPlugin {
private final ArcadeDBServer server;
private final ArcadeDBServer server;
private final HttpSessionManager sessionManager;
private final JsonSerializer jsonSerializer = new JsonSerializer();
private final WebSocketEventBus webSocketEventBus;
private Undertow undertow;
private String listeningAddress;
private String host;
private int httpPortListening;
private final JsonSerializer jsonSerializer = new JsonSerializer();
private final WebSocketEventBus webSocketEventBus;
private Undertow undertow;
private String listeningAddress;
private String host;
private int httpPortListening;

public HttpServer(final ArcadeDBServer server) {
this.server = server;
this.sessionManager = new HttpSessionManager(server.getConfiguration().getValueAsInteger(GlobalConfiguration.SERVER_HTTP_TX_EXPIRE_TIMEOUT) * 1000L);
this.sessionManager = new HttpSessionManager(
server.getConfiguration().getValueAsInteger(GlobalConfiguration.SERVER_HTTP_TX_EXPIRE_TIMEOUT) * 1000L);
this.webSocketEventBus = new WebSocketEventBus(this.server);
}

Expand Down Expand Up @@ -109,10 +110,12 @@ public void startService() {
final int[] httpPortRange = extractPortRange(configuredHTTPPort);

final Object configuredHTTPSPort = configuration.getValue(GlobalConfiguration.SERVER_HTTPS_INCOMING_PORT);
final int[] httpsPortRange = configuredHTTPSPort != null && !configuredHTTPSPort.toString().isEmpty() ? extractPortRange(configuredHTTPSPort) : null;
final int[] httpsPortRange =
configuredHTTPSPort != null && !configuredHTTPSPort.toString().isEmpty() ? extractPortRange(configuredHTTPSPort) : null;

LogManager.instance().log(this, Level.INFO, "- Starting HTTP Server (host=%s port=%s httpsPort=%s)...", host, configuredHTTPPort,
httpsPortRange != null ? configuredHTTPSPort : "-");
LogManager.instance()
.log(this, Level.INFO, "- Starting HTTP Server (host=%s port=%s httpsPort=%s)...", host, configuredHTTPPort,
httpsPortRange != null ? configuredHTTPSPort : "-");

final PathHandler routes = new PathHandler();

Expand Down Expand Up @@ -151,7 +154,8 @@ public void startService() {
.addHttpListener(httpPortListening, host)//
.setHandler(routes)//
.setSocketOption(Options.READ_TIMEOUT, configuration.getValueAsInteger(GlobalConfiguration.NETWORK_SOCKET_TIMEOUT))
.setWorkerThreads( 500 )
.setIoThreads(configuration.getValueAsInteger(GlobalConfiguration.SERVER_HTTP_IO_THREADS))//
.setWorkerThreads(500)//
.setServerOption(SHUTDOWN_TIMEOUT, 5000);

if (configuration.getValueAsBoolean(GlobalConfiguration.NETWORK_USE_SSL)) {
Expand Down Expand Up @@ -185,7 +189,8 @@ public void startService() {
}

httpPortListening = -1;
final String msg = String.format("Unable to listen to a HTTP port in the configured port range %d - %d", httpPortRange[0], httpPortRange[1]);
final String msg = String.format("Unable to listen to a HTTP port in the configured port range %d - %d", httpPortRange[0],
httpPortRange[1]);
LogManager.instance().

log(this, Level.SEVERE, msg);
Expand All @@ -212,7 +217,7 @@ private int[] extractPortRange(final Object configuredPort) {
}
}

return new int[]{portFrom, portTo};
return new int[] { portFrom, portTo };
}

public HttpSessionManager getSessionManager() {
Expand Down Expand Up @@ -242,71 +247,48 @@ public WebSocketEventBus getWebSocketEventBus() {
private SSLContext createSSLContext() throws Exception {
ContextConfiguration configuration = server.getConfiguration();

String keystorePath = validateStoreProperty(configuration,
NETWORK_SSL_KEYSTORE,
"SSL key store path is empty"
);
String keystorePassword = validateStoreProperty(configuration,
NETWORK_SSL_KEYSTORE_PASSWORD,
"SSL key store password is empty"
);
String keystorePath = validateStoreProperty(configuration, NETWORK_SSL_KEYSTORE, "SSL key store path is empty");
String keystorePassword = validateStoreProperty(configuration, NETWORK_SSL_KEYSTORE_PASSWORD,
"SSL key store password is empty");

String truststorePath = validateStoreProperty(configuration,
NETWORK_SSL_TRUSTSTORE,
"SSL trust store path is empty"
);
String truststorePassword = validateStoreProperty(configuration,
NETWORK_SSL_TRUSTSTORE_PASSWORD,
"SSL trust store password is empty"
);
String truststorePath = validateStoreProperty(configuration, NETWORK_SSL_TRUSTSTORE, "SSL trust store path is empty");
String truststorePassword = validateStoreProperty(configuration, NETWORK_SSL_TRUSTSTORE_PASSWORD,
"SSL trust store password is empty");

KeyStore keyStore = configureSSLForKeystore(keystorePath,
keystorePassword);
KeyStore keyStore = configureSSLForKeystore(keystorePath, keystorePassword);

KeyStore trustStore = configureSSLForTruststore(truststorePath,
truststorePassword);
KeyStore trustStore = configureSSLForTruststore(truststorePath, truststorePassword);

KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore,
keystorePassword.toCharArray());
keyManagerFactory.init(keyStore, keystorePassword.toCharArray());
KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();

TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trustStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();

SSLContext sslContext = SSLContext.getInstance(TlsProtocol.getLatestTlsVersion().getTlsVersion());
sslContext.init(keyManagers,
trustManagers,
null
);
sslContext.init(keyManagers, trustManagers, null);

return sslContext;
}

private KeyStore configureSSLForKeystore(String keystorePath,
String keystorePassword)
private KeyStore configureSSLForKeystore(String keystorePath, String keystorePassword)
throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException {

return SslUtils.loadKeystoreFromStream(SocketFactory.getAsStream(keystorePath),
keystorePassword,
SslUtils.getDefaultKeystoreTypeForKeystore(() -> PKCS12)
);
return SslUtils.loadKeystoreFromStream(SocketFactory.getAsStream(keystorePath), keystorePassword,
SslUtils.getDefaultKeystoreTypeForKeystore(() -> PKCS12));
}

private KeyStore configureSSLForTruststore(String truststorePath,
String truststorePassword)
private KeyStore configureSSLForTruststore(String truststorePath, String truststorePassword)
throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException {

return SslUtils.loadKeystoreFromStream(SocketFactory.getAsStream(truststorePath),
truststorePassword,
SslUtils.getDefaultKeystoreTypeForTruststore(() -> JKS)
);
return SslUtils.loadKeystoreFromStream(SocketFactory.getAsStream(truststorePath), truststorePassword,
SslUtils.getDefaultKeystoreTypeForTruststore(() -> JKS));
}

private String validateStoreProperty(ContextConfiguration contextConfiguration,
GlobalConfiguration configurationKey,
String errorMessage) {
private String validateStoreProperty(ContextConfiguration contextConfiguration, GlobalConfiguration configurationKey,
String errorMessage) {
String storePropertyValue = contextConfiguration.getValueAsString(configurationKey);
if ((storePropertyValue == null) || storePropertyValue.isEmpty()) {
throw new ServerSecurityException(errorMessage);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public void setTestConfiguration() {
GlobalConfiguration.TEST.setValue(true);
GlobalConfiguration.SERVER_ROOT_PATH.setValue("./target");
GlobalConfiguration.SERVER_ROOT_PASSWORD.setValue(DEFAULT_PASSWORD_FOR_TESTS);
GlobalConfiguration.SERVER_HTTP_IO_THREADS.setValue(2);
}

@BeforeEach
Expand All @@ -58,8 +59,10 @@ public void endTest() {
}

protected static void testLog(final String msg, final Object... args) {
LogManager.instance().log(StaticBaseServerTest.class, Level.FINE, "***********************************************************************************");
LogManager.instance().log(StaticBaseServerTest.class, Level.FINE,
"***********************************************************************************");
LogManager.instance().log(StaticBaseServerTest.class, Level.FINE, "TEST: " + msg, args);
LogManager.instance().log(StaticBaseServerTest.class, Level.FINE, "***********************************************************************************");
LogManager.instance().log(StaticBaseServerTest.class, Level.FINE,
"***********************************************************************************");
}
}

0 comments on commit fff96e1

Please sign in to comment.