Вопрос или проблема
Мое весеннее приложение для пакетной обработки пытается загрузить файл с мейнфрейма через SFTP (JDK 17). После завершения загрузки процесс продолжается с чтением, обработкой и записью. Это происходит дважды, так как есть две категории (А и Б). Категория А завершена. Когда категория ‘Б’ близка к завершению в процессе записи, в журналах отображаются следующие строки, после чего выполнение задания занимает около 10 минут.
Как правило, выполнение задания занимает около 10-15 минут, но как только эти строки появляются в журнале, выполнение задания занимает дополнительные 20 минут, занимая до 2 Гб (сначала занимает 1 Гб).
2024-10-22T746.949Z INFO [,,] 1 --- [-timer-thread-1] o.a.s.client.session.ClientSessionImpl : Отключение(ClientSessionImpl[[email protected]/19.59.28.35:222]): SSH2_DISCONNECT_PROTOCOL_ERROR - Обнаружен IdleTimeout после 600000/600000 мс.
2024-10-22T13:52:59.343Z INFO [,,] 1 --- [-timer-thread-1] o.a.s.client.session.ClientSessionImpl : Отключение(ClientSessionImpl[[email protected]/19.59.28.35:222]): SSH2_DISCONNECT_PROTOCOL_ERROR - Обнаружен IdleTimeout после 600001/600000 мс.
2024-10-22T13:52:59.944Z INFO [,,] 1 --- [-timer-thread-1] o.a.s.client.session.ClientSessionImpl : Отключение(ClientSessionImpl[[email protected]/19.59.28.35:222]): SSH2_DISCONNECT_PROTOCOL_ERROR - Обнаружен IdleTimeout после 600493/600000 мс.
2024-10-22T13:53:00.344Z INFO [,,] 1 --- [-timer-thread-1] o.a.s.client.session.ClientSessionImpl : Отключение(ClientSessionImpl[[email protected]/19.59.28.35:222]): SSH2_DISCONNECT_PROTOCOL_ERROR - Обнаружен IdleTimeout после 600575/600000 мс.
2024-10-22T13:53:04.143Z INFO [,,] 1 --- [-timer-thread-1] o.a.s.client.session.ClientSessionImpl : Отключение(ClientSessionImpl[[email protected]/19.59.28.35:222]): SSH2_DISCONNECT_PROTOCOL_ERROR - Обнаружен IdleTimeout после 600262/600000 мс.
2024-10-22T13:53:06.940Z INFO [,,] 1 --- [-timer-thread-1] o.a.s.client.session.ClientSessionImpl : Отключение(ClientSessionImpl[[email protected]/19.59.28.35:222]): SSH2_DISCONNECT_PROTOCOL_ERROR - Обнаружен IdleTimeout после 600190/600000 мс.
2024-10-22T13:53:55.243Z INFO [,,] 1 --- [-timer-thread-1] o.a.s.client.session.ClientSessionImpl : Отключение(ClientSessionImpl[[email protected]/19.59.28.35:222]): SSH2_DISCONNECT_PROTOCOL_ERROR - Обнаружен IdleTimeout после 600219/600000 мс.
2024-10-22T14:16:17.809Z WARN [,,] 1 --- [lTaskExecutor-2] c.f.p.m.f.l.StepExecutionListenerFactory : Я вызываю после шага в createOrderPartStepExecutionListener
2024-10-22T14:16:17.809Z WARN [,,] 1 --- [lTaskExecutor-2] c.f.p.m.f.l.StepExecutionListenerFactory : Я вызываю метод закрытия для обработчика заказов
2024
Поскольку это указывает на тайм-аут бездействия, я попытался установить тайм-аут в DefaultSftpSessionFactory
и попытался закрыть, реализовав Closeable
и используя factory.getSession().close()
. Ранее мы не получали эти строки. Это может быть связано с обновлением spring integration sftp вместе с jdk 17.
FtpClient.java
@Component
@Scope("prototype")
public class FtpClient{
@Autowired
public RestClientBuilder restClientBuilder;
@Value("${application.sftp.cmms.privatekey:}")
@Setter
public String sftpCmmsPrivateKey;
public SftpRemoteTemplate sftpRemoteTemplate;
private DefaultSftpSessionFactory factory;
public RemoteFileTemplate createFtpRemoteFileTemplate(String plantCode) {
FtpDataDTO ftpData = restClientBuilder.createClient().getFtpData(plantCode);
if (isSecureFTP()) {
factory = = new DefaultSftpSessionFactory();
factory.setPrivateKey(new ByteArrayResource(sftpCmmsPrivateKey.getBytes(StandardCharsets.US_ASCII)));
factory.setUser(ftpData.getUserId().trim());
factory.setHost(ftpData.getHostname().trim());
factory.setPort(222);
factory.setAllowUnknownKeys(true);
factory.setTimeout(300000);
sftpRemoteTemplate = new SftpRemoteTemplate(factory);
return sftpRemoteTemplate;
} else {
DefaultFtpSessionFactory factory = defaultFtpSessionFactory();
factory.setPassword(MovisEncryption.decrypt(ftpData.getPassword().trim()));
factory.setUsername(ftpData.getUserId().trim());
factory.setHost(ftpData.getHostname().trim());
factory.setPort(21);
return new FtpRemoteTemplate(factory);
}
}
private boolean isSecureFTP() {
return !sftpCmmsPrivateKey.isBlank();
}
Ответ или решение
Решение проблемы SSH2_DISCONNECT_PROTOCOL_ERROR в Spring Batch при использовании SFTP
Проблема
Ваша система на основе Spring Batch сталкивается с ненормальным поведением при загрузке файлов с основного компьютера через SFTP в JDK 17. После успешного завершения задач для категории A, при близком завершении обработки категории B, в логах фиксируется несколько строк, указывающих на SSH2_DISCONNECT_PROTOCOL_ERROR
, связанное с IdleTimeout
. Это приводит к увеличению времени выполнения задач до 20 минут и значительному увеличению потребления памяти (с 1 Гб до 2 Гб).
Причины возникновения проблемы
Ошибка SSH2_DISCONNECT_PROTOCOL_ERROR
возникает, когда SSH-соединение автоматически разрывается из-за бездействия, превышающего установленный таймаут (в вашем случае – 600 секунд). Это может быть вызвано несколькими факторами:
-
Недостаточная активность между запросами. Если не происходит обмена данными или поддерживающих сигналов (keep-alive) в течение указанного времени, соединение может быть закрыто сервером.
-
Изменения в конфигурации Spring Integration или версии JDK. Обновления могут изменить поведение по умолчанию, что может привести к несоответствиям в работе вашего приложения.
-
Неправильная конфигурация таймаутов. Увеличение таймаута может не помочь, если в приложении не реализована логика для поддержания активности соединения.
Возможные решения
Чтобы устранить эту проблему, рассмотрите следующие подходы:
- Настройка таймаутов. Убедитесь, что вы правильно установили таймауты для вашей сессии SFTP в
DefaultSftpSessionFactory
. Вы можете установить таймауты и значения keep-alive:
factory.setTimeout(300000); // Ожидание ответа от сервера (50000 мс)
factory.setSessionTimeout(600000); // Тайм-аут соединения
factory.setIdletimeout(300000); // Тайм-аут при неактивности
- Реализация механизма keep-alive. Реализуйте механизм регулярной отправки пустых запросов (или специальных команд) для поддержания соединения активным:
@Override
public void afterPropertiesSet() throws Exception {
keepAliveExecutor = Executors.newScheduledThreadPool(1);
keepAliveExecutor.scheduleAtFixedRate(() -> {
try {
if (sftpRemoteTemplate != null) {
sftpRemoteTemplate.execute(session -> {
session.getSession().sendKeepAlive(); // специальная команда для поддержания активности
return null;
});
}
} catch (Exception e) {
// Логирование ошибок
}
}, 0, 300, TimeUnit.SECONDS); // Отправка каждые 5 минут
}
- Закрытие сессии. Убедитесь, что вы правильно закрываете сессию после завершения работы, например, через интерфейс
Closeable
:
try (SftpRemoteTemplate template = new SftpRemoteTemplate(factory)) {
// Ваш код загрузки/обработки
} catch (Exception e) {
// Обработка исключений
}
Заключение
Исправление ошибки SSH2_DISCONNECT_PROTOCOL_ERROR
требует комплексного подхода к управлению подключениями SFTP, настройкам таймаутов и поддержанию активности соединений. Проверьте настройки вашего Spring Batch приложения, настройте таймауты, добавьте механизмы keep-alive и убедитесь в корректном закрытии сессий. Практическое применение этих рекомендаций должно помочь эффективно решить проблему, а также оптимизировать производительность вашей системы.
Современные IT-решения требуют пристального внимания к деталям, чтобы избежать сбоев и улучшить общий опыт использования.