Слайд 2Меня хорошо слышно && видно?
![Меня хорошо слышно && видно?](/_ipx/f_webp&q_80&fit_contain&s_1440x1080/imagesDir/jpg/1086630/slide-1.jpg)
Слайд 3Карта вебинара
Statless vs stateful масштабирование
Идемпотентность API
Паттерны кэширования
Кэширование данных в разных сервисах
![Карта вебинара Statless vs stateful масштабирование Идемпотентность API Паттерны кэширования Кэширование данных в разных сервисах](/_ipx/f_webp&q_80&fit_contain&s_1440x1080/imagesDir/jpg/1086630/slide-2.jpg)
Слайд 4Stateless сервисы
stateless cервисы:
Не хранят состояние между запросами клиента внутри себя
Могут хранить состояние
![Stateless сервисы stateless cервисы: Не хранят состояние между запросами клиента внутри себя](/_ipx/f_webp&q_80&fit_contain&s_1440x1080/imagesDir/jpg/1086630/slide-3.jpg)
во внешнем сервисе
Примеры: proxy, gateway, обычные CRUD-сервисы.
Слайд 5Stateless сервисы
stateless cервисы масштабируются горизонтально
Можно направлять запрос на любую ноду
![Stateless сервисы stateless cервисы масштабируются горизонтально Можно направлять запрос на любую ноду](/_ipx/f_webp&q_80&fit_contain&s_1440x1080/imagesDir/jpg/1086630/slide-4.jpg)
Слайд 6Масштабирование stateless cервисов
![Масштабирование stateless cервисов](/_ipx/f_webp&q_80&fit_contain&s_1440x1080/imagesDir/jpg/1086630/slide-5.jpg)
Слайд 7Отказоустойчивость при балансировании
Отключить упавший сервис/сервер из балансирования
Подготовить код приложения и архитектуру к
![Отказоустойчивость при балансировании Отключить упавший сервис/сервер из балансирования Подготовить код приложения и](/_ipx/f_webp&q_80&fit_contain&s_1440x1080/imagesDir/jpg/1086630/slide-6.jpg)
частым и/или возможным падениям
В микросервисной архитектуре балансинг делается на стороне оркестратора приложений (Kubernetes)
Слайд 8Stateful сервисы
stateful сервисы
Хранят состояние
Масштабируются за счет репликаций, шардинга и т.д.
Примеры: БД
![Stateful сервисы stateful сервисы Хранят состояние Масштабируются за счет репликаций, шардинга и](/_ipx/f_webp&q_80&fit_contain&s_1440x1080/imagesDir/jpg/1086630/slide-7.jpg)
(oracle, postgres, mongodb)
in-memory cache (tarantool, ignite)
web-socket приложения (чаты)
Слайд 9Stateful cервисы
stateful cервисы масштабируются за счет шардинга и репликации
Запросы должны роутиться на
![Stateful cервисы stateful cервисы масштабируются за счет шардинга и репликации Запросы должны](/_ipx/f_webp&q_80&fit_contain&s_1440x1080/imagesDir/jpg/1086630/slide-8.jpg)
конкретные ноды
Sticky sessions, sharding и т.д
Слайд 10Кейс
Есть приложение «Интернет-магазин». Когда пользователь нажимает кнопку «Оформить заказ», то происходит запрос
![Кейс Есть приложение «Интернет-магазин». Когда пользователь нажимает кнопку «Оформить заказ», то происходит](/_ipx/f_webp&q_80&fit_contain&s_1440x1080/imagesDir/jpg/1086630/slide-9.jpg)
POST /api/v1/orders/
{
“products”: [{“id”: 42, “price”: “2500”}],
“shipping_to”: “Большая Филевская 16к1, кв. 14”
}
При этом деньги снимаются со счета в Личном кабинете, и происходит резервирование товара на складе.
Кнопка после нажатия остается активной. И иногда пользователи два раза нажимают на кнопку и происходит дублирование заказа со снятием двойной суммы.
Что бы вы предложили в качестве решения проблемы?
Слайд 11Кейс
Пока не придет ответ сервера, не делаем кнопку «активной».
Что делать, если интернет
![Кейс Пока не придет ответ сервера, не делаем кнопку «активной». Что делать,](/_ipx/f_webp&q_80&fit_contain&s_1440x1080/imagesDir/jpg/1086630/slide-10.jpg)
отвалился в момент нажатия на кнопку «Оформить заказ»? Клиент получил ошибку таймаута, и считает, что запрос не прошел, а сервер его выполнил.
Слайд 12Кейс
Добавляем ключ Request-Id в запрос
POST /api/v1/orders/
X-Request-Id: de7efba4-267c-11ea-978f-2e728ce88125
{
“products”: [{“id”: 42,
![Кейс Добавляем ключ Request-Id в запрос POST /api/v1/orders/ X-Request-Id: de7efba4-267c-11ea-978f-2e728ce88125 { “products”:](/_ipx/f_webp&q_80&fit_contain&s_1440x1080/imagesDir/jpg/1086630/slide-11.jpg)
“price”: “2500”}],
“shipping_to”: “Большая Филевская 16к1, кв. 14”
}
Если запрос с таким ключом уже пришел, то мы заказ не создаем.
Слайд 13Кейс
Пользователь нажал кнопку «Оформить заказ». Но ответ от сервиса был очень долгим.
![Кейс Пользователь нажал кнопку «Оформить заказ». Но ответ от сервиса был очень](/_ipx/f_webp&q_80&fit_contain&s_1440x1080/imagesDir/jpg/1086630/slide-12.jpg)
Клиент не стал дожидаться ответа от приложения и его полностью закрыл и выгрузил из памяти.
Когда он зашел в приложение, запрос еще не отработал и в списке заказов старого заказа не было. Клиент сформировал новый operation-id и отправил еще один запрос. В результате создалось 2 заказа.
Слайд 14Решение
Делаем версионирование коллекции /api/v1/orders/.
Сервер присылает заголовок ETag с версией коллекции orders.
Клиент
![Решение Делаем версионирование коллекции /api/v1/orders/. Сервер присылает заголовок ETag с версией коллекции](/_ipx/f_webp&q_80&fit_contain&s_1440x1080/imagesDir/jpg/1086630/slide-13.jpg)
при изменении коллекции заголовок If-Match с версией, которую он знает.
/api/v1/orders/
Etag: 42
Сервер проверяет: если If-Match совпадает с версией на сервере, то запрос проходит. Если нет, то отвечает ошибкой.
POST /api/v1/orders/
If-Match: 42
429 Conflict
Слайд 15Решение
Иногда Request-Id передается query параметром
Иногда используется не версия, а hash от содержания
![Решение Иногда Request-Id передается query параметром Иногда используется не версия, а hash](/_ipx/f_webp&q_80&fit_contain&s_1440x1080/imagesDir/jpg/1086630/slide-14.jpg)
коллекции – fingerprint
https://cloud.google.com/compute/docs/reference/rest/v1/instances/setTags
Слайд 16Идемпотентность API
Идемпотентность API – можно послать несколько раз один и тот же
![Идемпотентность API Идемпотентность API – можно послать несколько раз один и тот](/_ipx/f_webp&q_80&fit_contain&s_1440x1080/imagesDir/jpg/1086630/slide-15.jpg)
запрос (сообщение), и состояние на сервере не изменится.
Слайд 17Идемпотентность удаления
Можно несколько раз вызывать удаление ресурса и результат будет таким же.
DELETE
![Идемпотентность удаления Можно несколько раз вызывать удаление ресурса и результат будет таким](/_ipx/f_webp&q_80&fit_contain&s_1440x1080/imagesDir/jpg/1086630/slide-16.jpg)
/api/v1/orders/{id}/
Если заказ с таким id уже был удален, то 200 ОК
Если заказа с таким id не было, то 400 BAD REQUEST
Слайд 18Идемпотентность и внешние сервисы
Есть сервис notification. Он отправляет смс-ки.
Сервис читает сообщения
![Идемпотентность и внешние сервисы Есть сервис notification. Он отправляет смс-ки. Сервис читает](/_ipx/f_webp&q_80&fit_contain&s_1440x1080/imagesDir/jpg/1086630/slide-17.jpg)
из очереди и отправляет запрос к внешнему сервису, после чего помечает, что сообщение отправлено.
Иногда сервис умирает или внешний сервис не отвечает. И тогда сервис снова берет тоже самое сообщение из очереди. Что потенциально приводит к нескольким доставкам сообщений клиенту
Слайд 19Кэш в приложении
кэш хранится на уровне приложения
все кэши изолированы друг от друга
приходится
![Кэш в приложении кэш хранится на уровне приложения все кэши изолированы друг](/_ipx/f_webp&q_80&fit_contain&s_1440x1080/imagesDir/jpg/1086630/slide-18.jpg)
кэшировать одно и то же много раз на каждом из инстансов
Слайд 20Reverse proxy
Кэш хранится на балансере
Инвалидация кэша на стороне сервера, а не приложения
![Reverse proxy Кэш хранится на балансере Инвалидация кэша на стороне сервера, а не приложения](/_ipx/f_webp&q_80&fit_contain&s_1440x1080/imagesDir/jpg/1086630/slide-19.jpg)
Слайд 21Отдельный кэш
Кэш хранится в отдельном сервисе
Сервис кэша можно отдельно скейлить, если надо
Стек
![Отдельный кэш Кэш хранится в отдельном сервисе Сервис кэша можно отдельно скейлить,](/_ipx/f_webp&q_80&fit_contain&s_1440x1080/imagesDir/jpg/1086630/slide-20.jpg)
приложения не важен
Слайд 22Распределенный кэш
кэш хранится на уровне приложения, но шарится на несколько приложений (реплицируется
![Распределенный кэш кэш хранится на уровне приложения, но шарится на несколько приложений](/_ipx/f_webp&q_80&fit_contain&s_1440x1080/imagesDir/jpg/1086630/slide-21.jpg)
или распределяется)
Зависит от реализации в фреймворке и языке программирования
Слайд 23Distributed кэш
Распределенный кэш – если ключ не в текущем шарде, идем в
![Distributed кэш Распределенный кэш – если ключ не в текущем шарде, идем в другой инстанс](/_ipx/f_webp&q_80&fit_contain&s_1440x1080/imagesDir/jpg/1086630/slide-22.jpg)
другой инстанс
Слайд 24Replicated кэш
Репликация кэша – кэш реплицируется на все инстансы.
Быстрее распределенного, но
![Replicated кэш Репликация кэша – кэш реплицируется на все инстансы. Быстрее распределенного,](/_ipx/f_webp&q_80&fit_contain&s_1440x1080/imagesDir/jpg/1086630/slide-23.jpg)
требует больше ресурсов на инстанс
Слайд 25Sidecar кэш
кэш хранится на приложения кэша, но шарится на несколько приложений инстансов
Может
![Sidecar кэш кэш хранится на приложения кэша, но шарится на несколько приложений](/_ipx/f_webp&q_80&fit_contain&s_1440x1080/imagesDir/jpg/1086630/slide-24.jpg)
работать в режиме репликации, так и в распределенном режиме
Ниже latency
Не зависит от стека приложения
Слайд 26Sidecar кэш
https://www.unacast.com/post/high-performance-read-api-on-kubernetes-using-redis
Выделенный кластер redis-а
Side-car кластер redis-а
![Sidecar кэш https://www.unacast.com/post/high-performance-read-api-on-kubernetes-using-redis Выделенный кластер redis-а Side-car кластер redis-а](/_ipx/f_webp&q_80&fit_contain&s_1440x1080/imagesDir/jpg/1086630/slide-25.jpg)
Слайд 27Опрос
https://otus.ru/polls/6406/
![Опрос https://otus.ru/polls/6406/](/_ipx/f_webp&q_80&fit_contain&s_1440x1080/imagesDir/jpg/1086630/slide-26.jpg)