fix(music): add Lavalink health-check with auto-reconnect

- Add 30s scheduled health-check that detects unavailable nodes
- Remove and re-add node when it becomes unavailable (fixes WS disconnect)
- Log WebSocketClosedEvent for debugging connection drops
This commit is contained in:
loki5512344 2026-08-31 14:54:15 +02:00
parent 520dccbafe
commit ad17cff40e
Signed by: boba
GPG key ID: 253067914055423B
2 changed files with 54 additions and 4 deletions

View file

@ -9,6 +9,7 @@ import dev.arbjerg.lavalink.client.event.ReadyEvent;
import dev.arbjerg.lavalink.client.event.StatsEvent; import dev.arbjerg.lavalink.client.event.StatsEvent;
import dev.arbjerg.lavalink.client.event.TrackEndEvent; import dev.arbjerg.lavalink.client.event.TrackEndEvent;
import dev.arbjerg.lavalink.client.event.TrackStartEvent; import dev.arbjerg.lavalink.client.event.TrackStartEvent;
import dev.arbjerg.lavalink.client.event.WebSocketClosedEvent;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -65,5 +66,18 @@ class LavalinkEventRouter {
manager.getScheduler().onTrackEnd(event.getTrack(), event.getEndReason()); manager.getScheduler().onTrackEnd(event.getTrack(), event.getEndReason());
} }
}); });
client
.on(WebSocketClosedEvent.class)
.subscribe(
event ->
LoggerDecorator.music(
logger,
"WebSocket закрыт: code="
+ event.getCode()
+ ", reason="
+ event.getReason()
+ ", byRemote="
+ event.getByRemote()));
} }
} }

View file

@ -2,6 +2,9 @@ package creator.loki.feature.music.core;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.LongConsumer; import java.util.function.LongConsumer;
import com.google.inject.Inject; import com.google.inject.Inject;
@ -27,6 +30,13 @@ public class LavalinkManager {
private final BotConfig config; private final BotConfig config;
private final QueueRepository queueRepository; private final QueueRepository queueRepository;
private volatile LongConsumer voiceDisconnectHandler; private volatile LongConsumer voiceDisconnectHandler;
private final ScheduledExecutorService healthCheck =
Executors.newSingleThreadScheduledExecutor(
r -> {
Thread t = new Thread(r, "lavalink-health-check");
t.setDaemon(true);
return t;
});
@Inject @Inject
public LavalinkManager(BotConfig config, QueueRepository queueRepository) { public LavalinkManager(BotConfig config, QueueRepository queueRepository) {
@ -46,6 +56,17 @@ public class LavalinkManager {
this.client = new LavalinkClient(Helpers.getUserIdFromToken(botToken)); this.client = new LavalinkClient(Helpers.getUserIdFromToken(botToken));
addNode(lavalinkConfig);
new LavalinkEventRouter(client, musicManagers).register();
healthCheck.scheduleWithFixedDelay(this::checkNodeHealth, 30, 30, TimeUnit.SECONDS);
LoggerDecorator.music(
logger, "LavalinkManager инициализирован, нода: " + lavalinkConfig.getWsUri());
}
private void addNode(LavalinkConfig lavalinkConfig) {
client.addNode( client.addNode(
new NodeOptions.Builder() new NodeOptions.Builder()
.setName("main") .setName("main")
@ -53,11 +74,26 @@ public class LavalinkManager {
.setPassword(lavalinkConfig.getPassword()) .setPassword(lavalinkConfig.getPassword())
.setHttpTimeout(30000L) .setHttpTimeout(30000L)
.build()); .build());
}
new LavalinkEventRouter(client, musicManagers).register(); private void checkNodeHealth() {
try {
LoggerDecorator.music( var nodes = client.getNodes();
logger, "LavalinkManager инициализирован, нода: " + lavalinkConfig.getWsUri()); if (nodes.isEmpty()) {
logger.warn("Lavalink: нет нод, пересоздаю");
addNode(config.getLavalinkConfig());
return;
}
var node = nodes.get(0);
if (!node.getAvailable()) {
logger.warn("Lavalink нода '{}' недоступна, переподключаю", node.getName());
client.removeNode(node);
addNode(config.getLavalinkConfig());
LoggerDecorator.music(logger, "Lavalink нода переподключена");
}
} catch (Exception e) {
logger.error("Ошибка health-check Lavalink", e);
}
} }
public LavalinkClient getClient() { public LavalinkClient getClient() {