Ответ 1
Простое объяснение? Я попробую.:)
Вы строите трубопровод из компонентов. После того, как вы построили конвейер, его можно отправить. Это Iteratee, поэтому он знает, как выполнять итерацию данных.
Файл, который вы хотите загрузить, содержится в теле запроса, а BodyParser - это то, что обрабатывает тела запросов в Play. Таким образом, вы помещаете свой итерационный конвейер в BodyParser. Когда запрос будет сделан, ваш конвейер будет отправлен (он будет перебирать его).
Ваш конвейер (rechunkAdapter &>> writeToStore
) разбивает данные на бит 1 МБ, а затем отправляет их на S3.
Первая часть конвейера (rechunkAdapter
) выполняет разделение. На самом деле у него есть собственный мини-трубопровод, который выполняет разбиение (consumeAMB
). Как только мини-труба получила достаточное количество данных, чтобы сделать кусок, он отправляет его по основному конвейеру.
Вторая часть конвейера (writeToStore
) похожа на цикл, который вызывается на каждом фрагменте, что дает вам возможность отправлять каждый фрагмент на S3.
Преимущества итераций?
Как только вы знаете, что происходит, вы можете создавать итерационные конвейеры, подключая компоненты. И проверка типа будет чаще всего рассказывать вам, когда вы неправильно подключаете что-то вместе.
Например, мы можем изменить конвейер выше, чтобы исправить тот факт, что он медленный. Это, вероятно, медленное, потому что загрузка запроса приостанавливается всякий раз, когда кусок готов к загрузке на S3. Важно замедлить загрузку запроса, чтобы у нас не хватило памяти, но мы могли бы быть немного более прощающими, добавляя буфер фиксированного размера. Поэтому просто добавьте Concurrent.buffer(2)
в середину конвейера для буферизации до 2-х кусков.
Iteratees обеспечивают функциональный подход к потокам. Это преимущество или недостаток, в зависимости от того, как вы относитесь к функциональному программированию.:) По сравнению с ленивыми потоками (другой функциональный подход) итераторы предлагают точный контроль над ресурсом.
Наконец, итерации позволяют нам сделать очень сложное асинхронное потоковое программирование относительно (!) просто. Мы можем обрабатывать IO без хранения потоков, что является огромной победой для масштабируемости. Для классического примера Java InputStream/OutputStream требуется 2 потока.