В данной реализации API Service и Metaservice объединены в одно приложение
На сервер A (API Service) по REST присылают файл, его надо разрезать на 6 примерно равных частей и сохранить на серверах хранения Bn (n ≥ 6).
При REST-запросе на сервер A нужно достать куски с серверов Bn склеить их и отдать файл.
API Service получает информацию о серверах и по их весу определяет где сохранить каждый чанк файла. Информация о чанках и серверах хранится в БД Metaservice (PostgreSQL)
API Service узнает о доступности серверов хранения через механизм Heartbeat. В ответе сервера хранения также возвращают доступное место на серверах. Это позволяет присваивать каждому серверу хранения свой вес в зависимости от оставшегося свободного места на диске. При загрузке чанка сервер хранения выбирается по самому максимальному/минимальному весу, что поволяет заполнять сервера хранения равномерно.
При возникновении проблем с загрузкой файла на сервер, пользователь может продолжить загрузку файла и недостающие чанки будут загружены на сервера хранения
Так как в данном задании все файлы делятся на 6 чанков, то при большом количестве файлов маленького размера мы можем упереться в ограниченное количество индексных дескрипторов (inode). Для решения этой проблемы было решено сохранять чанки в файлы-контейнеры. Поэтому каждый сервер хранения имеет свою БД (SQLite), в которой указывается в каком файле-контейнере находится искомый чанк и его смещение в данном файле
Собираем docker-compose
docker-compose build
Поднимаем контейнеры (На текущий момент все порты и адреса захардкожены)
docker-compose up
Для загрузки и скачивания файла можно воспользоваться клиентом, однако он пока что не подходит для загрузки больших файлов (> 2Гб).
Загружает файл в хранилище.
-
URL:
http://localhost:8080/upload
-
Метод:
POST
-
Требуемые заголовки (Headers):
"X-Filename": "{file_name}"
"Content-Type": "application/octet-stream"
"Username": "{username}"
Имя пользователя (опционально, по дефолту user)
-
Тело запроса:
binary
curl -X POST http://localhost:8080/upload \
-H "X-Filename: myfile.txt" \
-H "Content-Type: application/octet-stream" \
-H "Username: user" \
--data-binary "@path/to/myfile.txt"
Скачивает файл по его имени.
- URL:
http://localhost:8080/download?file_name={file_name}
- Метод:
GET
- Требуемые заголовки (Headers):
"Username": "{username}"
Имя пользователя (опционально, по дефолту user)
curl -X GET "http://localhost:8080/download?file_name=myfile.txt" \
-H "Username: user" -o downloaded_myfile.txt
Метод | URL | Описание | Заголовки |
---|---|---|---|
POST | /upload |
Загружает файл | "X-Filename" , "Content-Type" , "Username" |
GET | /download?file_name={file_name} |
Скачивает файл по имени | "Username" |
В данной реализации эта проблема никак не решается, однако подразумевается, что для повышения надежности (durability) сервиса необходимо обезопасить пользовательские данные от выхода HDD из строя.
Предположим, что годовая частота отказов жестких дисков составляет 1.89% (ссылка)
Обычная репликация (3 реплики) в данном случае даст только 5 девяток надежности (99.99932%), что является довольно низким показателем.
Использование RAID по типу 60, может частично решить эту проблему, однако появится проблема при дальнейшем расширении.
В данном случае лучше использовать стирающее кодирование (erasure coding), так как оно позволяет достичь большей надежности и настроить под себя сетап, для корректировки устоичивости