Сложное решение простых проблем HighLoad WEB-сервисов



    Ключевой задачей высоконагруженных WEB-систем является способность обработать большое число запросов. Решить эту проблему можно по-разному. В этой статье я предлагаю рассмотреть необычный метод оптимизации запросов к backend через технологию content-range (range). А именно — сократить их количество без потери качества системы путем эффективного кеширования.

    Для начала, предлагаю изучить статью, где весьма емко и доходчиво изложена преамбула технологии с примером для S2S. Далее, желательно познакомиться с моей первой статьей об использовании этой технологии для оптимизации работы с market-data на проекте криптобиржи.

    В этой статье, я хочу показать, что данная технология может быть использована шире, чем продемонстрировала первая статья. Напомню, там трейдинговая информация (свечи) получаются range-запросами к статическим файлам, которые заранее готовятся специальным сервисом. Теперь, я хочу рассмотреть запросы к полноценному backend на примере той же market-data, и для тех же свечей, без потери ключевых профитов.

    Итак, что планируется достичь:

    1. Оптимизировать запросы к backend (уменьшить их количество);
    2. Повысить скорость доставки контента конечному пользователю;
    3. Оптимизировать трафик.

    Еще раз, особо выделю то, что технология range является стандартом (RFC 2616). Она нативно поддерживается браузерами и они способны кешировать полученные порции данных. Следовательно, очередной запрос из браузера, при наличии актуального кеша запрашиваемой порции, реализуется на клиенте, не потревожив ваши сервера.

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

    Таким образом, чтобы состоялся реальный запрос к серверу, запросу необходимо преодолеть два эшелона кеширования, каждый из которых снижает объем запросов к конечному серверу. Конечно, финальным “редутом”, на пути запроса, может стать ваш сервер со своим кешем.

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

    Думаю, вводных достаточно. Давайте перейдем к частному случаю, и уже на примере разберемся как мы можем все это “счастье” использовать в частной задаче — запрос свечей за заданный интервал с заданной экспозицией.

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

    moment: int32 // Момент времени
    min: float64 // Минимальная цена
    max: float64 // Максимальная цена
    open: float64 // Цена открытия
    close: float64 // Цена закрытия
    volume: float64 // Объем
    average: float64 // Средняя цена
    

    Таким образом, наша структура займет 52 байта. Примем ее как квант — минимальный бинарный блок.

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

    Range: 5200-52000

    Должен нами интерпретироваться в размерности нашего кванта как:

    Range: 100-1000

    По сути, это будет offset и limit нашего запроса к БД.

    С определением экспозиции совсем просто, мы ее можем поместить в url. К примеру:

    /api/v1/candles/7m

    Т.е. мы будем запрашивать свечи с экспозицией 7 минут. Естественно, мы предполагаем, что это параметр может меняться по желанию frontend.

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

    В целом, очень напоминает классическую задачу с пагинацией.

    Остались мелочи. Каждая строка результата запроса конвертируется в бинарную структуру (тот самый квант) и полученный бинарный массив выводится как результат запроса, а в заголовок ответа отдается content-range:

    Content-Range: [запрошенный интервал] / [[количество записей в БД] * [размер кванта]]

    Стоп. А как же фрон сможет запросить нужный временной интервал, да еще и в байтовом интервале? Откуда он знает какие-то там номера свечей? Тут тоже все придумано. Range поддерживает относительное смещение, например,

    Range: -52

    Запросит 52 байта с конца. Т.е. последнюю свечу. Теперь, зная последний момент времени последней свечи, зная, из ответа, общий размер “файла”, можно вычислить общее количество свечей, а отсюда и определить байтовый интервал для запроса нужной временной экспозиции.

    Если вам вдруг захотелось задать вопрос — зачем такие сложности? Прошу вернуться к поставленным целям. Данная технология “замаскировала” аналитические запросы к БД в бинарные файлы для CDN и браузера. Это позволяет большую часть повторяющихся запросов переложить на CDN и конечного клиента.

    Возможно, возникнет еще вопрос — а почему бы не использовать простое кеширование GET запросов? Хорошо. Давайте разбираться. Если мы выполним вот такой запрос в классическом REST:

    GET /api/v1/candles/7m?from=01-03-2018&to=31-03-2018

    Мы получим уникальный кэш для этого запроса. Выполнив следующий запрос:

    GET /api/v1/candles/7m?from=15-03-2018&to=20-03-2018

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

    Так вот, в случае выше предложенной реализации (range), второй запрос не сформирует отдельного кэша, а использует уже полученные данные из первого запроса. И не полезет на сервер. А это, экономия размера кешей и уменьшение количества обращений к серверу.

    Есть ли минусы у данной технологии? Да. Они очевидны:

    1. Эта технология плохо подходит для меняющихся со временем данных, т.к. базируется на тотальном кешировании.
    2. CDN CloudFlare кэширует файлы только целиком. Это значит, что если конечный клиент запросит интервал скажем, с 1 по 100 байт, то CloudFlare реально запросит с сервера весь файл. Т.е. в нашем случае, он загрузит все свечи с определенной экспозицией. Положит у себя и будет уже раздавать сам. Это можно было бы считать даже плюсом, если бы не ограничения по месту. И если у вас могут формироваться “тяжелые” ответы, а параметров множество, то… В общем понятно, что место кончится. Возможно, мы так и не смогли его правильно настроить. Но пока результат таков.
    3. Требуется с умом управлять кешами. Для этого есть достаточные механизмы, но они требуют тюнинга.
    4. Frontend должен уметь парсить бинарные данные и иметь на борту что-то аля dataset для работы с range запросами.

    Я бы сформулировал целесообразность реализации этой стратегии так — когда она вам понадобится, вы поймете. Если сейчас есть сомнения, полезно знать о ней, но не стоит заморачиваться.
    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 11

      0

      Сколько у вас данных в базе, что нужно заморачиваться таким экзотическим кешированием? Не проще все в память положить?

        0

        Мы используем механизм из первой статьи. Он очень прост. А этот для нас избыточен. Возможно, он будет применен в аналитической подсистеме в будущем. Данная статья — развитие идеи. Она возникла по итогу комментариев к первой. Там мне пытались доказать, что это "прибитое к nginx гвоздями решение" и что оно не имеет запаса "гибкости". В какой-то степени, просто гештальт закрыл для себя.

          0
          так сколько у вас данных? неужели действительно не влезет в память?
        0
        Несмотря на кучу негатива в комментариях к первой статье, я просто зашёл биржу с профиля и увидел разницу в работе с другими аналогичными. И точно такая система, описання теперь, у меня реализовалась в мозгу после прочтения предыдущей статьи.
          0
          +100 Мне казалось, что эта статья по лишняя. Что первой вполне достаточно для предоставления базы, которую уже может развить любой, в нужно направлении. Но… как вы правильно отметили, комментарии к прошлой статье показали обратное. Я откровенно рад, если наш опыт кому-то окажется полезен.
            0
            поделитесь пожалуйста ссылкой, никак не могу найти
          0
          Возможно я не понимаю Вас, но причем тут память? Вы статику в память хотите запихнуть? Зачем? Есть CDN который сам решает как ему эти файлы хранить. В памяти или на диске. Мы находимся за ним.

          Закешировать запросы? Ну так я, мне кажется наглядно на это дал пояснения в статье. И положив что-то в память, это никак не решит вопрос.

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

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

          to Moxa
            0
            если данные лежат в памяти, то никакие cdn не нужны, слабенкая впска за 10 баксов сможет обрабатывать десятки тысяч подключений
            0
            Т.е. если на дохлой VPS (2400core, 512кб ОЗУ, 10Гб HD) с каналом 100мб, мы запихнем все в память… и можно отказаться от CDN? Верно? Доступ к данным будет для всех одинаково скор, хоть в Африке, хоть в Европе? Ну и DDoS (шквальную нагрузку запросами) мы легко переживем? А падение этого сервера невероятно, т.к. все лежит в памяти. Я ничего не упустил?
              0

              Но замеров — так и не представили...


              Говорят, что очевидно, что mergesort лучше quicksort, а поддерживать отсортированный массив пар цена/количество это убого по сравнению с красночёрным деревом и уж тем более хэш таблицей. И эти утверждения даже строгие доказательства имеют.


              Но на практике, c++ реализация quicksort, проходит тесты для mergesort для остальных языков на том же stepic'e. А за хранение аггрегированной буки до 10000 элементов в хэш таблице, в приличном обществе, можно и в глаз получить.

                0
                Будет время — сделаю.

                Но на практике, c++ реализация quicksort, проходит тесты для mergesort для остальных языков на том же stepic'e. А за хранение аггрегированной буки до 10000 элементов в хэш таблице, в приличном обществе, можно и в глаз получить.


                Мне кажется, это как определять степень влажности по итогу сравнения объемов мокрого с теплым… в целом я не против такого механизма, если его используют другие.

                Only users with full accounts can post comments. Log in, please.