The current architecture of Prom.ua is built on microservices and GraphQL API, but it was not always like that. In this talk, I'll tell you how far we've come and how we've made using graphs in a microservice architecture convenient and simple. I will talk about the problems we faced and how we overcame them, made our development process more accessible, deployments faster, and the remains of the monolith less loaded.
"Leadership, Soft Skills, and Personality Types for IT teams", Sergiy Tytenko
"Distributed graphs and microservices in Prom.ua", Maksym Kindritskyi
1.
2. ● Кіндріцький Максим
● 6 останніх років у Prom.ua (EVO)
● Team Lead команд User Engagement/Feedback Ecosystem
Привіт
3. Prom.ua
● Більше 50 тис. підприємців
● 3 млн. унікальних юзерів за добу
● Щодня більше ніж 70 тисяч покупок
● Python backend
● GraphQL > 7 років
● 10k rps на бекенди (моноліт + сервіси)
4. 1. GraphQL + мікросервіси в Prom.ua
2. Проблеми розподіленого GraphQL на gRPC/REST
3. Як зробити розподілений GraphQL на Apollo Federation
4. Які плюси використання Apollo Federation
План
5. ● Хто вже використовує GraphQL у себе в проекті
● Що робити з GraphQL якщо ви плануєте розділяти моноліт на сервіси
● Ви же маєте мікросервіси, але граф ще живе в моноліті
● Вас не влаштовує граф побудований на gRPC
● Ви лише плануєте впроваджувати GraphQL в мікросервіси
Кому це може бути цікаво?
7. ● Мова запитів + рантайм(сервери написані на більшості популярних мов)
● Специфікація https://spec.graphql.org
● Python фреймворки
○ graphene
○ strawberry
○ ariadne
○ hiku
Що таке GraphQL ?
8. Що таке GraphQL ?
query GetUser {
user(id: 1) {
id
name
dateCreated
}
}
@strawberry.type
class User:
id: int
name: str
date_created: datetime
@strawberry.type
class Query:
@strawberry.field
def user(self, id: int) -> User | None:
return info.context.users.get(id)
13. ● Весь граф відгуків все ще знаходиться в моноліті
● Час на розробку фічей збільшився
● Час деплою все ще обмежений найповільнішим сервісом (моноліт)
● Трафік відгуків все ще проходить через моноліт
● Куча серіалізацій - json > dict > protobuf > dataclass > dict > json
● Знову over-fetching
Проблеми з якими ми зіткнулись
14. Serialize/deserialize pain
@dataclass
class Opinion:
id: int
date_created: datetime
def handle_request(body: str) -> str:
data = json.loads(body)
pb = api.get_opinion(data)
dcl = Opinion(id=pb.id, date_created=pb.date_created.ToDatetime())
result = db.to_dict()
return json.dumps(result)
16. Нам потрібно рішення яке дозволить:
● Перенести всі потрібні типи з моноліту в мікросервіс
● Зміни в типах і бізнес-логіці в мають бути лише в мікросервісі
● Прибрати частину трафіку з моноліту - щоб дані діставались з
потрібного сервісу
● Деплоїти зміни по нашим функціоналам незалежно від моноліту
● Деплоїти зміни значно швидше
Як вирішити всі ці проблеми ?
18. ● З’являється gateway, який вирішує в який сервіс за даними сходити
● Щоб gateway знав про всі сервіси йому потрібна суперграф схема
● Отримати суперграф схему можна скомпозувавши схеми всіх сервісів
● Схема зберігається в GraphQL Registry, до якого gateway має доступ
Apollo Federation
Технологія, яка дозволяє композувати багато сервісів що мають графове
апі в одну велику схему - так званий суперграф.
Як це працює?
21. ● В сервісі відгуків з’являється /graphql апі і всі його типи тепер тут
○ Тепер типи належать мікросервісу
Що змінилось ?
● Роутер за даними про відгуки ходить в мікросервіс
○ Ми прибрали частину трафіку з моноліту
● Пишемо код і деплоїмо лише сервіс відгуків, моноліт не чіпаємо
○ Зміни по функціоналу відгуків деплояться незалежно від моноліту і це
швидко
● Роутер запитає в мікросервісу відгуків лише ті поля які запитав клієнт
○ Більше немає server over-fetching
22. ● Кожен GraphQL сервіс це Apollo Federation Subgraph
● Всі сервіси пушають свою схему в GraphQL Registry
● GraphQL Registry будує supergraph схему
● Роутер стартує з supergraph схемою
● Всі запити проходять через роутер
Як це працює ?
23. Subgraph сервер
@strawberry.type
class Product:
id: int
name: str
@strawberry.type
class Query:
products: list[Product] = strawberry.federation.field(resolver=get_products)
schema = strawberry.federation.Schema(query=Query, enable_federation_v2=True)
24. Apollo Federation Entities
● Entities - це типи, що знаходяться в одному сервісі і можуть
використовуватися в інших сервісах
● Можна розширювати тип одного сервісу в іншому сервісі
25. GraphQL схема в моноліті
type Product {
id: Int!
name: String!
}
type Product {
id: Int!
name: String!
opinions: [Opinion!]!
}
26. GraphQL схема в сервісі відгуків
stub type
type Opinion {
id: Int!
rating: Int!
productId: Int!
}
type Product @key("id") {
id: Int!
opinions: [Opinion!]!
}
27. Код в моноліті
@strawberry.federation.type(keys=["id"])
class Product:
id: int
name: str
@classmethod
def resolve_reference(cls, id: strawberry.ID, info: Info):
product = info.context.products.get(int(id))
return Product(id=product.id, name=product.name)
28. Код в сервісі відгуків
@strawberry.type
class Opinion:
id: int
product_id: int
@strawberry.federation.type(keys=[“id”])
class Product:
id: int
@classmethod
def resolve_reference(cls, id: strawberry.ID, info: Info):
opinions = info.context.opinions.get_by_product(int(id))
return Product(id=id, opinions=opinions)
39. Висновки
В результаті ми не просто вирішили наші проблеми, а і отримали навіть
деякі бенефіти:
● Ми стали релізити фічі швидше ніж при монолітній архітектурі
● Моноліту стало трошки легше, бо ми зняли з нього частину трафіку
● Менший асинхронний сервер + окрема менша база - функціонал
швидший
● В GraphQL Registry ми тепер можемо відслідковувати поля, що більше
не використовуються і видаляти їх безпечно
● Ми перевели декілька сервісів теж на Apollo Federation
40. Корисні посилання Apollo Federation
● Apollo Federation Spec - https://www.apollographql.com/docs/federation/
● Apollo Router - https://www.apollographql.com/docs/router
● Навчальні матеріали по Federation -
https://www.apollographql.com/tutorials/browse?categories=federation
● Hive GraphQL Registry - https://the-guild.dev/graphql/hive/docs
● How Netflix uses federation (big scale) -
https://www.infoq.com/presentations/netflix-scaling-graphql