Параллельные вычисления для http-сервиса

19.11.20

База данных - HighLoad оптимизация

Процесс, поток, нить - в различных операционных системах и языках программирования это наименьшая единица программы. В платформе существует аналог: фоновое задание. Посмотрим, как его можно использовать для распараллеливания вычислений http-сервиса.

Скачать файл

ВНИМАНИЕ: Файлы из Базы знаний - это исходный код разработки. Это примеры решения задач, шаблоны, заготовки, "строительные материалы" для учетной системы. Файлы ориентированы на специалистов 1С, которые могут разобраться в коде и оптимизировать программу для запуска в базе данных. Гарантии работоспособности нет. Возврата нет. Технической поддержки нет.

Наименование По подписке [?] Купить один файл
Параллельные вычисления для http-сервиса:
.cf 78,66Kb ver:1
5
5 Скачать (1 SM) Купить за 1 850 руб.

Для начала на всякий случай вспомним, что регламентные и фоновые задания - это не совсем про одно и то же. Механизм регламентных заданий - это по сути планировщик, который будет запускать определенное фоновое задание с заданной периодичностью. Фоновое задание - это отдельный процесс (он же поток, он же нить, он же Жора, он же Гоша... не будем вдаваться в детали), исполняющий часть работы приложения. Фоновые задания мы можем создавать/запускать по мере необходимости, чем и воспользуемся для решения задачи ускорения вычислений.

В рассматриваемом примере имеем http-сервис, выполняющий вычисления, которые могут быть абсолютно любыми: от "2*2" до запросов к БД. Наш сервис будет вычислять квадрат для каждого из переданного в массиве числа. Если размерность массива превысит определенный порог, то будет применяться распараллеливание. В реальных задачах к этому лучше отнестись внимательно, т.к. из-за излишних накладных расходов на организацию параллелизма можно получить деградацию времени отклика сервиса. В качестве инструмента организации параллелизма будем использовать фоновые задания.

Зачастую ответом сервиса являются данные, отформатированные в определенной нотации. Никто не ограничивает нас в раздельном формировании частей общего ответа. Ведь мы можем сформировать несколько частей JSON, а затем объединить их в один большой результат? Конечно можем. Так и будем делать.

Ниже представлена основная логика с комментариями

//  
//  точка входа обработки тела запроса
//  тело запроса json-массив числовых значений [1, 2, 3, 4, 5, 6, 7]
//	результатом исполнения будет массив квадратов исходных чисел [1, 4, 9, 16, 25, 36, 49]
Функция Квадрат(ТелоЗапроса) Экспорт
    
    Результат = Новый Массив;    
    
    Попытка
        
        // получаем коллекцию из входных данных
        ВходящийМассив = ИзВеба(ТелоЗапроса);
        
        // служебная структура, которая содержит все необходимые для основного процесса вычислений параметры
        ПараметрыВычисления = СтруктураПараметровВычисления();
        ПараметрыВычисления.ВходящийМассив = ВходящийМассив;
        
        // определяем необходимость распараллеливания работы
        КоличествоНаПроцесс = 2;
        Если ВходящийМассив.Количество() > КоличествоНаПроцесс Тогда
            
            // реализуем параллельную обработку
            
            // разделяем данные на части
            МассивЧастей = РазделитьНаЧастиЗаданнойРазмерности(ВходящийМассив, КоличествоНаПроцесс);
            
            // формируем параметры исполнения для каждого процесса
            НомерПакета = 0;
            МассивПараметровФоновыхЗаданий = Новый Массив;
            Для Каждого Часть из МассивЧастей Цикл
                
                ТекущиеПараметрыВычисления = СтруктураПараметровВычисления();
                ТекущиеПараметрыВычисления.ВходящийМассив = Часть;
                ТекущиеПараметрыВычисления.НомерПакета = НомерПакета;
                
                ПараметрыФоновогоЗадания = Новый Массив;
                ПараметрыФоновогоЗадания.Добавить(ТекущиеПараметрыВычисления);
                МассивПараметровФоновыхЗаданий.Добавить(ПараметрыФоновогоЗадания);
                
                НомерПакета = НомерПакета + 1;
                
            КонецЦикла;
            
            // выполняем вычисления
            // для этого запускаем фоновые задания, ожидаем их завершения и собираем переданные сообщения
            // каждое сообщение - это кусочек вычисления, которые необходимо будет просто собрать вместе в один общий ответ
            МетодОбработки = "Вычислитель.ВычислитьКвадрат";
            СообщенияФоновыхПроцессов = ВыполнитьПроцессы(МетодОбработки, МассивПараметровФоновыхЗаданий);
            
            // получение и сортировка результата
            // сортировка может потребоваться, если результат сервиса должен быть собран в строго определенном порядке
            ТаблицаСообщений = Новый ТаблицаЗначений;
            ТаблицаСообщений.Колонки.Добавить("НомерПакета");
            ТаблицаСообщений.Колонки.Добавить("Результат");
            Для Каждого ТекущееСообщение Из СообщенияФоновыхПроцессов Цикл                
                ЗаполнитьЗначенияСвойств(ТаблицаСообщений.Добавить(), ИзВеба(ТекущееСообщение.Значение));
            КонецЦикла;
            ТаблицаСообщений.Сортировать("НомерПакета");
            
            // компоновка ответа сервиса
            Для Каждого ТекущееСообщение Из ТаблицаСообщений Цикл
                ТекущийРезультат = ТекущееСообщение.Результат;
                Для Каждого ТекущийЭлемент Из ТекущийРезультат Цикл
                    Результат.Добавить(ТекущийЭлемент);
                КонецЦикла;
            КонецЦикла;            
            
        Иначе
            
            // сюда попадаем, если не нужно распараллеливать            
            Результат = Вычислитель.ВычислитьКвадрат(ПараметрыВычисления);
            
        КонецЕсли;
                
    Исключение
        ОписаниеОшибки = ОписаниеОшибки();
        ЗаписьЖурналаРегистрации(ИмяСобытияЖурнала(), УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки);
    КонецПопытки;
    
    
    Возврат ВВеб(Результат);    
    
КонецФункции


А вот здесь можно увидеть, как фоновые задания передают результат исполнения

//
//
Функция ВычислитьКвадрат(ПараметрыВычисления) Экспорт
    
    Результат = Новый Массив;
    
    // выполняем вычисление квадрата для каждого числа
    Для Каждого Элемент Из ПараметрыВычисления.ВходящийМассив Цикл
        
        ТекущееЧисло = КакЧисло(Элемент);
        Результат.Добавить(ТекущееЧисло*ТекущееЧисло);
        
    КонецЦикла;
    
    // определяем необходимость передачи результата
    // если контекст текущего исполнения - это фоновое задание, то выполняется сообщение результата для родительского процесса
    Если ИсполнениеВФоновомПроцессе() Тогда
        
        Сообщение = Новый СообщениеПользователю;
        Сообщение.ИдентификаторНазначения = Новый УникальныйИдентификатор;
        Сообщение.Текст = ЗначениеВСтрокуВнутр(Новый ХранилищеЗначения(ВВеб(Новый Структура("НомерПакета, Результат", ПараметрыВычисления.НомерПакета, Результат)), Новый СжатиеДанных(9)));
        Сообщение.Сообщить();
        
    КонецЕсли;
    
    Возврат Результат;
    
КонецФункции

 

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

Чего мы можем добиться, используя описанный подход? В первую очередь быстродействие: параллельная обработка одного и того же набора данных может быть быстрее, чем последовательная. Во-вторых, надежность: уменьшение пакетов обработки может благотворно сказаться на показателях надежности. В-третьих, масштабируемость: можно распределять фоновые задания в кластере между нодами через ТНФ. 

Полная реализация описанного примера приведена во вложении к публикации. Конфигурация с примером (платформа 8.3.17).

http параллелизм фоновые задания параллельные вычисления

См. также

HighLoad оптимизация Программист Платформа 1С v8.3 Бесплатно (free)

Метод очень медленно работает, когда параметр приемник содержит намного меньше свойств, чем источник.

06.06.2024    9259    Evg-Lylyk    61    

44

HighLoad оптимизация Программист Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Анализ простого плана запроса. Оптимизация нагрузки на ЦП сервера СУБД используя типовые индексы.

13.03.2024    5097    spyke    28    

49

HighLoad оптимизация Программист Платформа 1С v8.3 Бесплатно (free)

Оказывается, в типовых конфигурациях 1С есть, что улучшить!

13.03.2024    7573    vasilev2015    20    

42

HighLoad оптимизация Инструменты администратора БД Системный администратор Программист Платформа 1С v8.3 Конфигурации 1cv8 Абонемент ($m)

Обработка для простого и удобного анализа настроек, нагрузки и проблем с SQL сервером с упором на использование оного для 1С. Анализ текущих запросов на sql, ожиданий, конвертация запроса в 1С и рекомендации, где может тормозить.

2 стартмани

15.02.2024    12419    241    ZAOSTG    80    

115

HighLoad оптимизация Системный администратор Программист Платформа 1С v8.3 Конфигурации 1cv8 Абонемент ($m)

Принимать, хранить и анализировать показания счетчиков (метрики) в базе 1С? Почему бы нет? Но это решение быстро привело к проблемам с производительностью при попытках построить какую-то более-менее сложную аналитику. Переход на PostgresSQL только временно решил проблему, т.к. количество записей уже исчислялось десятками миллионов и что-то сложное вычислить на таких объемах за разумное время становилось все сложнее. Кое-что уже практически невозможно. А что будет с производительностью через пару лет - представить страшно. Надо что-то предпринимать! В этой статье поделюсь своим первым опытом применения СУБД Clickhouse от Яндекс. Как работает, что может, как на нее планирую (если планирую) переходить, сравнение скорости работы, оценка производительности через пару лет, пример работы из 1С. Все это приправлено текстами запросов, кодом, алгоритмами выполненных действий и преподнесено вам для ознакомления в этой статье.

1 стартмани

24.01.2024    5669    glassman    18    

40

HighLoad оптимизация Программист Платформа 1С v8.3 Конфигурации 1cv8 Абонемент ($m)

Встал вопрос: как быстро удалить строки из ТЗ? Рассмотрел пять вариантов реализации этой задачи. Сравнил их друг с другом на разных объёмах данных с разным процентом удаляемых строк. Также сравнил с выгрузкой с отбором по структуре.

09.01.2024    14010    doom2good    49    

71
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. ltfriend 19.11.20 19:04 Сейчас в теме
Не стоит путать понятия "процесс" и "поток".
Процесс - экземпляр программы во время выполнения, независимый объект, которому выделены системные ресурсы (например, процессорное время и память).
Потоки (Thread) существуют внутри процесса. Есть основной поток, в котором выполняется основной код приложения, а также процесс может создавать дополнительные потоки, которые выполняются параллельно. Поток использует то же самое пространства стека, что и процесс.
А "нить" - просто прямой перевод слова Thread.
Другими словами, экземпляр запущенного сервера 1С - это процесс, фоновые задания, которые создаются и выполняются на этом сервере - потоки.
Shmell; VitaliyCeban; qwe322; van_za; +4 Ответить
2. NoRazum 29 03.12.20 11:40 Сейчас в теме
Увидел
"Попытка"
и большой кусок кода.
Дальше читать не стал.

Считаю эта плохим подходам.
(Хотя сама 1с очень любит так делать)
3. Lars Ulrich 625 03.12.20 15:23 Сейчас в теме
(2) Читать не призываю, но соглашусь с высказанной мыслью.
Здесь это сделано для того, чтобы не перегружать код проверкой входных данных. Тут ведь как: будешь смотреть на льва не увидишь золота, будешь смотреть на золото не увидишь льва.
4. NoRazum 29 03.12.20 17:40 Сейчас в теме
(3) Лев то хорош, Из-за этого поставил ПЛЮС.
Lars Ulrich; +1 Ответить
5. artbear 1563 03.12.20 19:02 Сейчас в теме
(0) Интересная публикация, спасибо!

одно но
Никогда так не пишите код обработки исключения
Исключение
        ОписаниеОшибки = ОписаниеОшибки();
        ЗаписьЖурналаРегистрации(ИмяСобытияЖурнала(), УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки);
КонецПопытки;

Так вы маскируете стек вызовов от администраторов, используйте подробное представление ошибки.
Стандарт 1С так и рекомендует.

подобная неточная обработка исключений - одна из самых частых ошибок\неточностей разработчиков.
очень часто наблюдаю такой код.
Lars Ulrich; +1 Ответить
9. efin 06.12.20 04:39 Сейчас в теме
(5) покажите, пожалуйста, пример кода. Потому что вот вроде слова знакомые, а общий смысл ускользает...
10. Lars Ulrich 625 08.12.20 10:51 Сейчас в теме
(9) имеется ввиду, что следует использовать подробное представление исключения.
Пример из синтаксис-помощника:
Попытка
    ...
Исключение
    Инфо = ИнформацияОбОшибке();
    Сообщить(НСтр("ru='Описание=';en='Description='") + Инфо.Описание + "'");
    Сообщить(НСтр("ru='ИмяМодуля=';en='ModuleName='") + Инфо.ИмяМодуля + "'");
    Сообщить(НСтр("ru='НомерСтроки=';en='LineNumber='") + Инфо.НомерСтроки + "'");
    Сообщить(НСтр("ru='ИсходнаяСтрока=';en='SourceLine='") + Инфо.ИсходнаяСтрока + "'");
КонецПопытки;
Показать


Источник для ознакомления: https://its.1c.ru/db/v8std/content/499/hdoc
divmaster; efin; +2 Ответить
11. artbear 1563 10.12.20 13:49 Сейчас в теме
(10) Да, спасибо за ссылку на стандарт.

Но пример кода из СП так себе (
вот как раз такое описание не нужно, а нужно как по ссылке на стандарт
6. artbear 1563 03.12.20 19:08 Сейчас в теме
(0) Ну и вообще при работе с фоновыми заданиями нужно помнить, что они могут быть прекращены в любой момент и могут не доработать до конца.
поэтому приходится использовать спец.обработку состояний фоновых заданий.

я для работы с фоновыми заданиями с гарантированным выполнением и гибкой настройкой много лет использую подсистему Менеджер Заданий моего товарища, Евгения Павлюка.
в ней многие проблемы работы с ФЗ решены.

Исходники есть на гитхабе, есть хорошая статья на habr-е
- https://github.com/wizi4d/TaskManagerFor1C
- https://habr.com/ru/post/255387/

Рекомендую.
Shmell; Lars Ulrich; +2 Ответить
7. artbear 1563 03.12.20 19:13 Сейчас в теме
(0) А почему юзаете возврат значений из ФЗ через СообщенияПользователю?
почему не штатный механизм 1С через временное хранилище, как написано в документации https://its.1c.ru/db/v8312doc#bookmark:dev:TI000000819

все-таки ненужная\излишня сериализация\десереализация
8. Lars Ulrich 625 04.12.20 12:06 Сейчас в теме
(7) можно и через механизм временного хранилища, будет побыстрее работать. соглашусь, что следовало бы в примере его использовать. Через СообщенияПользователю в текущей практике часто используется трансляция состояния исполнения фоновых заданий, как-то сходу его и задействовал.
user1503726; +1 Ответить
Оставьте свое сообщение