ScheduledExecutorService прекращает запуск случайным образом

#java #runnable #scheduledexecutorservice #discord-jda

#java #работоспособный #scheduledexecutorservice #разногласия-jda

Вопрос:

У меня довольно сложный бот, использующий Discord JDA API, и для моей периодической проверки изменений статуса у меня есть ScheduledExecutorService . Проблема в том, что когда я ввожу команды боту, существует случайная (?) вероятность того, что служба просто перестанет работать, делая всю программу невосприимчивой без какой-либо ошибки в stacktrace. Я вывел это, установив простой счетчик таймера, и кажется, что он просто останавливается случайным образом (не совсем, скорее, у него нет определенного шаблона для этого). Это класс средства проверки:

 
import net.dv8tion.jda.core.JDA;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.User;
import us.verif.bot.sql.Sql;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class PeriodicCheck implements Runnable {
    int i = 0;
    private JDA jda;

    public PeriodicCheck(JDA jda) {
        this.jda = jda;
    }


    public void run() {
        i  ;
        System.out.println(i);
        try {
            Connection connection = DataSource.getConnection();
            connection.setCatalog("verifus");
            PreparedStatement preparedStatement = connection.prepareStatement("select * from `activatedservers` where `expireDate` <= NOW()");
            ResultSet rs = preparedStatement.executeQuery();

            while (rs.next()) {
                String guildId = Config.getGuildId();
                User user = jda.getGuildById(guildId).getOwner().getUser();
                String server = jda.getGuildById(guildId).getName();
                user.openPrivateChannel().queue((channel) -> channel.sendMessage("Your Verifus activation for your server `"   server   "` has expired. nVisit https://verif.us/ to purchase.").queue());
            }
            connection.close();
            Sql.deleteExpiredGuilds();

            Connection connection1 = DataSource.getConnection();
            connection1.setCatalog(Config.getGuildId());
            PreparedStatement preparedStatement1 = connection1.prepareStatement("select * from `verifiedusers` where `expireDate` <= NOW()");
            ResultSet rs1 = preparedStatement1.executeQuery();
            while (rs1.next()) {
                User user = jda.getUserById(rs1.getString("discordId"));
                String serverName = jda.getGuildById(Config.getGuildId()).getName();
                Guild guild = jda.getGuildById(Config.getGuildId());
                String role = rs1.getString("roleId");
                user.openPrivateChannel().queue((channel) -> channel.sendMessage("Your role `"   jda.getRoleById(role).getName()   "` in the server `"   serverName   "` has expired.").queue());
                guild.getController().removeSingleRoleFromMember(guild.getMemberById(rs1.getString("discordId")), jda.getRoleById(role)).queue();
            }
            connection1.close();
            Sql.deleteExpiredUsers();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
  

И вот часть основного класса, который запускает службу:

         PeriodicCheck periodicCheck = new PeriodicCheck(api);
        ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
        try {
            scheduledExecutorService.scheduleAtFixedRate(periodicCheck, 0, 1, TimeUnit.SECONDS);
        } catch (Exception e) {
            e.printStackTrace();
        }
  

Если я не ошибся при создании этого, то я почти уверен, что это происходит где-то еще в моей программе, и это будет трудно найти.
Обратите внимание, что это происходило раньше, когда я использовал TimerTask вместо ScheduledExecutorService , и это было потому, что я не закрывал свои SQL-соединения, но я позаботился о том, чтобы закрыть их все в своем коде.

Если ничего не появляется, я могу предоставить больше классов моей программы, хотя я не знаю, какой из них вызывает это, потому что это происходит случайным образом с каждой командой. Может ли это быть из-за моего компьютера / Интернета? Я запускаю это дома на своем персональном компьютере для тестирования.

В моем классе проверки также есть два других метода, которые явно не указаны, вот они: Sql.deleteExpiredGuilds :

         try {
            Connection connection = DataSource.getConnection();
            connection.setCatalog("verifus");
            connection.prepareStatement("delete from activatedservers where expireDate <= NOW()").executeUpdate();
            connection.close();
        } catch (SQLException e) { e.printStackTrace(); }
    }
  

Sql.deleteExpiredUsers :

         try {
            Connection connection = DataSource.getConnection();
            connection.setCatalog(Config.getGuildId());
            connection.prepareStatement("delete from verifiedusers where expireDate <= NOW()").executeUpdate();
            connection.close();
        } catch (SQLException e) { e.printStackTrace(); }
    }
  

Комментарии:

1. Возможно, у вас возникает неперехваченное исключение из scheduleAtFixedRate Throws: исключение RejectedExecutionException — если задача не может быть запланирована к выполнению Исключение NullPointerException — если команда равна null Исключение IllegalArgumentException — если период меньше или равен нулю

2. ScheduleExecutorServices имеют неудачную политику автоматической остановки, если задача выдает исключение, без предоставления какого-либо уведомления об этом исключении. На мой взгляд, это один из немногих случаев, когда catch (Exception e) (что вы уже делаете) оправдано. Возможно, вы даже захотите изменить его на catch (Throwable t) , просто чтобы убедиться, что какая-то ошибка не останавливает исполнителя.

3. Спасибо, я попробую это, когда смогу. Есть ли какой-либо лучший способ проверки, кроме использования ScheduledExecutorService ?

4. Вы можете изменить обработчик неперехваченных исключений по умолчанию с помощью Thread.setDefaultUncaughtExceptionHandler