Как улучшить производительность FtpWebRequest?

У меня есть приложение, написанное на.NET 3.5, которое использует FTP для загрузки / выгрузки файлов с сервера. Приложение работает нормально, но есть проблемы с производительностью:

  1. Подключение к FTP-серверу занимает много времени. FTP-сервер находится в другой сети и имеет Windows 2003 Server (IIS FTP). Когда несколько файлов ставятся в очередь для загрузки, переход из одного файла в другой создает новое соединение с использованием FTPWebRequest, и это занимает много времени (около 8-10 секунд).

  2. Возможно ли повторно использовать соединение? Я не очень уверен насчет свойства KeepAlive. Какие соединения поддерживаются и используются повторно.

  3. IIS-FTP в Windows Server 2003 не поддерживает SSL, поэтому любой может легко увидеть имя пользователя / пароль с помощью анализатора пакетов, такого как WireShark. Я обнаружил, что Windows Server 2008 поддерживает SSL через FTP в своей новой версии, если IIS 7.0.

Я хочу улучшить производительность загрузки / выгрузки моего приложения. Любые идеи будут оценены.

** Пожалуйста, обратите внимание, что 3 не проблема, но я хотел бы, чтобы у людей были комментарии по этому поводу

18 ответов

Я провел несколько экспериментов (загрузил около 20 файлов разных размеров) в FtpWebRequest со следующими факторами

KeepAlive = true / false

ftpRequest.KeepAlive = isKeepAlive;

Имя группы соединения = UserDefined или null

ftpRequest.ConnectionGroupName = "MyGroupName";

Предел соединения = 2 (по умолчанию) или 4 или 8

ftpRequest.ServicePoint.ConnectionLimit = ConnectionLimit;

Режим = Синхронный или Асинхронный

увидеть этот пример

Мои результаты:

  1. Использовать ConnectionGroupName,KeepAlive=true (21188,62 мс)

  2. Использовать ConnectionGroupName,KeepAlive=false заняло (53449,00 мс)

  3. Нет ConnectionGroupName,KeepAlive=false заняло (40335,17 мсек)

  4. Используйте ConnectionGroupName,KeepAlive=true;async=true, соединения = 2 заняло (11576,84 мс)

  5. Используйте ConnectionGroupName,KeepAlive=true;async=true, соединения =4 заняло (10572,56 мс)

  6. Используйте ConnectionGroupName,KeepAlive=true;async=true, соединения =8 заняло (10598,76 мс)

Выводы

  1. FtpWebRequest был разработан для поддержки внутреннего пула соединений. Чтобы пул соединений использовался, мы должны убедиться, что ConnectionGroupName устанавливается

  2. Установка соединения стоит дорого. Если мы подключаемся к одному и тому же ftp-серверу с использованием тех же учетных данных, то для флага keep alive, установленного в значение true, будет минимизировано количество подключений.

  3. Асинхронный - это рекомендуемый способ, если у вас много файлов для ftp.

  4. Количество подключений по умолчанию равно 2. В моей среде ограничение числа подключений в 4 даст мне наибольшее увеличение производительности. Увеличение количества соединений может или не может улучшить производительность. Я бы порекомендовал, чтобы у вас был лимит соединения в качестве параметра конфигурации, чтобы вы могли настроить этот параметр в вашей среде.

Надеюсь, вы найдете это полезным.

Неважно, если отдельные соединения занимают много времени, если вы можете запустить много параллельно. Если у вас есть много элементов для передачи (скажем, сотни), то имеет смысл запускать десятки и даже сотни веб- запросов параллельно, используя асинхронные методы, такие как BeginGetRequestStream и BeginGetResponse. Я работал над проектами, которые сталкивались с подобными проблемами (длительное время соединения / аутентификации), но, выпуская много вызовов параллельно, общая пропускная способность была действительно очень хорошей.

Также имеет огромное значение, если вы используете асинхронные или синхронные методы, как только у вас есть много (десятки, сотни) запросов. Это относится не только к вашим методам WebRequests, но и к вашим методам чтения / записи Stream, которые вы будете использовать после получения потока загрузки / выгрузки. Книга "Улучшение производительности и масштабируемости.Net" несколько устарела, но большая часть ее рекомендаций остается в силе, и ее можно свободно читать в Интернете.

Стоит учесть, что класс ServicePointManager скрывается в Framwework с единственной целью: подорвать вашу производительность. Убедитесь, что вы получили ServicePoint своего URL-адреса и изменили ConnectionLimit на разумное значение (по крайней мере, такое же, как количество одновременных запросов).

Сеть отладки

Несколько трюков для простой сетевой отладки:

  1. Проверьте время отклика, когда вы пропингуете FTP-сервер с сервера приложений.
  2. Проверьте время отклика для маршрута трассировки (tracert из оболочки DOS).
  3. Передайте файл из командной строки, используя ftp команда.
  4. Подключитесь к FTP-серверу через Telnet: telnet server 21,

Результаты дадут ключ к решению проблемы.

Сетевое оборудование

Для медленного трассировки маршрута:

  • Определите, почему на двух компьютерах возникают проблемы с сетью.
  • Обновите сетевое оборудование между самым медленным соединением.

конфигурация сети

Для медленного пинга:

  • Проверьте конфигурацию сети на каждой машине.
  • Убедитесь, что настройки являются оптимальными.

Проверить API

Медленный сеанс FTP из командной строки скажет вам, что проблема не изолирована от используемого вами API-интерфейса FTP. Это не исключает API как потенциальную проблему, но, безусловно, делает его менее вероятным.

Ошибки сети

Если пакеты отбрасываются между источником и адресатом, ping сообщит вам. Возможно, вам придется увеличить размер пакета до 1500 байт, чтобы увидеть любые ошибки.

FTP-сервер очередей

Если у вас нет контроля над целевым FTP-сервером, попросите промежуточный сервер получать загруженные файлы. Затем посредник отправляет файлы на удаленный сервер с любой возможной скоростью. Это создает иллюзию того, что файлы отправляются быстро. Однако если файлы должны существовать на удаленном сервере сразу после их загрузки, это решение может оказаться неэффективным.

Программное обеспечение FTP-сервера

Используйте другой FTP-демон на FTP-сервере, например ProFTPd в качестве службы Windows. (ProFTPd имеет плагины для различных баз данных, которые позволяют аутентификацию с использованием SQL-запросов.)

Операционная система FTP-сервера

Операционная система на основе Unix может быть лучшим вариантом, чем основанная на Microsoft.

Программное обеспечение FTP-клиента

Существует ряд различных API для отправки и получения файлов по FTP. Может потребоваться некоторая работа, чтобы сделать ваше приложение достаточно модульным, чтобы вы могли просто подключить новую службу передачи файлов. Несколько разных API указаны здесь как ответы.

Альтернативный протокол

Если FTP не является абсолютным требованием, попробуйте:

  • сетевой диск Windows
  • HTTPS
  • scp, rsync или аналогичные программы (может потребоваться Cygwin)

Эта ссылка описывает влияние ConnectionGroupName и KeepAlive: WebRequest ConnectionGroupName

Вы обязательно должны проверить BITS, который является большим улучшением по сравнению с FTP. Пароли в виде открытого текста - не единственная слабость FTP. Существует также проблема предсказания порта, который он откроет для пассивной загрузки или загрузки, и просто общая сложность, когда клиенты используют NAT или брандмауэры.

BITS работает через HTTP/HTTPS с использованием расширений IIS и поддерживает загрузку и загрузку по очереди, которые можно запланировать с низким приоритетом. В целом это намного более гибко, чем FTP, если вы используете Windows на клиенте и сервере.

БИТЫ для PowerShell

БИТЫ для.NET

Я настоятельно рекомендую Starksoft FTP/FTPS Component для.NET и Mono. У него есть объект подключения, который вы можете кэшировать и использовать повторно.

Лично я перенес все наши приложения с использования FTP для загрузки / выгрузки файлов и вместо этого развернул решение на основе веб-служб XML в ASP.NET.

Производительность значительно улучшена, безопасность настолько низка, насколько вы хотите кодировать (и вы можете использовать встроенные в.NET вещи), и все это может пройти через SSL без проблем.

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

Посмотрите на эту страницу - http://www.ietf.org/rfc/rfc959.txt

В нем говорится об использовании другого порта при подключении, чтобы иметь возможность повторно использовать подключение.
Это работает?

Чтобы решить проблему с производительностью, вам просто нужно установить:

ftpRequest.ConnectionGroupName = "MyGroupName"; ftpRequest.KeepAlive = false; ftpRequest.ServicePoint.CloseConnectionGroup("MyGroupName");

У меня были хорошие результаты с библиотекой ftp EDT:

http://www.enterprisedt.com/products/edtftpnet/overview.html

Я знаю, что это старая тема, но недавно мне пришлось пройти через аналогичную ситуацию.

Нам нужно было загрузить более 70 XML-файлов с ftp-сервера менее чем за 25 минут, не открывая более 5 соединений в течение этого периода времени.

Это были все альтернативы, которые мы попробовали:

  • wget - это было быстро, но каждый GET означал новое соединение. Нам пришлось остановиться из-за количества созданных соединений. У нас были некоторые проблемы с GETM, которые хорошо документированы в Интернете, поэтому это было невозможно.
  • FtpWebRequest - Каждый вызов Create будет регистрировать новое соединение, даже если мы использовали свойства KeepAlive и ConnectionGroupName. Плюс это было очень медленно.
  • Webclient - мы не проверяли, создавало ли оно новое соединение для каждого файла (хотя я предполагаю, что это так), но оно копировало 1 файл / минуту. Так что это был не вариант.

В итоге мы использовали старомодный пакетный скрипт ftp. Это быстро, и я использую только одно соединение, чтобы загрузить все файлы. Это не гибко, но гораздо быстрее, чем все остальное, что я пробовал (75 файлов менее чем за 20 минут).

Я бы порекомендовал перейти на rsync.
Плюсы:
Оптимизирован для сокращения времени передачи.
Поддерживает SSH для безопасной передачи
Использует TCP, что делает ваши ИТ-отделы / брандмауэры счастливее

Минусы:
Нет собственной поддержки.NET
Направлен на установку Linux-серверов - хотя есть и приличные порты Windows, такие как DeltaCopy

В целом, хотя это гораздо лучший выбор, чем FTP

Единый совет:

НИЖНИЙ БУФЕР / КУЛОЧНЫЕ РАЗМЕРЫ ЗНАЧИТЕЛЬНО СОКРАЩАЮТ РАБОТУ

Причина: еще много дискового ввода-вывода, памяти ввода-вывода, инициализация потока ftp и еще много других факторов

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

Примеры таких клиентов включают: http://www.codeproject.com/KB/IP/FtpClient.aspx который также содержит хорошее объяснение того, почему эти библиотеки могут работать быстрее, чем стандартный FtpWebRequest и http://www.codeproject.com/KB/macros/ftp_class_library.aspx который также выглядит как достаточно простая реализация.

Лично я откатил свою собственную реализацию FTP назад в.NET 1.1 за несколько дней до введения FtpWebRequest, и это все еще хорошо работает для.NET 2.0 и выше.

Я сделал несколько тестов для FtpWebRequest, аналогично ответу @Sid выше. Установка KeepAlive в значение true улучшает, но не асинхронные вызовы в моем тесте. Тест состоит из

1) FtpWebRequest для проверки существования файла 2) FtpWebRequest для загрузки 3) FtpWebRequest для переименования файла на сервере

Test FTP client 30 files of size 5 Kbytes took ... 14.897 seconds Test upload (alive true, connection name) 30 files of size 5 Kbytes took ... 34.229 seconds Test async(alive true, connection name) 30 files of size 5 Kbytes took ... 40.659 seconds Test send thread (alive true, connection name) 30 files of size 5 Kbytes took ... 38.926 seconds, 30 files

что улучшилось, так это реализация FTP-клиента, сделанного с использованием класса Socket

эталон здесь

https://github.com/pedro-vicente/ftp_t

KeepAlive работает. FtpWebRequest кэширует соединения внутри, так что они могут быть повторно использованы через некоторое время. Для получения подробной информации и объяснения этого механизма вы можете обратиться к ServicePoint.

Другим хорошим источником информации является поиск в источнике FtpWebRequest (вы можете сделать это на VS2008).

Попробуйте этот код ниже, вы получите лучшую производительность:

private void Upload144_Click(object sender, EventArgs e)
{
    OpenFileDialog fileobj = new OpenFileDialog();
    fileobj.InitialDirectory = "C:\\";
    //fileobj.Filter = "Video files (*.mp4)";
    //fileobj.ShowDialog();

    if (fileobj.ShowDialog() == DialogResult.OK)
    {
        if (fileobj.CheckFileExists)
        {
            string test = Properties.Settings.Default.Connection;
            SqlConnection con = new SqlConnection(test);
            con.Open();
            string correctfilename = System.IO.Path.GetFileName(fileobj.FileName);
            SqlCommand cmd = new SqlCommand("Insert into Path(ID,Path5) VALUES   ((select isnull(MAX(id),0) + 1 from Path),'\\Videos\\" + correctfilename + "')", con);

            cmd.ExecuteNonQuery();

            string path = Application.StartupPath.Substring(0, Application.StartupPath.Length - 10);
            con.Close();

            //For Progressbar
            DataTable dt = new DataTable();
       //   SqlDataAdapter da = new SqlDataAdapter(cmd);
       //   da.Fill(dt);

            timer5.Enabled = true;

            // FOR FtpServer File Upload::
            string uploadfile = fileobj.FileName;
            string uploadFileName = new FileInfo(uploadfile).Name;

            string uploadUrl = "ftp://ftp.infotech.com/";
            FileStream fs = new FileStream(uploadfile, FileMode.Open, FileAccess.Read);
            try
            {
                long FileSize = new FileInfo(uploadfile).Length; // File size of file being uploaded.
                Byte[] buffer = new Byte[FileSize];

                fs.Read(buffer, 0, buffer.Length);

                fs.Close();
                fs = null;
                string ftpUrl = string.Format("{0}/{1}", uploadUrl, uploadFileName);
                FtpWebRequest requestObj = FtpWebRequest.Create(ftpUrl) as FtpWebRequest;
                requestObj.Method = WebRequestMethods.Ftp.UploadFile;
                requestObj.Credentials = new NetworkCredential("test@sample.com", "test@123");
                Stream requestStream = requestObj.GetRequestStream();
                requestStream.Write(buffer, 0, buffer.Length);

                requestStream.Flush();
                requestObj = null;
            }
            catch (Exception ex)
            {
                //MessageBox.Show("File upload/transfer Failed.\r\nError Message:\r\n" + ex.Message, "Succeeded", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }
    }
}

Я работал с этим несколько дней... и скорость была действительно низкой, не с чем сравнивать с FileZilla... наконец, я решил с многопоточностью. 10 потоков, создающих соединения для загрузки, дают мне хорошую скорость, может быть, даже могут быть улучшены еще... с помощью стандартной конфигурации ftprequest

PeticionFTP.ConnectionGroupName = "MyGroupName"
PeticionFTP.ServicePoint.ConnectionLimit = 4
PeticionFTP.ServicePoint.CloseConnectionGroup("MyGroupName")

PeticionFTP.KeepAlive = False 
PeticionFTP.UsePassive = False

PeticionFTP.UseBinary = True

PeticionFTP.Credentials = New NetworkCredential(lHost.User, lHost.Password)
Другие вопросы по тегам