Вікі-код для Batch запити

Версія 6.1 додана 2024/05/16 17:24 автором Ashterix

Показати останніх авторів
1 {{box cssClass="floatinginfobox" title="**Зміст**"}}
2 {{toc/}}
3 {{/box}}
4
5 = Вступ =
6
7 (% class="box infomessage" %)
8 (((
9 Batch запити до API — це підхід, який дозволяє об'єднати кілька запитів в один. Замість того, щоб надсилати численні окремі запити до сервера, клієнт може надіслати один запит, який містить кілька операцій. Сервер отримує цей запит, обробляє всі операції та повертає одну загальну відповідь, яка включає результати всіх запитів.
10 )))
11
12 Уявімо що ми розробляємо бекенд API для інтернет-магазину і в нас вже є метод {{code language="none"}}ProductService.getInfo{{/code}} для сутності {{code language="none"}}Product{{/code}}. Задача на фронті зробити логіку додавання товарів в кошик і актуалізація інформації про товари по запиту (наприклад при відкритті кошика
13
14 Уявімо, що ми розробляємо бекенд API для інтернет-магазину і в нас вже є метод ProductService.getInfo для сутності Product. Задача на фронті полягає у створенні логіки додавання товарів до кошика і актуалізації інформації про товари за запитом (наприклад, при відкритті кошика нам потрібно оновити інформацію про товари, які користувач додав: наявність, ціни, описи, тощо). У класичному варіанті є два сценарії:
15
16 == Сценарій, яким йдуть 100% розробників ==
17
18 Ставиться задача на бекенд для створення окремого методу API, який має повертати колекцію об’єктів за масивом ідентифікаторів. Це вимагає робочого часу бекенд-розробника, тестувальника і фронтенд-розробника, оскільки новий метод має бути покритий юніт-тестами, а також додатковими сценаріями для регресійного тестування. Усе це потрібно зробити, незважаючи на те, що у нас вже є метод, який може повертати один товар за його id.
19
20 == Сценарій, яким ніхто не йде ==
21
22 Надсилати окремий запит до API для кожного товару, щоб отримати актуальну інформацію. Якщо в кошику 10 товарів, це означає 10 HTTP-запитів. Це створює значне навантаження на мережу і збільшує час очікування для користувача, що є неприпустимим варіантом.
23
24 == Альтернатива з використанням batch запитів ==
25
26 Якщо ми маємо можливість відправляти і обробляти batch запити, то можемо значно спростити реалізацію бізнес-логіки. Ми можемо використовувати batch запит, щоб об'єднати всі ці запити в один. Ми надсилаємо один запит до API, який містить всі ідентифікатори товарів, і сервер повертає інформацію про всі товари в одній відповіді. Таким чином, ми отримуємо:
27
28 1. **Універсальність**: Використання batch запитів дозволяє уникнути створення спеціалізованих методів для кожного випадку. Це спрощує API і зменшує кількість необхідного коду.
29 1. **Зручність для клієнта**: Клієнт може самостійно визначити, які саме запити потрібно об'єднати, що робить API більш гнучким і зручним для використання.
30 1. **Зниження складності бекенду**: Замість того, щоб писати та підтримувати спеціалізовані методи для різних сценаріїв, можна використовувати універсальний підхід з batch запитами, що спрощує архітектуру бекенду.
31
32 (% class="box successmessage" %)
33 (((
34 Такий підхід забезпечує більш ефективне використання ресурсів і покращує продуктивність, зменшуючи навантаження на мережу і сервери, а також поліпшує досвід користувача.
35 )))
36
37 (% class="box warningmessage" %)
38 (((
39 Звісно, що у високонавантажених системах подібниій сценарій буде не ефективним, прото існують інші варіанти оптимізацій, які ми розглянемо в інших статтях.
40 )))
41
42 = Приклад =
43
44 Розглянемо запит, приклад якого я наводив вище
45
46 (% class="row" %)
47 (((
48 (% class="col-xs-12 col-sm-6" %)
49 (((
50 {{code language="json" layout="LINENUMBERS" title="Request"}}
51 [
52 {
53 "id":"example_1",
54 "method":"ProductService.getInfo",
55 "params":{
56 "productId": 345234
57 }
58 },
59 {
60 "id":"example_2",
61 "method":"ProductService.getInfo",
62 "params":{
63 "productId": 994234
64 }
65 }
66 ]
67 {{/code}}
68
69 (% class="box warningmessage" %)
70 (((
71 **Важливо!!!** Завжди вказуйте в butch запитах унікальні ід запиту, бо відповіді можуть повертатися в іншому порядку.
72 )))
73 )))
74
75 (% class="col-xs-12 col-sm-6" %)
76 (((
77 {{code language="json" layout="LINENUMBERS" title="Response"}}
78 [
79 {
80 "id":"example_2",
81 "result": {
82 "id": 994234,
83 "name": "product 2",
84 "price": 11.99,
85 "balance": 28
86 }
87 },
88 {
89 "id":"example_1",
90 "result": {
91 "id": 345234,
92 "name": "product 1",
93 "price": 99.99,
94 "balance": 14
95 }
96 }
97 ]
98 {{/code}}
99 )))
100 )))
101
102
103
104 == Переваги batch запитів: ==
105
106 1. **Спрощення бекенду:** Замість того, щоб кожен раз реалізовувати специфічну логіку на боці бекенду, можна винести це на рівень побудови клієнтських запитів.
107 1. **Зменшення кількості мережевих запитів**: Замість надсилання декількох HTTP-запитів, що може створювати навантаження на мережу та сервер, використовується лише один запит. Це значно знижує навантаження на мережу та серверні ресурси.
108 1. **Час відповіді**: RPC Server віддасть результат всіх запитів не зважаючи на їх кількість за стільки часу, скільки необхідно на обробку найдовшого запиту з пакету, тому що обробка всіх запитів відбувається [[паралельно>>https://docs.ufo-tech.space/bin/view/docs/JsonRpcBundle/functionality/batch/#H42F43A44643543F44043044644E454]].
109 1. **Зменшення часу затримки**: Виконання декількох запитів в одному з'єднанні може знизити загальний час затримки, оскільки зменшується кількість часу, витраченого на встановлення та завершення з'єднань.
110 1. **Оптимізація використання ресурсів клієнта та сервера**: Оскільки менше запитів обробляється одночасно, знижується навантаження на сервер, що дозволяє йому обробляти більше запитів. Клієнт також використовує менше ресурсів, що може бути критичним для мобільних та обмежених пристроїв.
111 1. **Покращена керованість транзакцій**: Коли декілька операцій необхідно виконати атомарно (усі або жодна), batch запит дозволяє це зробити легше, оскільки усі операції виконуються в межах одного запиту.
112
113 = Підготовка до використання =
114
115 Функціональність batch запитів доступна одразу і не вимагає додаткових налаштувань з боку розробника.
116
117 = Послідовність запитів =
118
119 Послідовність наповнення batch не має значення, бо на RPC сервері всі запити виконуються паралельно і ви отримаєте відповідь із швидкістю найдовшого запиту з переліку.
120
121 Якщо в якомусь запиті з переліку відбудеться помилка, це ніяк не впливає на опрацювання інших запитів з переліку.
122
123
124 = Залежні запити =
125
126 (% class="box infomessage" %)
127 (((
128 Механізм batch запитів дозволяє створювати залежні запити. Це такий запит, один або кілька параметрів якого, залежать від відповіді з іншого запиту в переліку.
129 )))
130
131 (% class="wikigeneratedid" %)
132 Уявімо, що нам з фронтенда потрібно отримати email адресу користувача за його ідентифікатором, а потім відправити йому повідомлення на цю адресу. В нас вже існують методи {{code language="none"}}UserService.getInfo{{/code}} та {{code language="none"}}Messenger.sendEmail{{/code}}. В класичному сценарії це буде або два запити або необхідно створити на бекенді окремий метод, який буде відправляти email по id користувача.
133
134 (% class="wikigeneratedid" %)
135 Використовуючи бібліотеку {{code language="none"}}JsonRpcBundle{{/code}} від UFO-Tech у вас є можливість створювати batch запити, де перший один запит може ставати залежним від іншого і буде отримувати його відповідь для запуску. В контексті прикладу ми в одному запиті отримаємо інформацію про користувача, а другий запит використує отриманий емейл для відправки повідомлення.
136
137 (% class="row" %)
138 (((
139 (% class="col-xs-12 col-sm-6" %)
140 (((
141 {{code language="json" layout="LINENUMBERS" title="Request"}}
142 [
143 {
144 "id":"example_1",
145 "method":"UserService.getInfo",
146 "params":{
147 "userId": 141
148 }
149 },
150 {
151 "id":"example_2",
152 "method":"Messenger.sendEmail",
153 "params":{
154 "email": "@FROM:example_1(email)",
155 "subject": "Welcome!",
156 "message": "Thank you for joining our service!"
157 }
158 }
159 ]
160 {{/code}}
161 )))
162
163 (% class="col-xs-12 col-sm-6" %)
164 (((
165 {{code language="json" layout="LINENUMBERS" title="Response"}}
166 [
167 {
168 "id":"example_1",
169 "result": {
170 "id": 141,
171 "name": "John",
172 "email": "john.dou@example.com",
173 "status": 1
174 }
175 },
176 {
177 "id":"example_2",
178 "result": "Email for 'john.dou@example.com' sent"
179 }
180 ]
181 {{/code}}
182 )))
183 )))
184
185 У цьому прикладі:
186
187 1. Перший запит отримує інформацію про користувача з id 141, включаючи його емейл.
188 1. Другий запит відправляє лист на отриманий емейл, використовуючи дані з першого запиту.
189
190 (% class="box infomessage" %)
191 (((
192 **Послідовність запитів не має значення! **RPC Server сам визначає наявність залежність запитів і ставить їх в чергу в потрібній послідовності.
193 )))
194
195 (% class="box warningmessage" %)
196 (((
197 Залежні запити виконуються послідовно, тож відповідь на batch запит повернеться після завершення залежних запитів.
198 )))
199
200 == Переваги використання залежних запитів: ==
201
202 1. **Зручність і ефективність**: Залежні запити дозволяють виконувати складні операції з мінімальними зусиллями, забезпечуючи правильну послідовність виконання запитів.
203 1. **Зменшення кількості мережевих запитів**: Усі запити об'єднуються в один, що знижує навантаження на мережу.
204 1. **Гнучкість**: Можливість створювати складні сценарії запитів без необхідності додаткових налаштувань чи змін на бекенді.
205
206 = Як це працює =
207
208 Механізм обробки batch запитів працює асинхронно.
209
210 Отримуючи масив запитів RPC Server створює чергу з [[Symfony Process>>https://symfony.com/doc/current/components/process.html]], тобто запускає CLI команди, які обробляються асинхронно. В циклі while стан процесів перевіряється, і якщо отримано результат, він додається до масиву відповідей. Якщо ж відповіді немає до закінчення таймауту, повертається помилка про те, що запит не оброблено.
211
212 == Алгоритм: ==
213
214 * Batch запит розбивається на окремі запити, кожен з яких додається до черги.
215 * В циклі перевіряється наявність об’єктів у черзі.
216 * Для кожного об'єкту черги перевіряється, чи завершився процес.
217 * Якщо процес завершився, результат додається до масиву відповідей, а процес видаляється з черги.
218 * Якщо процес не завершився і таймаут ще не вийшов, цикл продовжується.
219 * Якщо таймаут вийшов до отримання результату, для конкретного запиту повертається помилка про те, що запит не оброблено.
220
221 Щоб збільшити таймаут у batch запиті, в параметрах конкретного запиту можна вказати додатковий службовий параметр {{code language="none"}}$rpc.timeout{{/code}} - максимальна кількість секунд очікування відповіді від процеса. За замовчуванням значення таймауту становить 10 секунд.
222
223 {{code language="json" layout="LINENUMBERS" title="Request"}}
224 [
225 {
226 "id":"example_1",
227 "method":"ExampleApi.fastMethod",
228 "params":{
229 "someParam": "someValue"
230 }
231 },
232 {
233 "id":"example_2",
234 "method":"ExampleApi.longMethod",
235 "params":{
236 "someParam": "someValue",
237 "$rpc.timeout": 30
238 }
239 }
240 ]
241 {{/code}}
242
243 Це дозволяє налаштувати тривалість очікування результатів для методів, що потенційно можуть працювати довго, що може бути важливо для обробки деяких запитів.