diff --git a/config.yaml b/config.yaml index 2cb1cfa5..bcaa8a4b 100644 --- a/config.yaml +++ b/config.yaml @@ -3,7 +3,10 @@ sendStatusMessage: true newsGuildId: newsChannelId: newsChannelPublishWaitMilliSec: 1000 -http.timeoutSec: 30 +http: + timeoutSec: 30 + thread.pool.size: 20 + logging: NONE shardsTotal: shardIds: disableCommandUpdate: false diff --git a/discord-connector/jda/build.gradle.kts b/discord-connector/jda/build.gradle.kts index bb765aef..cb6aa335 100644 --- a/discord-connector/jda/build.gradle.kts +++ b/discord-connector/jda/build.gradle.kts @@ -18,6 +18,7 @@ dependencies { implementation(libs.commons.text) implementation(libs.avaje.config) implementation(libs.avaje.slf4j) + implementation("com.squareup.okhttp3:logging-interceptor:5.1.0") compileOnly(libs.lombok) annotationProcessor(libs.lombok) diff --git a/discord-connector/jda/src/main/java/de/janno/discord/connector/jda/JdaClient.java b/discord-connector/jda/src/main/java/de/janno/discord/connector/jda/JdaClient.java index 1a47a9fd..6dd9a55a 100644 --- a/discord-connector/jda/src/main/java/de/janno/discord/connector/jda/JdaClient.java +++ b/discord-connector/jda/src/main/java/de/janno/discord/connector/jda/JdaClient.java @@ -4,6 +4,7 @@ import com.google.common.base.Stopwatch; import com.google.common.base.Strings; import com.google.common.io.Resources; +import com.google.common.util.concurrent.ThreadFactoryBuilder; import de.janno.discord.connector.api.*; import io.avaje.config.Config; import lombok.NonNull; @@ -33,7 +34,10 @@ import net.dv8tion.jda.api.sharding.DefaultShardManagerBuilder; import net.dv8tion.jda.api.sharding.ShardManager; import net.dv8tion.jda.internal.utils.IOUtil; +import okhttp3.ConnectionPool; +import okhttp3.Dispatcher; import okhttp3.OkHttpClient; +import okhttp3.logging.HttpLoggingInterceptor; import org.apache.commons.lang3.StringUtils; import reactor.core.publisher.Mono; import reactor.core.scheduler.Scheduler; @@ -45,6 +49,8 @@ import java.time.temporal.ChronoUnit; import java.util.*; import java.util.concurrent.ConcurrentSkipListSet; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; @@ -65,15 +71,34 @@ public JdaClient(@NonNull List slashCommands, final Stopwatch startStopwatch = Stopwatch.createStarted(); final Scheduler scheduler = Schedulers.boundedElastic(); final Set botInGuildIdSet = new ConcurrentSkipListSet<>(); - final Duration timeout = Duration.of(Config.getLong("http.timeoutSec"), ChronoUnit.SECONDS); + final int CONCURRENCY_LEVEL = Config.getInt("http.thread.pool.size", 20); + final Duration timeout = Duration.of(Config.getLong("http.timeoutSec", 30), ChronoUnit.SECONDS); + ConnectionPool connectionPool = new ConnectionPool(CONCURRENCY_LEVEL, 5, TimeUnit.MINUTES); + JdaMetrics.registerHttpClientConnectionPool(connectionPool, CONCURRENCY_LEVEL); + Dispatcher dispatcher = new Dispatcher(new ThreadPoolExecutor(8, 8, + 1, TimeUnit.MINUTES, + new LinkedBlockingQueue<>(16), + new ThreadFactoryBuilder().setNameFormat("okhttp-dispatcher-%d").setDaemon(true).build(), + new ThreadPoolExecutor.CallerRunsPolicy())); + dispatcher.setMaxRequestsPerHost(CONCURRENCY_LEVEL); + dispatcher.setMaxRequests(CONCURRENCY_LEVEL); + HttpLoggingInterceptor logging = new HttpLoggingInterceptor(log::debug); + logging.setLevel(logLevelFromString(Config.get("http.logging", "NONE"))); + + Config.onChange("http.logging", s -> logging.setLevel(logLevelFromString(s))); + final OkHttpClient okHttpClient = IOUtil.newHttpClientBuilder() .eventListener(JdaMetrics.getOkHttpEventListener()) + .addInterceptor(logging) .writeTimeout(timeout) .readTimeout(timeout) .connectTimeout(timeout) + .connectionPool(connectionPool) + .dispatcher(dispatcher) + .pingInterval(Duration.ofSeconds(3)) + .retryOnConnectionFailure(true) .build(); - JdaMetrics.registerHttpClient(okHttpClient); final String token = Config.get("token"); if (Strings.isNullOrEmpty(token)) { throw new IllegalArgumentException("Missing discord token"); @@ -238,6 +263,12 @@ public void onButtonInteraction(@NonNull ButtonInteractionEvent event) { .registerSlashCommands(shards.getFirst(), disableCommandUpdate); } + private static HttpLoggingInterceptor.Level logLevelFromString(String level) { + return HttpLoggingInterceptor.Level.getEntries().stream() + .filter(l -> l.name().equals(level)) + .findFirst().orElse(HttpLoggingInterceptor.Level.NONE); + } + @VisibleForTesting static void onGuildReadyHandler(GuildReadyEvent event, Set botInGuildIdSet, diff --git a/discord-connector/jda/src/main/java/de/janno/discord/connector/jda/JdaMetrics.java b/discord-connector/jda/src/main/java/de/janno/discord/connector/jda/JdaMetrics.java index 3b2b647d..0899d6fc 100644 --- a/discord-connector/jda/src/main/java/de/janno/discord/connector/jda/JdaMetrics.java +++ b/discord-connector/jda/src/main/java/de/janno/discord/connector/jda/JdaMetrics.java @@ -7,9 +7,10 @@ import io.micrometer.core.instrument.binder.okhttp3.OkHttpMetricsEventListener; import lombok.extern.slf4j.Slf4j; import net.dv8tion.jda.api.JDA; +import okhttp3.ConnectionPool; import okhttp3.EventListener; -import okhttp3.OkHttpClient; +import java.util.List; import java.util.Locale; import java.util.Set; @@ -24,8 +25,8 @@ public class JdaMetrics { private static final String USER_LOCALE = "userLocale"; private static final String SHARD_ID = "shardId"; - public static void registerHttpClient(OkHttpClient client) { - new OkHttpConnectionPoolMetrics(client.connectionPool()).bindTo(globalRegistry); + public static void registerHttpClientConnectionPool(ConnectionPool connectionPool, int maxIdleConnections) { + new OkHttpConnectionPoolMetrics(connectionPool, "okhttp.pool", List.of(), maxIdleConnections).bindTo(globalRegistry); } public static void startGuildCountGauge(Set botInGuildIdSet) {