Məzmuna keç
Tez Base

Обмен 1С через FTP рвётся на большом пакете: почему он не докачивается и как сделать, чтобы докачивался

· Dərc edilib:

Интеграции и обмены

Типичная картина: у филиала или склада плохой интернет, обмен с центральной базой идёт через FTP, пакет РИБ или обмена БСП весит несколько сотен мегабайт. Связь моргает, и следующая попытка снова везёт пакет с самого начала. Если обрывы случаются чаще, чем пакет успевает доехать, обмен не проходит вообще. Бывает и хуже: пакет доехал обрезанным, и это всплывает только на загрузке, ошибкой в журнале регистрации, которую никто не связывает с транспортом.

Обе беды растут из одного места. Штатный транспорт отправляет файл одним вызовом и сразу под финальным именем, платформа не умеет дописывать файл на FTP со смещения, а приёмная сторона проверяет только, что файл не пустой. Ниже разбор по порядку: как убедиться, что у вас именно это, какие есть выходы и как устроена доставка частями, которую мы сделали и проверили с настоящими обрывами.

Стенд для всех замеров: платформа 8.3.27.1606, УТ 11.5 с БСП 3.1.11, локальный FTP-сервер pyftpdlib. Мегабайт здесь двоичный, 1 048 576 байт.

Признаки, что виноват транспорт

  • Обмен с одним узлом стабильно не доходит, а с соседями по хорошему каналу идёт.
  • В журнале отправки ошибки вида “Ошибка подключения к FTP-серверу”, и каждая следующая попытка длится столько же, сколько первая: прогресса между попытками нет.
  • На FTP в каталоге обмена лежит файл с именем пакета, но меньше, чем у отправителя.
  • Загрузка падает на разборе XML или на распаковке архива, хотя выгрузка прошла чисто.
  • В своём коде обмена вы ставили FTP-соединению таймаут “чтобы не висело”, и большие пакеты перестали уходить.

Последний пункт стоит разобрать отдельно, потому что он не про обрывы.

Таймаут FTPСоединения считается на всю операцию

Параметр Таймаут в конструкторе FTPСоединение ограничивает операцию целиком, а не время простоя. Проверка: таймаут 3 секунды, файл, который на ограниченном канале идёт около десяти секунд. Соединение рвётся ровно на третьей секунде с “Превышен таймаут”, хотя данные всё это время шли. В штатном транспорте БСП на передачу стоит 12 часов. Если в самописном обмене вы поставили минуту, крупный пакет на медленном канале не пройдёт никогда, и выглядеть это будет как обрыв связи.

Что делает штатный FTP-транспорт БСП

Код обработки ТранспортСообщенийОбменаFTP и общего модуля ТранспортСообщенийОбмена открыт (БСП 3.1.11, лицензия CC BY 4.0). Отправка пакета там такая:

// БСП 3.1.11, Обработка.ТранспортСообщенийОбменаFTP, ВыполнитьКопированиеФайлаНаFTPСервер
Попытка
	FTPСоединение.Записать(ИмяФайлаИсточника, КаталогНаСервере + ИмяФайлаПриемника);
Исключение
	СообщениеОбОшибке = НСтр("ru = 'Ошибка подключения к FTP-серверу, ...'");
	// ...
	Возврат Ложь;
КонецПопытки;

Из этого фрагмента следует всё остальное:

Свойство транспортаСледствие
один вызов Записать на весь файлпосле ошибки следующая попытка повторяет то же самое с нуля
запись сразу в финальное имяпосле обрыва на сервере остаётся обрезанный файл под именем настоящего пакета
приём берёт самый свежий файл по шаблону имени и пропускает только пустыеогрызок с ненулевым размером и свежей датой уходит в загрузку как целый
контрольной суммы нетнесжатый обрезанный XML не отличить от целого до загрузки; сжатый не распакуется, и это случайная защита

Реквизит МаксимальныйДопустимыйРазмерСообщения пакет на части не режет, на него иногда надеются зря. Это ограничение: файл крупнее отправка отвергает с текстом “Превышен допустимый размер сообщения обмена”.

На приёме проверка одна:

// там же, ПолучитьСообщение
ИначеЕсли (ТекущийФайл.Размер() = 0) Тогда
	Продолжить;

Почему не дописать файл с места обрыва

Справка не показывает у Записать и Получить параметра смещения. Мы проверили это вызовом, а не чтением справки:

Попытка
	Выполнить("С.Записать(Ф, ""/proba2.txt"", 100)");
Исключение
	// "Слишком много фактических параметров"
КонецПопытки;

На 8.3.27 оба метода отвечают “Слишком много фактических параметров”. Третьего параметра нет, дописать существующий файл на FTP средствами платформы нечем. Следом мы отправили пакет одним Записать, как это делает БСП, и посреди передачи убили процесс FTP-сервера. Платформа выдала исключение “Failed sending data to the peer”, а на сервере под именем пакета остался файл на 46 % от исходного.

Какие есть выходы

ВариантЧто даётЧего не даёт
Включить сжатие в настройке транспортапакет меньше, обрезанный архив не распакуетсяпосле обрыва всё равно с нуля, огрызок лежит под настоящим именем
Уменьшить пакет (чаще выгружать, дробить по времени)меньше потеря на обрывничего не меняет в механике, большой первичный пакет всё равно не пройдёт
Внешняя утилита с докачкойдокачка с байтаэто уже не 1С: установка на каждом сервере, свой мониторинг
Свой транспорт в конфигурацииполный контрольв БСП 3.1.11 список транспортов зашит в ТранспортСообщенийОбмена и переопределяемую процедуру, нужна правка конфигурации
Доставка частями внешней обработкой между каталогамидокачка с первой недоехавшей части, проверка SHA-256 до загрузки, конфигурация не меняетсяобрыв стоит одной части, а не нуля байт

Мы выбрали последний вариант. Дальше о том, как он устроен.

Доставка частями: требования

Раз писать со смещения нельзя, единицей докачки становится файл. Пакет режется на части, каждая едет отдельным файлом и либо дошла целиком, либо повторяется. Чтобы это работало надёжно, нужно закрыть пять вопросов.

1. Как понять, что часть доехала. Размер файла на сервере не годится: у огрызка размер тоже есть. Поэтому часть пишется во временное имя и переименовывается в финальное только после того, как Записать вернулась без ошибки:

ИмяВременного = ПолучитьИмяВременногоФайла("part");
Данные.Записать(ИмяВременного);
ПутьЧасти = ПутьНаСервере(Пакет.УдалённыйКаталог, ИмяЧасти(Номер));
Попытка
	Соединение.Записать(ИмяВременного, ПутьЧасти + ".tmp");
Исключение
	УдалитьФайлы(ИмяВременного);
	ВызватьИсключение;
КонецПопытки;
УдалитьФайлы(ИмяВременного);
Соединение.Переместить(ПутьЧасти + ".tmp", ПутьЧасти);

Связь упала между записью и переименованием - на сервере остался .tmp, часть уйдёт заново. Потеря та же, одна часть.

2. Как резать, не удваивая пакет на диске. У платформы есть РазделитьФайл, она быстрая, но кладёт рядом вторую копию пакета. Отправителю эта копия не нужна, часть читается из исходного файла на лету:

Поток = ФайловыеПотоки.ОткрытьДляЧтения(ПолноеИмя);
Поток.Перейти(Смещение, ПозицияВПотоке.Начало);
Буфер = Новый БуферДвоичныхДанных(Длина);
Прочитано = Поток.Прочитать(Буфер, 0, Длина);
Поток.Закрыть();
Данные = ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных(Буфер);

3. Как приёмной стороне понять, что пакет готов. У каждого пакета на FTP своя папка:

/obmen/Message_A_B.xml.parts/opis.txt     размер, SHA-256 пакета и каждой части (уходит первой)
/obmen/Message_A_B.xml.parts/0001.part    части; пишутся в NNNN.part.tmp, потом переименовываются
/obmen/Message_A_B.xml.parts/gotovo.txt   SHA-256 пакета (уходит последним)

Пока нет gotovo.txt, получатель пакет не трогает и сообщает, сколько частей из скольких уже на сервере. После обрыва отправитель читает опись, убеждается по SHA-256, что она от того же пакета, и шлёт только недостающие части. Если там опись другого пакета, значит обмен успел выгрузить новый, и старые части удаляются.

Хеши пакета и частей считаются за один проход объектом ХешированиеДанных. SHA-256 для 600 МБ через ДобавитьФайл занимает около пяти секунд, результат совпал до символа с независимым расчётом в Python. CRC32 втрое быстрее, но 32 бита на файл в сотни мегабайт мы сочли слабой защитой.

4. Как не отдать загрузке битый пакет. Получатель качает части в рабочую папку и у каждой сверяет SHA-256. Не совпало - качает повторно. Совпало со второго раза, значит сбоила сеть. Не совпало дважды, значит часть испорчена на сервере, и её надо перепослать:

Совпал = Ложь;
Для Заход = 1 По 2 Цикл
	УдалитьФайлы(ИмяВременного);
	Соединение.Получить(ПутьЧасти, ИмяВременного);
	Если ХешФайла(ИмяВременного) = Ожидаемый Тогда
		Совпал = Истина;
		Прервать;
	КонецЕсли;
КонецЦикла;
Если Не Совпал Тогда
	// битую часть убираем с сервера вместе с отметкой готовности:
	// следующая отправка дошлёт только её
	УдалитьНаСервере(Соединение, ПутьЧасти);
	УдалитьНаСервере(Соединение, ПутьНаСервере(Пакет.УдалённыйКаталог, "gotovo.txt"));
КонецЕсли;

Собранный через ОбъединитьФайлы пакет проверяется ещё раз целиком и только при совпадении переносится в каталог обмена. Рабочая папка лежит внутри этого каталога, а штатный транспорт “каталог” ищет файлы без подпапок (НайтиФайлы(КаталогОбменаИнформацией, ШаблонИмениСообщения, Ложь)), поэтому части и полусобранный файл загрузка не видит. Перенос готового пакета это переименование в пределах одного диска. Места на время сборки нужно около двух размеров пакета.

5. Как не запустить два раза. Отправка стоит по расписанию, администратор нажимает кнопку руками, потому что “что-то долго”. Чтобы две копии не слали одни и те же части, в рабочей папке лежит файл-замок, отдельный для отправки и приёма, и каждый шаг обновляет его время. Вторая копия видит свежий замок и уходит, ничего не трогая. Замок старше 15 минут считается брошенным (например, форму закрыли посреди передачи), следующий запуск его перехватывает и продолжает с первой недоехавшей части. 15 минут взяты с запасом больше таймаута одной FTP-операции.

Какого размера часть

Каждая часть стоит нескольких обменов командами с сервером: подключение, вход, пассивный канал, переименование. На петле 127.0.0.1 это около 25 мс на часть независимо от размера, нижняя граница. На живом канале с пингом 150 мс шесть-семь таких обменов дадут около секунды на часть; это оценка, на живом канале мы её не мерили.

Мелкие части проигрывают на накладных, крупные дорого терять: в среднем обрыв съедает половину части. Минимум суммы двух потерь даёт формулу

оптимальный размер части = корень(2 x размер пакета x накладные на часть x скорость канала / число обрывов)

Для пакета в гигабайт, секунды накладных и пяти обрывов это около 7 МБ на канале 1 Мбит/с и около 23 МБ на 10 Мбит/с. Плохой канал обычно и медленный, поэтому взяли 8 МБ: на 1 Мбит/с такая часть идёт минуту с небольшим, это худшее, что теряется при обрыве. Размер записывается в опись, получатель берёт его оттуда. Граница у такого выбора есть: таймаут операции 10 минут, значит на канале медленнее 14 КБ/с часть в 8 МБ не пройдёт.

Как это встаёт в обмен

Свой транспорт в БСП подключается только правкой конфигурации, поэтому обработка работает мостом между двумя каталогами. В узле обмена ставится штатный транспорт “Локальный или сетевой каталог”. У отправителя обработка забирает пакет из этого каталога и везёт на FTP, у получателя забирает с FTP и кладёт в такой же каталог, откуда пакет берёт штатная загрузка. От версии БСП это почти не зависит.

Входящий пакет от исходящего в одном каталоге отличается по имени. БСП называет пакет Message_<отправитель>_<получатель>: если мы узел А, наш исходящий называется Message_А_Б, входящий Message_Б_А (перед кодом иногда стоит ещё префикс базы). Код своего узла обработка берёт из ЭтотУзел() планов обмена, в форме его можно перебить.

Для расписания есть серверная команда дополнительной обработки БСП “Отправить и получить пакеты обмена через FTP”: сначала отправка, потом приём. Вызовом из кода мы прогнали её в обе стороны, через справочник дополнительных обработок с расписанием пока нет.

Старые конфигурации

FTP-обменом чаще пользуются базы, которые годами не обновлялись: платформа свежая, а режим совместимости 8.2.16. В этом режиме закрыто всё, что появилось в 8.3.6: СтрНайти с направлением поиска, СтрРазделить, СтрСоединить, СтрЗаканчиваетсяНа, НаправлениеПоиска. Первая сборка обработки в такой базе даже не создавалась:

Ошибка инициализации модуля: ВнешняяОбработка.FTPДокачкаПакетовОбмена.МодульОбъекта
{...МодульОбъекта(213,34)}: Переменная не определена (НаправлениеПоиска)

Конфигуратор в той же базе на /CheckModules отвечал “Синтаксических ошибок не обнаружено”, ошибка ловится только созданием обработки. Функции заменены своими на Найти, Лев и Сред:

Функция ПозицияПоследнего(Знач Строка1, Знач Подстрока)
	Итог = 0;
	Позиция = Найти(Строка1, Подстрока);
	Сдвиг = 0;
	Пока Позиция > 0 Цикл
		Итог = Сдвиг + Позиция;
		Сдвиг = Итог + СтрДлина(Подстрока) - 1;
		Позиция = Найти(Сред(Строка1, Сдвиг + 1), Подстрока);
	КонецЦикла;
	Возврат Итог;
КонецФункции

После правки обработка создаётся, полная отправка и приём проходят, хеш доставленного пакета совпадает во временных базах с режимами 8.2.16, 8.3.5 и 8.3.10. Сборка до правки при 8.2.16 и 8.3.5 падала. Платформа нужна от 8.3.20.

Три прогона с настоящим обрывом

Пакет настоящий: сообщение обмена, выгруженное ЗаписьСообщенияОбмена по служебному плану обмена БСП, около 60 тысяч элементов “Номенклатуры”, 632 988 913 байт без сжатия, то есть 603,7 МБ и 76 частей (75 по 8 МБ и одна на 3,7 МБ). Обрыв делали убийством процесса FTP-сервера, канал ограничивали до 4 МБ/с, чтобы попадать в середину части.

ПрогонЧто сделалиЧто получилось
Обрыв отправкисервер убит на 16-й части из 76на сервере 15 готовых частей (120 МБ за 32 секунды) и недописанный 0016.part.tmp. Вторая попытка прочитала опись и довезла 61 часть, 483,7 МБ за 2 минуты 7 секунд. Повторно не ушло ничего: 15 + 61 = 76
Обрыв приёмасервер убит на 20-й части приёмау получателя 19 проверенных частей, в каталоге обмена пакета нет. Вторая попытка скачала оставшиеся 57
Порча частив части 0040 на сервере заменили 16 байт, размер тот жена 40-й части хеш не совпал дважды, итог “Пакет НЕ положен в каталог обмена: … часть 40 из 76 битая на сервере”. Следующая отправка дослала одну часть, 8 МБ за доли секунды, второй приём скачал части с 40-й по 76-ю и положил пакет с верным хешем
Чистая передача и загрузкабез обрывов и без ограничения каналаушёл за 7 секунд, пришёл за 12 (петля). Загружен в узле-получателе через ЧтениеСообщенияОбмена и ПрочитатьXML с ОбменДанными.Загрузка = Истина: все объекты записаны, ошибок ноль, номер принятого сообщения равен номеру пакета

Без докачки вторая попытка в первом прогоне повезла бы все 603,7 МБ. Целый пакет на этом канале идёт около двух с половиной минут, и при обрывах каждые полминуты он не дошёл бы никогда. SHA-256 сверяли независимо от 1С, через crypto в Node.js: у отправителя, в склейке частей на FTP и в каталоге получателя.

Полноценный узел синхронизации БСП между двумя базами в этих прогонах не настраивался. Утверждаем ровно то, что проверили: доставленный пакет платформа прочитала как сообщение обмена и записала целиком. FTPS тоже не заявляем: на стенде с TLS передача зависала на листинге каталога.

Обработка

Всё описанное собрано в обработку “FTP с докачкой для пакетов обмена”, на Инфостарте она стоит 10 SM, код открыт, конфигурацию не меняет. Сверху строка итога по цвету: идёт, доставлено с совпавшим хешем, обрыв с номером части, с которой продолжим, или отказ класть пакет в каталог с причиной. Журнал хранит все попытки по каждому пакету и отдельной строкой число частей, ушедших повторно; выгрузка в Markdown собирает журнал и диагноз обрывов для администратора канала, без логина и пароля FTP. Подробнее на странице инструмента.

Если пакет доходит, но объекты в нём не те или их нет, транспорт ни при чём, смотрите регистрацию изменений в плане обмена. Обмен с внешним сервисом, который отваливается по времени, разобран отдельно: таймаут обмена 1С с внешним сервисом.

Настроить обмен между центральной базой и филиалами на слабых каналах, от плана обмена до доставки и контроля, мы можем под ключ: обмен данными 1С.

Bunu sizin əvəzinizə həll edə bilərəm

1C-i hər şeylə əlaqələndiririk: digər 1C bazaları, saytlar, CRM, banklar, dövlət sistemləri. "Qopan" və növbə yığan mübadilələri təmir edirik.

Qiyməti
450 000 ₸-dənmübadilə altsisteminin tam tətbiqi; vergilərsiz, nöqtəvi tapşırıqlar - daha ucuz

Yazmaq tezdir? Özünüz ölçün

Обмен через FTP с докачкой: пакет продолжает с места обрыва

Emalı aç

INFOSTART TECH EVENT 2026: конференция для 1с-специалистов. если собираетесь, регистрируйтесь по нашей ссылке. Регистрация →

Bunu da oxuyun

Отчёт в хранилище не сходится с 1С: какие правки теряет выгрузка и как сверять по дням

Ночная выгрузка из 1С во внешнюю базу зелёная, а остаток за март в отчёте не тот. Почти всегда это правка задним числом, до которой не дотянулось окно выгрузки. Разбираем по типам правок, какая проверка что видит: число строк, паспорт дня, версии документов, сверка самого приёмника. С запросом 1С, SQL для приёмника на MS SQL и замерами на 10 миллионах строк.

Число в строку в 1С: неразрывный пробел в ключах и как его вычистить

Строка() форматирует число для человека и вставляет между разрядами символ с кодом 160. Когда такая строка уходит в ключ сопоставления, совпадения пропадают без единой ошибки. Здесь таблица поведения Строка() и Формат(), прогнанная на платформе 8.3.27, запрос для поиска испорченных значений, проверка по кодам символов, ловушка с нулём в Формат() и порядок правки, при котором не рвутся уже работающие связи.

Регламентное задание 1С обрабатывает не все записи и не выдаёт ошибок: проверка и исправление цикла

Задание прошло, журнал чистый, счётчик обработанных сходится, а утром часть записей по-прежнему ждёт обработки. Частая причина: постраничный обход со смещением по набору, который сам сжимается от обработки. Как подтвердить это холостым прогоном, сколько теряется при разных размерах страницы, как переписать цикл и чем платить за исправление.