JSON-RPC? Візьміть хитрий REST

JSON-RPC? Візьміть хитрий REST

Упевнений, що заголовок викликав здорову реакцію - "ну знову почалося ..." Але дозвольте заволодіти вашою увагою на 5-10 хвилин, і я постараюся не обдурити очікування.

Структура статті буде такою: береться стереотипне твердження і розкривається “природа” виникнення цього стереотипу. Сподіваюся, це дозволить глянути на вибір парадигми обміну даними у ваших проектах під новим кутом.

Для того щоб була ясність у тому, що таке RPC, пропоную розглядати стандарт JSON-RPC 2.0. C REST немає ясності. І не має бути. Все, що потрібно знати про REST - він не відрізняється від HTTP.

RPC запити швидше та ефективніше, тому, що дозволяють робити batch-запити.

Йдеться про те, що RPC можна в одному запиті виконати виклик відразу декількох процедур. Наприклад, створити користувача, додати йому аватар і в тому ж запиті підписати його на якісь топіки. Усього один запит, а скільки користі!

Дійсно, якщо у вас буде всього одна нода backend, це здаватиметься швидше при batch-запиті. Тому що три REST запити вимагають утричі більше ресурсів від однієї ноди на встановлення з'єднань.

JSON-RPC? Візьміть хитрий REST

Зверніть увагу, що перший запит у випадку REST повинен повернути ідентифікатор користувача, для виконання наступних запитів. Це також негативно впливає на загальний результат.

Але такі інфраструктури можна зустріти, хіба що, в рішеннях in-house і Enterprise. У крайньому випадку, у невеликих WEB проектах. А ось повноцінні WEB рішення, та ще й звані HighLoad так будувати не варто. Їхня інфраструктура повинна відповідати критеріям високої доступності та навантаженості. І картина змінюється.

JSON-RPC? Візьміть хитрий REST

Зеленим відзначені канали активності інфраструктури за такого ж сценарію. Зверніть увагу, як поводиться RPC тепер. Запит використовує інфраструктуру лише по одному плечу від балансувальника до backend. В той час, як REST все також програє в першому запиті, але надолужує втрачене, використовуючи всю інфраструктуру.

Достатньо до сценарію ввести не два запити на збагачення, а, скажімо, п'ять чи десять… і відповідь на запитання “хто виграє тепер?” стає неочевидним.

Пропоную глянути ще ширше на проблему. На схемі видно, як використовують канали інфраструктури, але інфраструктура не обмежується каналами. Важливою складовою високонавантаженої інфраструктури є кеші. Давайте тепер матимемо якийсь артефакт користувача. Декілька разів. Скажімо 32 рази.

JSON-RPC? Візьміть хитрий REST

Подивіться, як помітно "виправилася" інфраструктура на RPC для того, щоб відповідати вимогам високого навантаження. Вся справа в тому, що REST використовує всю потужність протоколу HTTP на відміну від RPC. На наведеній схемі ця міць реалізується через метод запиту GET.

У HTTP методів, окрім іншого, є стратегії кешування. Познайомитися з ними можна у документації на HTTP. Для RPC використовуються запити POST, які не вважаються ідемпотентними, тобто багаторазове повторення одних і тих же запитів POST може повертати різні результати (наприклад, після кожної відправки коментаря буде з'являтися чергова копія цього коментаря) (джерело).

Отже, RPC не може ефективно використовувати інфраструктурні кеші. Це призводить до того, що доводиться завозити софтові кеші. На схемі у цій ролі представлено Redis. Софтовий кеш, у свою чергу, вимагає від розробника додатковий кодовий шар та помітні зміни в архітектурі.

Давайте тепер порахуємо, скільки ж запитів "народив" REST і RPC в інфраструктурі?

запити
Вхідні
до backend
до СУБД
до софт-кешу (Redis)
РАЗОМ

REST
1 / 32 *
1
1
0
3 / 35

RPC
32
32
1
31
96

[*] у кращому випадку (якщо локальний кеш використовується) 1 запит (один!), у гіршому 32 вхідні запити.

У порівнянні з першою схемою різниця разюча. Тепер стає очевидним виграш REST. Але пропоную не зупинятися на досягнутому. Розвинена інфраструктура включає CDN. Часто він вирішує питання протидії DDoS і DoS атакам. Отримаємо:

JSON-RPC? Візьміть хитрий REST

Тут для RPC все стає дуже плачевно. RPC просто не в змозі делегувати роботу із навантаженням CDN. Залишається сподіватися лише на системи протидії атакам.

Чи можна на цьому закінчити? І знову ні. Методи HTTP, як уже говорилося, мають свою “магію”. І недарма спосіб GET є тотально використовуваним в Internet. Зверніть увагу, що цей метод здатний звертатися до частини контенту, здатний ставити умови, які зможуть інтерпретувати інфраструктурні елементи ще до передачі управління вашому коду і т.д. Все це дозволяє створювати гнучкі, керовані інфраструктури, здатні перетравлювати справді великі потоки запитів. А в RPC цей метод… ігнорується.

Так чому такий стійкий міф про те, що batch запити (RPC) швидше? Особисто мені здається, що більшість проектів просто не досягають такого рівня розвитку, коли REST здатний показати свою силу. Більше того, у невеликих проектах він охочіше показує свою слабкість.

Вибір REST або RPC – це не вольовий вибір окремої людини в проекті. Цей вибір має відповідати вимогам проекту. Якщо проект здатний вичавити з REST все те, що він справді може, і це справді потрібно, то REST буде чудовим вибором.

Але якщо на те, щоб отримати всі профіти REST, потрібно буде найняти в проект девопсов, для оперативного масштабування інфраструктури, адмінів для управління інфраструктурою, архітектора для проектування всіх шарів WEB сервісу... а проект, при цьому, продає три пачки маргарину на день... я б зупинився RPC, т.к. цей протокол більш утилітарний. Він не вимагатиме глибоких знань роботи кешів та інфраструктури, а сфокусує розробника на простих та зрозумілих викликах потрібних йому процедур. Бізнес буде задоволеним.

RPC запити надійніше, тому що можуть виконувати batch-запити в рамках однієї транзакції

Ця властивість RPC є безперечним плюсом, т.к. легко утримувати БД у консистентному стані. А ось із REST виходить все складніше. Запити можуть надходити непослідовно на різні ноди backend.

Цей "недолік" REST є зворотним боком його переваги описаного вище - здатність ефективно використовувати всі ресурси інфраструктури. Якщо інфраструктура спроектована погано, а тим більше, якщо спроектована погано архітектура проекту та БД зокрема, то це справді великий біль.

Але чи такі надійні batch-запити як здаються? Давайте розглянемо кейс: створюємо користувача, збагачуємо його профіль якимось описом та надсилаємо йому SMS із секретом для завершення реєстрації. Тобто. три виклики в одному batch-запиті.

JSON-RPC? Візьміть хитрий REST

Розглянемо схему. На ній представлено інфраструктуру з елементами високої доступності. Є два незалежні канали зв'язку з SMS шлюзами. Але що ми бачимо? При надсиланні SMS виникає помилка 503 - сервіс тимчасово недоступний. Т.к. відправка SMS упакована в batch-запит, весь запит повинен відкотитися. Дії в СУБД анулюються. Клієнт отримує помилку.

Наступна спроба – лотерея. Або запит знову потрапить на ту саму ноду і знову поверне помилку, або пощастить, і він здійсниться. Але головне, що щонайменше один раз наша інфраструктура вже попрацювала даремно. Навантаження було, а профіту немає.

Добре, уявімо, що ми напружилися (!) і продумали варіант, коли запит може успішно виконатися частково. А решту, ми знову спробуємо виконати через якийсь інтервал часу (Який? Вирішує фронт?). Але лотерея так і лишилася. Запит на відправку SMS із ймовірністю 50/50 знову провалиться.

Погодьтеся, з боку клієнта, сервіс не здається таким надійним, як хотілося б… а що REST?

JSON-RPC? Візьміть хитрий REST

REST знову використовує "магію" HTTP, але тепер із кодами відповідей. У разі виникнення помилки 503 на SMS шлюзі, backend транслює цю помилку балансувальнику. Балансувальник отримуючи цю помилку, і не розриваючи з'єднання з клієнтом, надсилає запит на іншу ноду, яка успішно відпрацьовує запит. Тобто. клієнт отримує очікуваний результат, а інфраструктура підтверджує своє високе звання "доступною". Користувач щасливий.

І знову це не все. Балансувальник не просто отримав код відповіді 503. Цей код при відповіді, за стандартом, бажано забезпечити заголовком "Retry-After". Заголовок дає зрозуміти балансувальнику, що не варто турбувати цю ноду по цьому роуту протягом заданого часу. І наступні запити на відправку SMS надсилатимуться відразу на ноду, у якої немає проблем із SMS шлюзом.

Як бачимо, надійність JSON-RPC переоцінена. Справді, легше організувати консистентність у БД. Але жертвою, у такому разі, стане надійність системи загалом.

Висновок багато в чому аналогічний попередньому. Коли інфраструктура проста, очевидність JSON-RPC безперечно його плюс. Якщо проект передбачає високу доступність з високою навантаженістю, REST виглядає більш вірним, хоч і складнішим рішенням.

Поріг входу до REST нижче

Я думаю, що вище проведений аналіз, що розвінчує усталені стереотипи про RPC, наочно показав, що поріг входу в REST безсумнівно вищий, ніж у RPC. Пов'язано це з необхідністю глибокого розуміння роботи HTTP, а також, з необхідністю мати достатні знання про існуючі інфраструктурні елементи, які можна і потрібно застосовувати у WEB проектах.

То чому багато хто думає, що REST простіше буде? Особисто моя думка полягає в тому, що ця простота, що здається, виходить з самих маніфестів REST. Тобто. REST це не протокол, а концепція… REST не має стандарту, є деякі рекомендації… REST не складніше HTTP. Вольна свобода і анархія вабить "вільних художників".

Безперечно, REST не складніше HTTP. Але сам HTTP це добре продуманий протокол, який довів свою спроможність десятиліттями. Якщо немає глибокого розуміння самого HTTP, то й про REST не можна судити.

А ось про RPC можна. Достатньо взяти його специфікацію. Так чи потрібен вам тупий JSON-RPC? Чи все ж таки хитрий REST? Вирішувати вам.

Щиро сподіваюся, що я не витратив ваш час.

Джерело: habr.com

Додати коментар або відгук