Некоторое время назад мне нужно было загрузить несколько больших двоичных файлов с удаленного сервера, и в то время у меня было очень нестабильное интернет-соединение. Примерно после пятой попытки загрузить файл и через 15 минут после того, как мой интернет упал, я сдался. Диспетчер загрузки Chrome не резал его, поэтому я открыл GitHub, быстро посмотрел, как другие пишут инструменты загрузки, и наткнулся на Snatch.
Урвать
Описание Snatch было «Простой, быстрый и прерывистый ускоритель загрузки, написанный на Rust». Именно то, что я хотел, и написанное на интересующем меня языке! Мало того, они также были популярным репозиторием Rust в то время и имели почти 600 звезд. При внимательном рассмотрении файла readme репозитория это волнение довольно быстро сменилось разочарованием.
функция Interruptable еще не реализована.
Какого черта…
Как у вас может быть прерываемый ускоритель загрузки без прерываемой части ?!
Предложение прерывания
Я попытался освоиться с кодовой базой, чтобы увидеть, будет ли трудно писать прерываемую функцию. Я открыл проблему, написал PR, чтобы исправить ее, и еще немного подумал об этой функции. Я формализовал свои мысли о том, как я считаю, что функция должна быть написана, и написал проект предложения по ней в открытом вопросе о прерывании репо.
Snatch работал с предварительным выделением места, необходимого для загрузки файла. Запустите n
поток через равные промежутки времени для файла и пусть каждый из потоков загрузит свой раздел. Если загрузка была прервана, не было возможности определить, сколько файла было загружено каждым потоком, поэтому все потоки должны были запускаться с начала своего раздела.
Чтобы эта функция заработала, я предложил следующие изменения:
- Первые 32 бита будут 32-битным целым числом, которое представляет количество блоков размером 64 КБ в файле.
- Позвоните по этому номеру
n
- Следующие
n
биты были битовыми флагами, показывающими, был ли загружен соответствующий фрагмент. - например Для файла размером 256 КБ
0101
будет означать, что 2-й и 4-й фрагменты были полностью загружены. - Остальная часть файла будет следовать
1 2 3 |-|-------|---------------------| 1: 32-bit number holding the number of 64kb chunks 2: n bits holding bit flags for each chunk 3: File contents
При перезапуске загрузки каждый поток может посмотреть на битовые флаги в разделе 2 и определить, можно ли пропустить часть загрузки. Когда загрузка была завершена, первые 32 бита могли быть прочитаны, что дало бы нам знать, сколько битов в разделе 2, поэтому мы могли удалить как раздел 1, так и раздел 2, не касаясь содержимого файла.
У этого подхода определенно было несколько ограничений. Поскольку мы могли удерживать состояние только для фрагментов размером 2³² по 64 КБ, максимальный размер файла составлял 256 ТБ (2³² * 64 * 1024 / 1024⁴). Потокам необходимо будет загрузить диапазон байтов, которые начинаются и заканчиваются в блоке размером 64 КБ, чтобы ни один блок не загружался двумя отдельными потоками. Также было несколько других небольших предостережений, которые означают небольшие изменения в логике загрузки и потоковой передачи.
Я надеялся, что после того, как все это изложит автору проекта, он одобрит это, и я смогу продвинуться вперед и построить его. К сожалению, он не выглядел слишком заинтересованным в написании функции прерывания, и вместо этого хотел отполировать каждый маленький аспект части загрузки, прежде чем рассматривать возможность прерывания.
Поэтому, естественно, как разработчик, я просто пошел и написал свой собственный пакет.
Схватить
Представляю Grapple, актуальный ускоритель прерывистой загрузки. Я черпал вдохновение из Snatch, поскольку Grapple изначально задумывался как доказательство концепции этой функции, но быстро затмил набор функций Snatch.
Фактическая реализация немного отличалась от того, что я предлагал изначально. Оказывается, очень сложно эффективно удалить первые n
байтов файла. И наоборот, обрезать конец файла очень легко, поэтому одно из изменений заключалось в перемещении всех метаданных в конец файла. Кроме того, чтобы резко увеличить ограничение на размер файла, я использовал 64-битное целое число для количества фрагментов и увеличил размер фрагмента с 64 КБ до 128 КБ. Это увеличило новый предел размера файла до 2 147 483 648 ПБ. Я, вероятно, умру прежде, чем файлы станут большими.
Новый макет файла:
3 2 1 |---------------------|-------|-| 1: 64-bit number holding the number of 128kb chunks 2: n bits holding bit flags for each chunk 3: File contents
Я использую Grapple примерно раз в неделю, и он еще не разбился о мне (касаться дерева). Я до сих пор возвращаюсь к нему время от времени, чтобы внести некоторые улучшения, совсем недавно я добавил ограничение пропускной способности для каждого потока, чтобы не перегружать мое интернет-соединение и не расстраивать моих соседей по дому. Grapple определенно был одним из самых полезных инструментов, которые я написал, и я намерен постоянно обновлять его, поскольку мне нужно от него все больше и больше.
Просто для удовольствия, когда я писал это, я вернулся, чтобы посмотреть на Snatch, чтобы узнать, была ли реализована функция прерывания. Snatch заменен вилкой Zou, и это было в readme:
Функция прерывания по-прежнему еще не реализована. Думаю, я просто буду использовать Grapple.