Go против Excel на сотни тысяч строк

в 8:39, , рубрики: Excel, Go, Блог компании Voximplant, микросервисы, Программирование, Разработка веб-сайтов

В этом году мы уже писали на Хабре про наш проект SmartCalls.io – визуальный конструктор звонков, созданный для бизнес-пользователей. Проект решает задачу бизнеса по массовым обзвонам клиентов: создается визуальный сценарий звонка, загружается Excel-файл с номерами телефонов и далее создается кампания по обзвону. Запускается кампания – начинается обзвон клиентов; в любой момент можно смотреть статистику, приостанавливать кампанию, подкручивать настройки. Клиенты были довольны, пока не выяснилось, что иногда надо обзванивать не просто много людей, а ОЧЕНЬ, ОЧЕНЬ много. Под катом – суть проблемы и как мы ее победили с помощью хайпового (не безосновательно) языка программирования.

Go против Excel на сотни тысяч строк - 1

Проблема

Изначально обработка файлов была реализована на PHP 7.1 – это был очевидный выбор, так как весь API SmartCalls был написан именно на нем. Работа с колл-листами имела одно ограничение – файл должен содержать не более 10 тысяч заполненных строк. Это ограничение было с самого начала в SmartCalls и, впрочем, не было критичным. До определенного момента.

У одного крупного банка появилась потребность в очень больших кампаниях по обзвону: требовалось обзванивать гораздо больше 10 тысяч пользователей. Конечно, ничто не мешало разбивать большие файлы на несколько маленьких и загружать их поочередно, но заставлять клиентов вот так страдать – не наш метод. К слову о поочередной загрузке – если наш клиент уже запустил кампанию по обзвону и вдруг захотел добавить в нее пользователей, то он может легко это сделать. Это весьма удобно, потому что не надо останавливать обзвон или запускать отдельную кампанию по новым пользователям. Но стоит понимать, что возможность дозагрузки не задумывалась как способ грузить большие файлы вручную, по кускам.

Итак, у команды появилась задача – реализовать загрузку больших файлов в кампании по обзвону.

Решение

Мы очень компетентны в разработке на Java – например, частично API Voximplant реализован на этом языке; также мы хорошо умеем в PHP (см. пример выше – подсказывает Капитан Очевидность). То есть мы могли быстро закрыть эту задачу, используя один из этих языков, однако мы давно думали расширить наш стек технологий, и тут как нельзя кстати мы вспомнили про Go: он достаточно быстрый (хорошо работает с памятью), многопоточный и ему не нужен рантайм, т.к. Go компилируется в исполняемый бинарник. Вдобавок можно сказать про размер контейнеров, но об этом чуть позже…

В итоге мы написали микросервис на языке Go, который принимает листы большого размера (тестировали до 300 тысяч строк) и формата (xls, xlsx и все их разновидности). Настало время для подробностей.

Реализация

Когда клиент загружает в кампанию SmartCalls файл >10 тысяч строк, за него берется микросервис. Он принимает на вход указатели:

  • на файл, загруженный в S3-хранилище;
  • на кампанию, в которую этот файл нужно загрузить.

Затем микросервис пробегает по файлу, бьет его на чанки по 10 тысяч строк (максимум для платформы) и каждый чанк в виде csv-файла загружает в S3-хранилище, делая о каждом чанке заметки в БД (путь до файла, количество строк). Каждый чанк обрабатывается и загружается в отдельном потоке, что дает дополнительный прирост в скорости выполнения.

Для чтения Excel-файлов использовали опенсорсные библиотеки от tealeg и extrame. Хорошо, что у них не только много звезд, но еще и свежие коммиты :)

import (
  "github.com/tealeg/xlsx"
  "github.com/extrame/xls"
  // прочие импорты
)

И все бы хорошо, но не обошлось без нюансов. В ходе разработки выяснилось, что xlsx и xls, созданные в разных редакторах, сильно отличаются по форматам и правилам работы с ними. Пришлось делать много тестов – OpenOffice, Excel разных версий, LibreOffice, Google Sheets, чтобы научить микросервис приводить файлы к единому виду – CSV. После того, как микросервис «прожевывает» большой файл и превращает в CSV, в работу включается API SmartCalls и уже работает с этим csv-файлом. Для микросервиса мы оставили лимит в 300 тысяч строк, так как он сильно покрывает нужды клиентов, а с бОльшими потребностями мы и вовсе не сталкивались.

В итоге реализация отлично показала себя на тестах и препроде, после чего мы выкатили это в прод.

Вывод

Наша команда всегда старается быстро выкатывать новые фичи/доработки, потому что мы хотим, чтобы довольные клиенты таковыми и оставались. Задача с большими файлами была не просто очередным челленджем для нас, но еще и хорошим поводом внедрить в проект Go, к которому мы давно присматривались. Помимо быстрой разработки и скорости работы, Go дает нам задел на будущее, когда мы начнем внедрять контейнеры (чтобы делать бесшовные апдейты и вот это всё), которое у этого языка весьма легковесные. Про контейнеры мы обязательно напишем отдельно, stay tuned :)

Автор: nvpushkarskiy2

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js