Асинхронная концепция программирования в «1С:Предприятие 8» или выполнение программного кода «в фоне»




Принцип обмена данными из 1С с сайтом (на MySQL) и выдачи (публикации) этих данных по запросу.
PHP-Скрипт автоматической загрузки данных из файла данных в формате CSV в базу данных сайта работающего на WordPress.

В продолжение моей темы: 1С:Альфа-Авто Автосалон Автосервис: обмен с сайтом.
С помощью данного скрипта можно загружать в автоматическом режиме, по расписанию, данные сервисных книжек (ремонтов авто) из 1С:Альфа-Авто Автосалон Автосервис.
Также можно загружать данные в ручном режиме: для этого делается скрытая страница, где размещается специальная кнопка.
Комментарии размещенные внутри скрипта разъяснят логику и порядок действия.
Комментарии с "/////    echo" использовались для отладки.
Дополнительно создана таблица для журналирования результатов загрузки данных.
Скрипт включает в себя защиту от SQL инъекций (думаю безопасность соблюдена в полной мере).
В кратце:
1. Пишется скрипт, который запускает этот.
2. Создается регламентное задание в WordPress, по которому запускается скрипт из п.1. 
3. Этот скрипт осуществляет проверку на существование файла обмена в папке.
4. Если данные не новые, загрузка не производится.
5. Если данные новые, очищается таблица сервисных книжек.
6. Загружаются новые данные.

Собственно сам скрипт:

<?php // Полная загрузка сервисных книжек, создан 2026-01-05 12:44:55

global $wpdb2;
global $failure;
global $file_hist;

/////  echo '<H2><b>Старт загрузки</b></H2><br>';

$failure=FALSE;
//подключаемся к базе
$wpdb2 = include_once 'connection.php'; ; // подключаемся к MySQL
// если не удалось подключиться, и нужно оборвать PHP с сообщением об этой ошибке
if (!empty($wpdb2->error))
{
/////   echo '<H2><b>Ошибка подключения к БД, завершение.</b></H2><br>';
$failure=TRUE;
wp_die( $wpdb2->error );
}

$m_size_file=0;
$m_mtime_file=0;
$m_comment='';
/////проверка существования файлов выгрузки из 1С
////файл выгрузки сервисных книжек
$file_hist = ABSPATH.'/_1c_alfa_exchange/AA_hist.csv';
if (!file_exists($file_hist))
{
/////   echo '<H2><b>Файл обмена с сервисными книжками не существует.</b></H2><br>';
$m_comment='Файл обмена с сервисными книжками не существует';
$failure=TRUE;
}

/////инициируем таблицу лога
/////если не существует файла то возврат и ничего не делаем
if ($failure){
///включает защиту от SQL инъекций и данные можно передавать как есть, например: $_GET['foo']
/////   echo '<H2><b>Попытка вставить запись в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>$m_comment));
wp_die();
/////    echo '<H2><b>Возврат в начало.</b></H2><br>';
return $failure;
}
/////проверка лога загрузки, что бы не загружать тоже самое
$masiv_data_file=stat($file_hist);   ////передаем в массив свойство файла
$m_size_file=$masiv_data_file[7];    ////получаем размер файла
$m_mtime_file=$masiv_data_file[9];   ////получаем дату модификации файла
////создаем запрос на получение последней удачной загрузки
////выбираем по штампу времени создания (редактирования) файла загрузки AA_hist.csv, $m_mtime_file

/////   echo '<H2><b>Размер файла: '.$m_size_file.'</b></H2><br>';
/////   echo '<H2><b>Штамп времени файла: '.$m_mtime_file.'</b></H2><br>';
/////   echo '<H2><b>Формирование запроса на выборку из лога</b></H2><br>';
////препарируем запрос
$text_zaprosa=$wpdb2->prepare("SELECT * FROM `vin_logs` WHERE `last_mtime_upload` = %s", $m_mtime_file);
$results=$wpdb2->get_results($text_zaprosa);

if ($results)
{   foreach ( $results as $r)
{
////если штамп времени и размер файла совпадают, возврат
if (($r->last_mtime_upload==$m_mtime_file) && ($r->last_size_upload==$m_size_file))
{////echo '<H2><b>Возврат в начало, т.к. найдена запись в логе.</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>'Загрузка отменена, новых данных нет, т.к. найдена запись в логе.'));
wp_die();
return $failure;
}
}
}
////если данные новые, пишем в лог запись о начале загрузки
/////echo '<H2><b>Попытка вставить запись о начале загрузки в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>0, 'last_size_upload'=>$m_size_file, 'comment'=>'Начало загрузки'));

////очищаем таблицу
$clear_tbl_zap=$wpdb2->prepare("TRUNCATE TABLE %s", 'vin_history');
$clear_tbl_zap_repl=str_replace("'","`",$clear_tbl_zap);
$results=$wpdb2->query($clear_tbl_zap_repl);
/////   echo '<H2><b>Очистка таблицы сервисных книжек</b></H2><br>';
if (empty($results))
{
/////   echo '<H2><b>Ошибка очистки таблицы книжек, завершение.</b></H2><br>';
//// если очистка не удалась, возврат
$failure=TRUE;
wp_die();
return $failure;
}

////загружаем данные
$table='vin_history';         // Имя таблицы для импорта
//$file_hist Имя CSV файла, откуда берется информация     // (путь от корня web-сервера)
$delim=';';          // Разделитель полей в CSV файле
$enclosed='"';      // Кавычки для содержимого полей
$escaped='\

38 Comments

  1. recon

    Отличная статья! Но у фоновых заданий есть и подводные камни, связанные с ошибками (или фичами?) платформы. В частности 1С не контролирует расход процессорного времени при выполнении фонового задания и десяток фоновых заданий, выполняющих какой то «тяжелый» расчет, могут запросто положить сервер на лопатки. Выход — перенос менеджера фоновых заданий на другой сервер (доступно с платформы 8.3), а значит фоновые задания будут выполняться на выделенном сервере. Возможно в новых релизах что то поменялось

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

    Reply
  2. Infactum

    Конечно на безрыбье и рак рыба, но называть это все асинхронным программированием будет не совсем корректно. Для реализации полноценного асинхронного программирования нужна либо нормальная работа с обратными вызовами, либо Promise/Deferred. Очень рекомендую по смотреть в сторону node.js + callback/promise или C# + async/await, чтобы понять, как именно все это должно работать. По факту сейчас за 1С просто стыдно 🙁

    Reply
  3. pumbaE

    (2) Ну так используйте Показатьоповещение, бесконечный loop и подключитьобработчик ожидания. Превращать в callback ад, код ради «стыдно» не имеет смысла.

    В том же питоне, попытались не реализовывать сразу весь ужас callback и не превращать в код в спагети.

    Reply
  4. Infactum

    (3) pumbaE, в питоне есть asyncio. Я на callback модели и не настаивал, специально привел несколько примеров.

    И к слову о JS и «callback hell» — это проблема исключительно начинающих программистов. Использование библиотек типа node-async или отказ от обратных вызовов в пользу обещаний решает все это на корню.

    Reply
  5. pumbaE

    (4) в типовой поставке 1с нет ни тестирования, ни документации кода, как получить работающий код при малейшем изменении одного из обещаний?

    Уже сейчас можно с трудом отладить модель подписок в БСП, пока инструментов нет для нормальной поддержки кода.

    Reply
  6. klinval
    &НаСервере
    Процедура ВыполнитьДлительнуюОперациюАсинхронноНаСервере()
    
    Параметры = Новый Массив;
    Параметры.Добавить();
    
    ФоновыеЗадания.Выполнить(«АсинхронныеОбработчики.НашаДлительнаяОперация»,
    Параметры, Новый УникальныйИдентификатор, «Пример асинхронной концепции программирования»);
    
    КонецПроцедуры

    Показать

    Вы случайно не забыли в параметрах передать длительность?

    Reply
  7. AXIOMLAB24

    (6) klinval, Спасибо! Подправил.

    Reply
  8. Rustig

    (0) проект интересный!

    ….а как мы жили раньше без этого?!…..

    я учил бухгалтеров так: запускаете один сеанс бухгалтерии, запускаете формирование ОСВ,

    открываете второй сеанс бухгалтерии и продолжаете работу во втором сеансе….

    задачи другого рода, когда бухгалтер нажимает на кнопку, и следующий процесс чересчур длительный я решал таким образом: например отправку счета на оплату в БП 1.6 — создал регистр сведений «Очередь отправки», куда записывалось электронное письмо. В параллельном сеансе под пользователем «Электронка» все собранные письма начинали отправляться по очереди, не нагружая сеанс 1С у бухгалтера…

    Reply
  9. nSpirit2

    Много много букав совсем не о чем 🙂

    К сожалению пока вся концепция 1с завязана на том что клиентские приложение само отслеживает выполнилось фоновое задание или нет.

    Обилие описания плюсов о том что пользовательский интерфейс не блокируется по большей части преувеличение удобности этого для пользователя. Мы не завешиваем окно пользователя но не факт ему нужно работать пока не закончиться длительная операция. Отслеживание работы пользователя по большей части показывает, что выполнение отчета в фоне не как не помогает пользователю работать так как он ожидает результат. Для моей базы это примерно 70 процентов. Так что речь о оставлении возможности интерактивной работы пользователя по большей части надумана. Так как любая длительная операция которая необходима пользователю по большей части не требует не блокировки интерфейса это скорее удобность работы так как пользователи по большей части не многозначны и ожидают результата своих действий перед выполнением следующей задачи. Основная удобность всех фоновых процессов к сожалению сводиться к возможности незаметно для пользователя обновлять часть видимой ему информации. Либо перенос в них задач результат выполнения которых пользователь не ожидает.

    Что относительно описания в БСП. Я думал будет больше функции из модулей <color #FF6347>ДлительныеОперации</>*.

    Такие функции как СообщитьПрогресс и ПрочитатьПрогресс к сожалению не нашли отражения в примере хотя именно для длительных операций они весьма интересны. Что интересно. C помощью него можно выполнять не только экспотрные процедуры но и команды отчетов и обработок что значительно удобней чем строковых адресов в функции.

    Простите но статья по большей части не о чем. Ждем детального продолжения.

    (5) pumbaE, То что вы по прежнему кушайте кактус совсем не значит что другие тоже его едят. Есть и инструменты для тестирования и инструменты для документирования. А относительно спагети. Вы пробуйте написать паралельно вычисление на фоновых заданиях вам callback счастьем покажется.

    (4) Infactum, К слову а JS в 1с со всей этой концепцией фоновых вычислений по большей части не хватает возможности помещения в переменные ссылок на функции для дальнейшего их использования.

    Reply
  10. pumbaE
    Есть и инструменты для тестирования и инструменты для документирования

    поделитесь хотя-бы названиями.

    Reply
  11. nSpirit2

    (10) pumbaE,

    https://github.com/xDrivenDevelopment/xUnitFor1C ну для тестирования вот вам ссылка. Посмотрите) А то куда ведет ссылка ответ на вопрос про документацию.

    Reply
  12. pumbaE

    (11) дайте догадаюсь документацию в wiki?

    Reply
  13. nSpirit2

    (12) pumbaE, Ну в моем случае это git + некоторые скрипты для извлечения комментариев из хода.

    Reply
  14. pumbaE

    (13) не поделитесь скриптами по документированию и опытом работы с xUnitfor1c?

    Какие режимы работы тестируете (управляемые формы, обычные формы)?

    Используете ли 8.3.5 кнопку нажималку для тестового случая?

    Как часто запускаете тесты? Автоматизировали ли CI в виде какого-либо сервиса сборки? Автоматизировали ли CD для обновления production базы?

    Какие затраты на написание тестов были вначале, какие сейчас?

    Мне было-бы очень интересно услышать ответы на эти вопросы.

    Reply
  15. AXIOMLAB24

    (9) nSpirit2, благодарю за детальный комментарий!

    По пунктам,

    п.1

    Простите но статья по большей части не о чем.

    Нет предела совершенству — будем стараться, будем работать над собой!

    п.2

    Отслеживание работы пользователя по большей части показывает, что выполнение отчета в фоне не как не помогает пользователю работать так как он ожидает результат. Для моей базы это примерно 70 процентов.

    Асинхронная концепция вписывается и в Ваш сценарий — тот, кто привык сидеть и ждать пусть так и продолжает. Но все же, на мой взгляд, современная тенденция развития личности, как профессионала своего дела, требует от него многозадачности. А если основной инструмент работы (ПП «1С:Предприятие») заблокирован хоть на несколько секунд, то как должен к нему относится пользователь?

    п.3

    Что относительно описания в БСП. Я думал будет больше функции из модулей <color #FF6347>ДлительныеОперации</>*

    Главной задачей статьи является попытка передать КОНЦЕПЦИЮ асинхронного программирования, но с небольшими и понятными примерами в «1С:Предприятие».

    Ждем детального продолжения.

    Если тема заинтересует достаточное количество специалистов, то продолжение следует!

    Reply
  16. lustin

    (11) nSpirit2, 😉 сделал мой день 😉

    Reply
  17. lustin

    (0) Ну это всё конечно хорошо, но раз уж тема актуальна и поднята, но это не наш метод — нет у нас доверия стабильности БСП, потому что нет тестов на эту функциональность, как верно заметили в (5)

    А что же тогда наш метод ? Вот вам пример вчера публикации на Хабре:

    1. Исходная публикация http://habrahabr.ru/post/255387/

    2. Репозиторий на github https://github.com/wizi4d/TaskManagerFor1C

    3. Кстати *внезапно* подсистема накрыта тестами https://github.com/wizi4d/TaskManagerFor1C/tree/master/%D0%A2%D0%B5%D1%81%D­1%82%D1%8B

    Reply
  18. nSpirit2

    (16) lustin, Нуу я все таки надеюсь что эта часть будет сильно популярна в среде 1С разработчиков что бы было меньше объяснять новым разработчикам.

    (17) lustin, Я описания на github прочитал. Идея конечно отличная не подскажите к быстро будет развиваться функциональность. К несчастью функционал БСП она пока не покрывает есть ли план развития ?

    Reply
  19. lustin

    (13) nSpirit2, комментарии из кода имеются ввиду наверное вот такие ?

    https://lh5.googleusercontent.com/-kKwl_8wC4t4/VSefrjB4TJI/AAAAAAAABXE/QrNgevaRws0/w605-h453-no/d695c2b9bb507dc8.png

    Reply
  20. lustin

    (18) nSpirit2

    1. по развитию надо спрашивать у https://plus.google.com/+ЕвгенийПавлюк — не могу определенно сказать какие у него дальнейшие планы. Сейчас насколько я понимаю была основная цель — поделиться подсистемой.

    2.

    что бы было меньше объяснять новым разработчикам.

    тут вот какое дело, только без обид — потому что это было весело…

    мы также поступаем — советуем всем использовать подходы и инструментарий. Тот же xUnit или https://github.com/silverbulleters/vanessa-behavoir, но блин… вы дали ссылку на инструментарий одному из авторов 😉 https://github.com/pumbaEO

    Reply
  21. nSpirit2

    (20) Неожиданные высказывания для разработчика 😀 Теперь и я ржу 😀

    Reply
  22. pumbaE

    (21) ну основное мое замечание касалось «типовой»(поставки, типового функционала от 1с).

    Reply
  23. karapuzzzz

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

    Для начала создаем временное хранилище:

    АдресВХранилище = ПоместитьВоВременноеХранилище(Неопределено);

    Потом создаем массив, в который заносим адрес в хранилище и остальные параметры:

    НаборПараметров = Новый Массив;
    НаборПараметров.Добавить(АдресВХранилище);
    НаборПараметров.Добавить(Параметр1);
    НаборПараметров.Добавить(Параметр2);
    НаборПараметров.Добавить(Параметр3);

    Если идет распараллеливание задания, то создается массив заданий и массив адресов в хранилище.

    В самом задании результат помещаем в хранилище:

    ПоместитьВоВременноеХранилище(Результат, АдресВХранилище);

    После того как словили факт окончания выполнения задания можно обработать результат. Для этого получаем результат из хранилища и делаем что хотим с этим:

    Результат = ПолучитьИзВременногоХранилища(АдресВХранилище)

    Reply
  24. JohnyDeath

    (11) Весело наблюдать за тем, как советуют поучиться инструменту одному из основных разработчиков этого инструмента )))

    Reply
  25. Evil Beaver

    (24) JohnyDeath, Ну это же круто, это признание!

    Reply
  26. pumbaE

    (25), (24), (16) тролли. Я, наоборот, рад тому, что кто-то другой знает и использует данный инструмент.

    p.s.:

    Мне все таки интересен ответ на (14) вопрос, хоть частичный.

    Reply
  27. nSpirit2

    (14) pumbaE, xUnitfor1c познакомились примерно в июле 2014. Случайно наткнулся в Гугле. С тем учетом что наша компания по факту занимается разработкой и продажей ПО которое сами и пишем (не 1с ). Cсобственно к разработке на 1с для внутренних нужд применяются те же требования. Используем для CI что то отдаленно напоминающее TeamCity. С возможностью автоматического тестирования и выкладки при условии динамического обновления. В части тестов интерфейсов после полного перехода на Управляемые формы в конце июня используем xUnitfor1c.

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

    Относительно документирования используем большую часть возможностей которые дает Git. В текущей ситуации пытаемся понять насколько возможно использовать стандартный git-flow для 1с не храня бинарники *.cf на всякий случай.

    Это если совсем коротко.

    Reply
  28. artbear

    Что-то я эту тему пропустил.

    И упоминание «моей прелести» xUnitFor1C также 🙂

    (27) А используете именно Gui/интерфейсные тесты в xUnitFor1C (через кнопко-нажималку) или обычные тесты в УФ для xUnitFor1C также юзаете?

    Reply
  29. WKBAPKA

    Статья довольно таки интересная.

    Для решения некоторых задач оч. даже полезная

    Reply
  30. kote

    Тоже делал механизм параллельной работы на фоновых заданиях..

    Сейчас возникла мысль — с сообщениями можно поступить следующим образом:

    Сделайте запуск фонового задания СообщениеИзФоновогоЗадания, которое будет вызываться из Вашего задания.

    В параметрах — передайте Сообщение для пользователя..

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

    Reply
  31. vitonya

    1С что-то оптимизирует, городит, а в результате, программа все медленней работает. ОПТИМИЗАЦИЯ, мля!!!

    Reply
  32. Dipod

    (9) В случае серверной базы с помощью фоновых заданий можно писать асинхронные обработки, которые распараллеливают выполнение алгоритма на несколько потоков.

    Вот пример сферической задачи в вакууме:

    Требуется создать очень много элементов справочника в базе, например, 4 миллиона.

    Проц у меня 4-хядерный. Если запустить выполнение обработки создания элементов справочника синхронным алгоритмом, то будет использоваться только одно ядро процессора.

    Если 4 миллиона элементов разбить на диапазоны по миллиону и запустить создание этих элементов в 4-х фоновых заданиях, то получаем загрузку проца в 100% и ускорение выполнения задачи в 4 раза (если не упремся в другие аппаратные ограничения, например в скорость записи на диск).

    Reply
  33. Denanhel

    (32)

    Требуется создать очень много элементов справочника в базе, например, 4 миллиона.

    Поделюсь своим печальным опытом… На выходе получаем «Конфликт блокировок при выполнениии транзакций» Для записи в БД такой подход очень бы не советовал использовать..

    Reply
  34. Dipod

    (33) Согласен, что есть вероятность конфликта блокировок, но я лично пробовал так создавать элементы и даже создавать и проводить документы, и конфликта блокировок не наблюдал. Но это еще не доказывает, что конфликта никогда не произойдет 🙂

    Reply
  35. Denanhel

    (34) Всё зависит от объёма данных, записываемых в базу и количества потоков. Мне нужно было записывать от 10 до 100 тысяч товаров в самописную конфигурацию раз в час. К тому же платформа 8.2. Блокировки не управляемые. Пробовал в 24 потока организовать (процессор позволяет). — тьма блокировок. Разложил на 4 потока. Блокировки пропали. Вот только скорость соответственно в 6 раз упала. Ну соответственно нужно искать компромисс… А так согласен плюсом идёт еще и то что можно сохранить адрес хранилища в локальной переменной и когда нужно получить ответ. Я допустим запускаю создание товаров в фоне, а в то же время загружаю справочник свойств этих товаров в отдельном потоке, а затем уже согласно полученным данным имея загруженные товары и свойства собираю регистр значений свойств. Получается гораздо быстрее чем делать всё поочередно.

    Reply
  36. Dipod

    (35)Да, я экспериментировал на платформе 8.3 и всего в 4 потока 🙂

    Reply
  37. masteroll

    Спасибо! Отличная статья!

    Reply
  38. ybatiaev

    (6) Ещё бы не мешало перед запуском фонового задания проверять уже запущенные фоновые задания. Некоторые можно плодить, а некоторые нет. К примеру, одно не закончилось, а ваша программа по функции ПодключитьОбработчикОжидания запускает точно такой же. Один не закончился из-за загрузки системы, второй запустился и ещё больше нагрузил систему…

    Мой пример с проверкой для разного вида фоновых дажаний

    //головная функция запуска фоновых заданий с оценкой
    Процедура ПроверитьЗапуститьФоновоеЗадание(Наименование, НаименованиеПроцедуры, Параметры_, ЗапуститьБезПроверки=Ложь) Экспорт
    //ФоновыеЗадания.ПолучитьФоновыеЗадания() // тут все, без отбора
    Отбор = новый структура(«Состояние, Наименование», СостояниеФоновогоЗадания.Активно, Наименование);
    КоличествоФЗ = ФоновыеЗадания.ПолучитьФоновыеЗадания(Отбор).Количество();
    ЗапускатьСледующееФоновоеЗадание = ?(КоличествоФЗ > 0, Ложь, Истина);
    
    Если ЗапускатьСледующееФоновоеЗадание или ЗапуститьБезПроверки Тогда
    фз = ФоновыеЗадания.Выполнить(«АсинхронныеОбработчики.»+НаименованиеПроцедуры, Параметры_, Новый УникальныйИдентификатор, Наименование);
    КонецЕсли;
    КонецПроцедуры
    

    Показать

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *