company_banner

Elliptics от Яндекса. Как с его помощью создать своё отказоустойчивое хранилище

  • Tutorial
Добрый день, дорогие читатели!

В прошлых статьях я в общих чертах рассказывал про наше открытое отказоустойчивое хранилище Elliptics, а также немного опускался в детали. Сегодня же я вам наглядно расскажу и покажу, как использовать Elliptics на примере создания своей собственной отказоустойчивой ХабраМузыки.



ХабраМузыка – это ваше личное хранилище музыки с поддержкой региональности, реплицирования данных, минимальной нагрузкой на диск и сеть, а также простым HTTP API, который можно использовать в любом вашем приложении или на личном сайте.

Под катом — пошаговые подробности.

Разворачиваем Elliptics


Первым делом нам нужно поставить Elliptics. Для этого мы собираем пакеты в нашем репозитории под такие распространненные серверные системы, как:

  • Debian Wheezy;
  • Ubuntu Precise 12.04;
  • Ubuntu Trusty 14.04;
  • RedHat 6 / CentOS 6.

Для других дистрибутивов вы можете собрать систему самостоятельно из исходников, которые расположены на нашем GitHub-аккаунте.

Для дальнейшей работы необходимо поставить пакеты:

$ sudo apt-get install elliptics elliptics-client rift

или

$ sudo yum install elliptics elliptics-client rift


Если у вас есть желание попробовать в деле API для C++, то также следует поставить пакет elliptics-dev или elliptics-client-devel в зависимости от дистрибутива.

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

Для начала ознакомимся с примером конфигурационного файла node-1.json:

{
        "loggers": {
                "type": "/srv/elliptics/1/log.txt",
                "level": 4
        },
        "options": {
                "join": true,
                "flags": 4,
                "remote": [
                        "autodiscovery:224.0.0.5:1025:2"
                ],
                "address": [
                        "localhost:1025:2"
                ],
                "wait_timeout": 60,
                "check_timeout": 60,
                "io_thread_num": 16,
                "nonblocking_io_thread_num": 16,
                "net_thread_num": 4,
                "daemon": true,
                "auth_cookie": "<-Don't forget to change it->",
                "cache": {
                        "size": 1073741824
                },
                "indexes_shard_count": 16,
                "monitor_port": 20000
        },
        "backends": [
                {
                        "type": "blob",
                        "group": 1,
                        "history": "/srv/elliptics/1/history/",
                        "data": "/srv/elliptics/1/blob/data",
                        "sync": "-1",
                        "blob_flags": "129",
                        "blob_size": "10G",
                        "records_in_blob": "1000000"
                }
        ]
}

Чтобы запустить Elliptics с этим конфигурационным файлом, надо создать следующие каталоги:
  • /srv/elliptics/1/ – здесь в файл log.txt будут писаться логи с уровнем Debug;
  • /srv/elliptics/1/blob/ – здесь будут лежать блобы с данными;
  • /srv/elliptics/1/history/ – здесь будет лежать файлик с началами интервалов из DHT-кольца.

Подробно об остальных указанных и неуказанных полях можно прочитать в нашей документации.

Для запуска достаточно выполнить:

$ dnet_ioserv -c node-1.json

Но мы ведь хотим построить отказоустойчивый кластер. Для этого надо поднять еще хотя бы одну машину во второй группе. В её конфиге нужно будет указать другой адрес, а также другие remote-адреса. В итоге, пока используются конфигурационные файлы node-1.json, node-2.json для хранения данных в 2 копиях.

Создадим каталоги, нужные для первых двух нод, и запустим каждую из них:

$ for id in `seq 1 2`; do mkdir /srv/elliptics/$id/{history,blob} -p; done
$ for id in `seq 1 2`; do dnet_ioserv -c node-$id.json; done

Знакомимся с C++/Python API



А теперь, когда сервера у нас запущены, приступим к знакомству с низкоуровневыми C++ и Python API. Для этого каждое действие я буду показывать сразу на двух языках с пояснениями.

Подключение к серверам


Сперва нужно создать клиентскую ноду для подключения к серверам Elliptics.

С++:

#include <elliptics/session.hpp>

using namespace ioremap::elliptics;

file_logger logger(“/dev/stderr”, DNET_LOG_ERROR);
node n(file_logger);

n.add_remote("localhost", 1025); 
n.add_remote("localhost", 1026);

session sess(n);
sess.set_groups({ 1, 2, 3 });

Python:

import elliptics
log = elliptics.Logger(“/dev/stderr”, elliptics.log_level.debug)
node = elliptics.Node(log)

node.add_remote(“localhost”, 1025)
node.add_remote(“localhost”, 1026)

sess = elliptics.Session(node)
sess.group = [ 1, 2, 3 ]

Записываем данные в Elliptics


Запишем в Elliptics немного данных:

C++:

auto async_result = sess.write_data(std::string("test_key"), "some data", offset);

Python:

async_result = sess.write_data(“test_key”, “some_data”, offset)

Вышеприведенный код отправит запрос записи данных на сервера Elliptics. Однако после этого нужно дождаться ответов и обработать их. Многие команды посылают по несколько запросов на сервера Elliptics’а. Так, например, запись данных шлет запросы одновременно в каждую группу по запросу. На каждый запрос приходит ответ, из которого можно узнать как некоторые общие для всех команд сведения (например, адрес сервера, обработавшего запрос, или код результата выполнения команды), так и специфичную для данной команды информацию (такую, как путь к блобу, в который была произведена запись, с координатами внутри него).

Обработка ответа от сервера


Существует несколько вариантов обработки результата выполнения команды. Их стоит рассмотреть подробнее.

Синхронный запрос


C++:

try {
    async_result.wait();
} catch (error &e) {
	std::cerr << “Error while write execution: “ << e.message();
	return;
}
auto sync_result = async_result.get();
for (write_result_entry entry : async_result.get()) {
    // Process entry
    std::cerr << "Received answer from: " << dnet_server_convert_dnet_addr(result.address()) << "status: " << result.status() << std::endl;
}

Python:

try:
        async_result.wait()
except Exception as e:
        print "Write failed: ", e

for entry in async_result.get():
        print("Reply from {}, status: {}".format(entry.address, entry.error.code))

От кидания исключений можно избавиться, вызвав у session метод set_exceptions_policy, который присутствует как в C++, так и в Python API. В таком случае нужно делать ручную проверку на ошибки:

C++:

if (async_result.error()) {
    // Логика обработки ошибки
}

Python:

if (async_result.error) {
    // Логика обработки ошибки
}

Итерируемый запрос


Иногда суммарный объем данных от сервера может быть слишком большим, чтобы разом поместить его в память. Также может иметься требование обработки данных по мере их поступления. В таком случае можно воспользоваться итерированием: тело цикла будет выполняться на каждый ответ, пришедший от сервера, по мере их выступлений. Между пакетами поток пользователя будет спать.

C++:

for (auto entry : async_result) {
}

Python:

for entry in async_result:
   pass

Асинхронный запрос


Также, как только ответ будет полностью получен, можно выполнить свой код в потоке Elliptics’а.

C++:

async_result.connect([] (const sync_write_result &result, const error_info &error) {
    if (error) {
        // Обработка ошибки
        return;
    }
    
    for (auto entry : result) {
        // Обработка ответа
    }
});


Python:

def handler(results, error):
    if error.code != 0:
        // Обработка ошибки
        return
    for entry in results:
        // Обработка ответа
        pass
async_result.connect(handler)

Асинхронный итеративный запрос


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

C++

async_result.connect([] (const write_result_entry &entry) {
    // Обработка ответа
}, [] (const error_info &error) {
    if (error) {
        // Обработка ошибки
    }
});

Python:

def result_handler(result, error):
    // Обработка ответа
    pass
def error_handler(results, error):
    if error.code != 0:
        // Обработка ошибки
        pass
async_result.connect(result_handler, error_handler)

Чтение данных


Для чтения данных достаточно выполнить нижеприведенный код, а затем обработать ответ так же, как в случае write’а:

C++:

auto async_result = sess.read_data(std::string("test_key"), offset, size);

Python:

async_result = sess.read_data(“test_key”, offset, size)

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

Удаление данных


Удалять данные также просто.

C++:

auto async_result = sess.remove_data(std::string("test_key"));

Python:

async_result = sess.remove_data(“test_key”)

И многое другое


доступно в нашей документации.

HTTP API



Для работы с HTTP API мы рекомендуем использовать Rift. Подробная документация доступна здесь. Rift основан на нашем высокопроизводительном асинхронном HTTP-сервере TheVoid, специально спроектированном, чтобы работать в качестве HTTP-backend’а для Nginx.

Rift имеет поддержку двух основных режимов работы – с поддержкой бакетов и без. В этом посте мы будем рассматривать режим с бакетами как более гибкий и, конечно же, сложный. Бакеты позволяют разделять файлы и права различных пользователей. Например, можно дать одному пользователю право писать в бакет, а другой будет иметь возможность из него только читать.

В качестве примера рассмотрим способ создания своего сервиса по прослушиванию музыки.

Для начала нам нужно поднять еще как минимум один сервер Elliptics для хранения такой мета-информации Rift’а, как бакеты.

$ dnet_ioserv -c node-3.json

Теперь надо настроить и поднять Rift, его конфиг приведен в GitHub-репозитории под именем rift.json, рассмотрим его поближе:

{
    "endpoints": [
        "0.0.0.0:8080"
    ],
    "backlog": 128,
    "threads": 2,
    "buffer_size": 65536,
    "logger": {
        "file": "/srv/rift/log.txt",
        "level": 4
    },
    "daemon": {
        "fork": true,
        "uid": 1000
    },
    "monitor-port": 21000,
    "application": {
        "remotes": [
            "localhost:1025:2",
            "localhost:1026:2",
            "localhost:1027:2"
        ],
        "groups": [],
        "metadata-groups": [
            3
        ],
        "bucket": {
            "timeout": 60
        },
        "read-timeout": 10,
        "write-timeout": 16,
        "redirect-port": 8081,
        "path-prefix": "/srv/elliptics/"
    }
}

Конфигурационный файл можно разбить на две секции. Первая отвечает за конфигурирование HTTP-сервера в целом:
  • “endpoints” – список портов и unix-сокетов, которые слушает сервер на входящие соединения;
  • “logger” – конфигурация системы логгирования;
  • “daemon” – описание демонизации сервера.

Вторая секция – “application” – отвечает за конфигурацию Rift’а:
  • “remotes” – список узлов Elliptics’а;
  • “metadata-groups” – список групп, в которых хранится вся мета-информация, например, бакеты;
  • “redirect-port” – порт, который будет слушать Nginx на машинах с данными;
  • “path-prefix” – начальная часть пути к блобам на машинах с данными.

Подробно обо всех параметрах конфигурационного файла можно прочитать здесь.

$ rift_server -c rift.json

Все операции в Rift с активированными бакетами требуют авторизации. Алгоритм формирования подписи описан в документации. Здесь же я буду использовать крайне простой http-клиент на Python, который все запросы будет подписывать надлежащим образом. Он лежит в архиве и называется http_auth.py.

Создадим бакет, в который мы будем заливать нашу музыку. Причем хотелось бы, чтобы правом залива музыки обладал только администратор ресурса, то есть мы, а слушать мог кто угодно. Рассмотрим файл конфигурации бакета:

{
    "groups": [
        1, 2
    ],
    "acl": [
        {
            "user": "admin",
            "token": "favorite_music",
            "flags": 6
        },
        {
            "user": "*",
            "token": "very_secret_token",
            "flags": 1
        }
    ]
}

Первое поле “groups” говорит, что данные будут писаться в двух копиях, по копии в группах 1 и 2. Подробнее о группах можно прочитать в предыдущих статьях.
Второе поле “acl” содержит права доступа для различных пользователей, в данном примере для каждого пользователя были указаны 3 поля:
  • “user” – публичный id пользователя;
  • “token” – секретный токен, по которому происходит авторизация;
  • “flags” – описание прав пользователя, на данный момент доступны следующие флаги и их битовые комбинации:
    • 1 – для данного пользователя не требуется авторизация;
    • 2 – данный пользователь имеет право для записи данных в бакет;
    • 4 – данный пользователь имеет право для изменения данных самого бакета (список групп, acl и т.д.).

Правами для записи новых файлов и изменения бакета обладает только “admin”, так же указан его секретный ключ, с помощью которого будет происходить авторизация. А вот правом чтения обладает любой внешний пользователь без необходимости авторизации. Создадим теперь бакет music в директории all:

$ ./http_auth.py http://localhost:8080/update-bucket/all/music --file update-bucket.json
200

Директория – это особенная сущность, которая позволяет, например, найти все содержащиеся в ней бакеты. В созданный бакет зальем немного музыки по ключу example.mp3:

$ ./http_auth.py http://localhost:8080/upload/music/example.mp3?user=admin --file example.mp3 --token favorite_music
200

После этого можно слушать музыку прямо из любого проигрывателя:

$ mplayer http://localhost:8080/get/music/example.mp3

Ручка /get поддерживает заголовки Last-Modified и Range для более эффективного управления траффиком, но это все равно не самое рациональное использование ресурсов. Гораздо эффективнее было бы стримить данные с помощью Nginx’а с ближайших к пользователю физических машин, на которых данный файл хранится. Это достигается за счет поддержки региональности в Elliptics.

Для этого нам нужен Nginx с нашим eblob-модулем. Его можно найти на GitHub’е.

Собрать его можно с использованием любого подходящего Nginx’а (поддерживаются версии по 1.7.х) примерно следующим образом :

$ git clone https://github.com/toshic/nginx_patches.git
$ wget 'http://nginx.org/download/nginx-1.7.0.tar.gz'
$ tar -xzvf nginx-1.7.0.tar.gz
$ cd nginx-1.7.0
$ ./configure --prefix=/opt/nginx --add-module=../nginx_patches/nginx-eblob
$ make
$ sudo checkinstall # :)

Для активации eblob-модуля достаточно добавить блок “eblob;” в location-секцию. Таким образом, минимальный конфигурационный файл выглядит так:

worker_processes  8;
 
events {
    worker_connections  1024;
}
 
http {
    sendfile on;
 
    server {
        listen       8081;
        server_name  localhost;
 
        location / {
            root   /var/srw;
            eblob;
        }
    }
}

Разумеется, для боевого окружения Nginx следует настраивать не настолько наивно.

Отлично, теперь, когда nginx настроен и запущен, можно стримить музыку напрямую с него:

$ mplayer -q http://localhost:8080/redirect/music/example.mp3

По данному запросу сервер сгенерирует url специального вида, получив который Nginx сможет отдавать музыку прямо с blob’ов, в который Elliptics хранит свои данные.

Гораздо подробнее про Rift можно почитать в нашей документации. Там же имеется подробное описание всех доступных методов.

Вместо заключения


Про это и многое другое можно почитать в нашей документации на doc.reverbrain.com:

Напоминаю, что все предоставленные в данной статье проекты открыты и расположены на GitHub: https://github.com/reverbrain.
Яндекс 427,35
Как мы делаем Яндекс
Поделиться публикацией
Похожие публикации
Комментарии 35
    +1
    а будет поддержка s3 api?
      +3
      Да, мы планируем поверх Rift'а сделать поддержку, в том числе, s3 API, но пока можно и нужно пробовать сам Rift на деле :)
      –4
      Интересно, кому может понадобится использовать подобное специфическое хранилище? Разве что большим компаниям, но у них и так уже есть свои аналоги.
        +1
        Расскажите, пожалуйста, чем Elliptics+Rift лучше/хуже, чем LeoFS.
          +1
          А расскажите, пожалуйста, что такое LeoFS. Вы им пользовались?
            0
            Я им не пользовался (как и эллиптиксом). Но в публичные описания архитектур (DHT, свой способ хранения чанков) весьма схожи.
            При том, что слабые места кластера — диск и сеть, хотелось бы узнать, почему для реализации использованы столь многословные языки как C и C++.
              +1
              Когда данных становится действительно много, то слабыми местами становятся так же оперативная память и процессор, как раз на таких случаях C++ и позволяет получить серьезный выигрыш по сравнению с Erlang (если уж сравнивать в контексте LeoFS).

              Следующим пунктом является распространенность языка, найти толкового C или C++ разработчика в разы проще (но все равно невероятно трудно), чем аналогичного специалиста по Erlang.

                –3
                Ну, то есть, «нам сказали, что эрланг жрет много памяти и процессора, поэтому мы найдем 10 крутых плюсовиков, а не двух эрлангистов». Ок.
                  +3
                  Нет, просто в Яндексе люди работают, а не играются с технологиями за счет работодателя. Что все в команде хорошо знают и используют в других проектах, и на что всегда найдется человек на суппорт — на том и написали.
                    –1
                    очень смешно читать такие аргументы.

                    Яндекс бесконечно кормит, Яндекс всегда даст человека на суппорт и вы называете это не играми за счёт работодателя? =)))
                    0
                    Что ж это 10 плюсовиков-то вместо 2?
                      –2
                      Очевидно, чтобы закончить бета-версию менее, чем за 5 лет.
                    –2
                    А ява вам чем не угодила с ее, например, уже давно написанным и работающим HDFS?
                      +1
                      С++, значит, многословный, а Java не многословная. Ну-ну :)
                        +1
                        Java при той же многословности и схожей производительности дает еще и защиту от сегфолтов. Ну, не считая того, что под нее уже три горы релевантных библиотек написано.
                          –1
                          Как жаль, что вы в Яндексе не руководитель отдела, вы бы их научили сразу, на чем надо писать!

                          Вы еще забыли предложить Go и Rust, подсказываю :)
                            +2
                            Вот так из просьбы сравнить два схожих продукта при помощи религии мы перешли на личности.
                            0
                            GC, чёртов GC испортит вам жизнь…

                            Segfault не страшен, Ну будет кора и что? Потом это починят, когда разбереут. Упавшее приложение естественно поднимут автоматом. А вот историй про то, что" Java течет и я ее перезапущу, а если опять потекла, то в cron поставлю" — хоть отбавляй. И Outofmemory никто не отменял, да
                              0
                              Как OOM, так и segfault ломают всю сотню соединений, находящихся в процессе передачи данных. При этом OOM сильно более предсказуем.
                        0
                        > найти толкового C или C++ разработчика в разы проще, чем по Erlang

                        Это либо злонамеренная ложь, либо нехватка информации. Надеюсь, что второе.

                        Что касается скорости, то у меня видеостриминговый сервер на эрланге без проблем раскидывает по 10 гигабит данных в сеть с чтением и перепаковкой. Процессорной работы НАМНОГО больше, чем в сторадже.

                        Поэтому ваша гипотеза о том, что VM эрланга не хватит по скорости для перекидывания данных в сторадже совершенно неверна, потому что основана на ваших домыслах, а не на фактах и проверках.
                          0
                          > найти толкового C или C++ разработчика в разы проще, чем по Erlang

                          Это либо злонамеренная ложь, либо нехватка информации. Надеюсь, что второе.


                          Зашел на hh.ru: C++ — 615 вакансий, Erlang — 14 вакансий, кажется, что это дает уже достаточное представление о рынках труда по этим двум языкам.

                          Что касается скорости, то у меня видеостриминговый сервер на эрланге без проблем раскидывает по 10 гигабит данных в сеть с чтением и перепаковкой. Процессорной работы НАМНОГО больше, чем в сторадже.


                          Чтобы раздавать большие видео-файлы и нагрузить ими 10-гигабитный канал много усилий не надо (даже при условии, что происходит конвертация видео-потока в другой формат, чем занимается, опять же, скорее всего не Erlang, а модуль на C/C++ с использованием какого-нибудь ffmpeg/gstreamer/libav/etc) :)

                          Гораздо сложнее с процессорной точки зрения нагрузить эти же 10-100 гигабит с одной машины маленькими 1-2-килобайтными файлами, при условии, что на машине этих файлов десятки миллиардов. Вот на таких задачах я уже начинаю сильно сомневаться в конкурентоспособности Erlang.
                            0
                            > кажется, что это дает уже

                            Вы продолжаете рассуждать о вещах, которые вы не знаете. Вы лишь предполагаете, но не знаете.

                            Я знаю и знаю, что erlang разработчик существенно дешевле (в силу высшей производительности) и проще ищется.

                            > раздавать большие видео-файлы и нагрузить ими 10-гигабитный канал много усилий не надо… чем занимается, опять же, скорее всего не Erlang, а модуль на C/C++ с

                            и вы опять сели в лужу со своим незнанием =)

                            Видеостриминг — это когда большой файл на лету парсится, читается, из него выгружаются нужные куски данных, перепаковываются и отправляются с перепаковкой до последнего байта. Причем никаких модулей на C у нас нет, как бы вам это ни фантазировалось.

                            Такая мелкая халява, как раздача целого файла с диска по сравнению с этим — это детский лепет. Это я опять же, в отличие от вас, оцениваю по своему опыту, а не по предположениям. Дисковый сегментный кеш у нас потребляет существенно меньше ресурсов.

                            Так что ваша гипотеза о том, что софт на Erlang упрется в процессор на файловом сторадже пока что не имеет под собой никаких оснований кроме сильной веры в волшебную силу C++.
                              0
                              Я знаю и знаю, что erlang разработчик существенно дешевле (в силу высшей производительности) и проще ищется.

                              Я свои доказательства привел, просьба вам также отойти от голословных обвинений и начать выражать свои мысли чуть более разумно.

                              Это я опять же, в отличие от вас, оцениваю по своему опыту, а не по предположениям.

                              Возможно, но вы пропустили пункт про десятки миллиардов файлов, превращающий задачу в гораздо более требовательную к cpu/io ресурсам. Взять один конкретный файл и каким угодно образом его нарезать сильно проще, чем найти тот единственный файл из десятков миллиардов и отдать его наружу. В любом случае, с вашей стороны все еще нет абсолютно ничего, кроме голословных предположений и обвинений в некомпетентности.
                                0
                                понятно. На этом я закончу с вами обсуждение. Вы придумки придумываете за счёт работодателя, а я делаю продукт и зарабатываю на нём деньги.

                    0
                    Аналогично не пользовался ни тем ни тем. Посмотрел документацию по LeoFS, понравилось:
                    * что есть поддержка S3 API;
                    не очень понравилось:
                    * просят (рекомендуют) держать на XFS,
                    * не понятно на счет балансировки и перераспределения на ближайшую ноду;
                    В остальном надо читать внимательней, ну и тестировать

                    Касательно Elliptics, хотелось бы поддержку S3 API, понравилось:
                    * можно использовать Nginx для отдачи + пересылка на ближайшую ноду,
                    * читабельные простые конфиги
                    * есть возможность формирования кэш ноды на быстрых носителях.
                    Ну и все же Elliptics тесно интегрируется с Cocaine, что тоже может быть полезно.

                    Собственно это все субъективно и что пришло в голову из прочтения документации по обоим продуктам.
                      0
                      Дополню, судя по слайдам, у LeoFS еще и средства для мониторинга и управления есть, это плюс.
                    0
                    А какой системой по теореме CAP является Elliptics? Насколько я понял, это eventual consistency, т.е. AP. А есть что-то вроде Mongo'вского WriteConcern.MAJORITY?
                      0
                      Да, все верно, Elliptics — это eventual consistency.
                      А есть что-то вроде Mongo'вского WriteConcern.MAJORITY?

                      Есть более клевая штука, в Elliptics пользователь (администратор HTTP proxy, например) сам выбирает необходимый уровень надежности и может как писать с кворумом, требуя определенный процент успешных записей, так и писать без оглядки на количество успешно-записанных копий.
                      0
                      А я по статье не очень понял, nginx-модуль можно без rift использовать?
                        0
                        Да, модуль всего-лишь предоставляет low-level доступ к данным в файловой системе в довольно общей форме, поэтому может использоваться не только с rift'ом.
                          0
                          а загружать объекты через nginx-модуль можно? или только раздача? если нет, в планах можно будет?
                            0
                            Судя по документации и примерам, то работа идет не через nginx, а напрямую с Rift-ом. При указании ему порта для на котором сидит nginx, при запросе на загрузку он дает 302ой редирект на NGINX и там уже начинается загрузка. Если смотреть на пример с музыкальным файлом. Поправьте если не прав.

                            Ну и в принципе никто не мешает настроить проксирование Nginx-ом запросов, это лишь вопрос правильно конфигурации ИМХО.
                        0
                        Подскажите, монитор порт для чего используется? Можете привести пример?
                          0
                          Он возвращает показатели работы сервера, статистику некоторую. А использовать можно соответственно для мониторинга нагрузки.
                          0
                          Я хорошо помню разговор с одним из авторов эллиптикса: если у тебя меньше чем три стойки в двух датацентрах, то отойди в сторону, мальчик, у нас взрослый продукт, не для тебя.

                          Вот я как ни посмотрю на эллиптикс, так у меня и не пропадает ощущение, что без трех стоек он не нужен, потому что начать им пользоваться непонятно как: безумно сложен в настройке.

                          Только вот им вообще пользоваться мне всё таки непонятно. Если нет поддержки S3 API, то как можно его проверить? Надо сразу затачиваться на неизвестную софтину с неизвестным уровнем поддержки и развития?

                          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                          Самое читаемое