Часто получаю ошибку сброса соединения с sftp-сервера, когда параллельные потоки пытаются поместить файл на sftp

#java #jsch #sshd

#java #jsch #sshd

Вопрос:

У меня есть фрагмент многопоточного кода, в котором 22 потока выполняются параллельно и пытаются поместить файлы на sftp-сервер.

Но я продолжаю периодически получать ошибку сброса соединения в своих журналах, и из-за этого некоторые записи терпят неудачу.

При первоначальном анализе я обнаружил, что размер sftp-сервера был t2.small, а загрузка процессора составляла 92%.

Учитывая это, по моей причине я сменил сервер на c5n.xlarge, теперь ошибка появляется реже, но, тем не менее, я получаю ее время от времени, даже когда максимальная загрузка процессора достигает 63%.

Я не могу найти ничего другого в журналах sftp-сервера в /var/log/ secure.

Ниже приведен фрагмент кода, используемый для размещения файла, каждый поток создает новый сеанс и закрывает его.

 JSch ssh = new JSch();
            // ssh.setKnownHosts("/path/of/known_hosts/file");
            java.util.Properties config = new java.util.Properties();
            config.put("StrictHostKeyChecking", "no");
            // Use key authentication if it is set, else use password auth
            if (mpServerDetails.get(SftpFile.SFTP_USERKEY) != null
                    amp;amp; mpServerDetails.get(SftpFile.SFTP_USERKEY) != "") {
                    File userKeyFile = new File(mpServerDetails.get(SftpFile.SFTP_USERKEY).toString());
                if (userKeyFile == null || !userKeyFile.exists()) {
                    throw new NonRetriableException(
                            "Key file "   mpServerDetails.get(SftpFile.SFTP_USERKEY).toString()   "not found.");
                }
                ssh.addIdentity(userKeyFile.getAbsolutePath());
                session = ssh.getSession(mpServerDetails.get(SftpFile.SFTP_USERNAME).toString(),
                        mpServerDetails.get(SftpFile.SFTP_HOSTNAME).toString());
            } else if (mpServerDetails.get(SftpFile.SFTP_PASSWORD) != null) {
                session = ssh.getSession(mpServerDetails.get(SftpFile.SFTP_USERNAME).toString(),
                        mpServerDetails.get(SftpFile.SFTP_HOSTNAME).toString());
                session.setPassword(mpServerDetails.get(SftpFile.SFTP_PASSWORD).toString());
            }
            session.setConfig(config);
            session.connect();
            if (session != null amp;amp; !session.isConnected()) {
                logger.warn("**session is not connected going to connect the sftp session ** {} ", session.getHost());
                session.connect();
            }
            channel = (ChannelSftp) session.openChannel("sftp");
            if (channel != null amp;amp; !channel.isConnected()) {
                logger.warn("**channel is not connected going to connect the sftp channel ** {} ",
                        channel.getSession().isConnected());
                channel.connect();
            }
            channel.put(file.getAbsolutePath(), dest.getConfig().get(TransporterFileConstants.SFTP_DIRECTORY).toString()
                      File.separatorChar   dest.getFileName(), new SystemOutProgressMonitor());

        }
        catch (NonRetriableException e) {
            throw new NonRetriableException(e);
        }
        catch (Exception e) {
            logger.error(
                    "Error occured while uploading file having name "   dest.getFileName()   " from remote directory:"
                              dest.getConfig().get(TransporterFileConstants.SFTP_DIRECTORY).toString(),
                    e);
            logger.error("SFTP Exception : ", e);
            throw new RetriableException(e);
        }
        finally {
            if (null != channel amp;amp; channel.isConnected()) {
                try {
                    channel.disconnect();
                }
                catch (Throwable e) {
                    logger.error("Error while disconnecting channel : ", e);
                }
            }
            if (null != session) {
                try {
                    session.disconnect();
                }
                catch (Throwable e) {
                    logger.error("Error while returning object to sftp pool : ", e);
                }
            }
        }
  

Может кто-нибудь помочь мне понять, почему я могу получать это исключение?

Конфигурации SFTP-сервера

 MaxSessions 50
Capacity - 25 GB
4 core server with 10 GB Ram
  

Фрагмент сообщения об ошибке

 com.jcraft.jsch.JSchException: Session.connect: java.net.SocketException: Connection reset
    at com.jcraft.jsch.Session.connect(Session.java:558) ~[honeybee-engine.jar:na]
  

Если это будет продолжаться, моя обработка данных не будет согласованной.

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

1. Вы открываете новое соединение для каждого отдельного файла?

2. Да, но также закрываю его.

3. И рассматривали ли вы возможность повторного использования соединения в каждом потоке?

4. На самом деле, это работает немного по-другому, есть различные исполнители, которые постоянно работают и выполняют различные задачи, и у нас есть эта задача, которая загружает файлы на sftp. Он может быть подобран любым потоком исполнителя. И эта задача использует службу загрузки sftp, которая создает новое соединение каждый раз, когда ее просят что-то поместить. Но одновременно эту задачу могут выполнять не более 22 исполнителей.

5. @MartinPrikryl Что я пытаюсь понять, так это то, что это проблема с моим кодом или какая-то проблема на стороне sftp-сервера, поскольку частота ошибок уменьшилась по мере увеличения размера сервера sftp, но все равно возникают ошибки при использовании 63%

Ответ №1:

 MaxSessions 50
  

Параметр SSH server MaxSessions ограничивает количество «сеансов», которые могут выполняться через одно SSH-соединение. Вы запускаете только один сеанс — сеанс SFTP — через каждое соединение, поэтому ограничение MaxSessions для вас не имеет особого значения.

Ваша проблема может быть связана с MaxStartups настройкой:

MaxStartups
Указывает максимальное количество одновременных не прошедших проверку подлинности подключений к SSH-демону. Дополнительные соединения будут удаляться до тех пор, пока аутентификация не завершится успешно или время входа в систему не истечет для соединения. По умолчанию 10:30:100….

В принципе, если к серверу подключено слишком много клиентов, которые еще не прошли проверку подлинности, сервер удалит некоторые из этих подключений. Если ваше приложение одновременно открывает слишком много подключений к серверу, сервер может отбрасывать некоторые из этих подключений. Решение здесь состоит в том, чтобы настроить значение MaxStartups или изменить ваше приложение, чтобы не открывать столько соединений одновременно.

Существует также ограничение операционной системы, называемое отставанием в прослушивании. В принципе, операционная система будет поддерживать только определенное количество ожидающих TCP-соединений. Если одновременно поступает достаточное количество попыток подключения, а процесс ssh-сервера недостаточно быстр при их принятии, ОС отбрасывает некоторые запросы на подключение. SSH-сервер запрашивает отставание в 128 подключений, но ОС может ограничить отставание меньшим значением. Если ваш SSH-сервер достаточно занят, вы можете столкнуться с этим ограничением.