gRPC в Kotlin: когда использовать, чтобы не пожалеть
Представьте: вы пишете микросервисы на Kotlin, всё работает, но запросы между ними становятся тормознутыми. REST API, который вы так любили, начинает раздражать — слишком много сериализации, слишком много HTTP-оверхеда. Вы начинаете гуглить "быстрый способ общения между микросервисами" и натыкаетесь на gRPC. И тут же возникает вопрос: "А не перебор ли это? Может, оставить как есть?"
Давайте разбираться. gRPC в Kotlin — это не просто "ещё один протокол". Это совершенно другой подход к межсервисному взаимодействию. Он быстрее, строже и мощнее REST, но и сложнее. В этой статье я расскажу, когда gRPC — ваш лучший друг, а когда лучше обойтись старым добрым REST. И, конечно, покажу реальный код на Kotlin.
Что такое gRPC и почему он так популярен в мире Kotlin?
gRPC — это фреймворк удалённого вызова процедур (RPC) от Google. Он использует Protocol Buffers (protobuf) для сериализации данных и HTTP/2 для транспорта. Если REST — это как отправка писем по почте (каждый запрос — отдельное письмо), то gRPC — это как прямой телефонный разговор: быстрый, двусторонний и с минимальными задержками.
Для Kotlin-разработчиков gRPC особенно интересен, потому что:
- Kotlin корутины отлично сочетаются с асинхронными стримами gRPC.
- Строгая типизация protobuf — меньше ошибок на этапе выполнения.
- Поддержка двунаправленного стриминга — идеально для чатов, игр и real-time приложений.
Но давайте честно: gRPC не нужен везде. И сейчас я покажу, как понять, нужен ли он именно вашему проекту.
Когда использовать gRPC в Kotlin: 5 железобетонных сценариев
1. Микросервисы с высокими требованиями к производительности
Если ваши микросервисы общаются десятками тысяч запросов в секунду, gRPC выигрывает у REST в 2-5 раз по скорости. Всё благодаря бинарному формату protobuf и мультиплексированию HTTP/2.
Пример: у вас есть сервис рекомендаций на Kotlin, который должен за миллисекунды вернуть персонализированный контент. REST с JSON будет "тяжеловат" — каждый запрос содержит имена полей, скобки и прочий текстовый мусор. Protobuf передаёт только числа и строки без лишней обёртки.
// Определение в .proto файле
service Recommender {
rpc GetRecommendations (UserRequest) returns (RecommendationsResponse);
}
message UserRequest {
string user_id = 1;
int32 max_results = 2;
}
message RecommendationsResponse {
repeated Item items = 1;
}
Код на Kotlin-клиенте будет выглядеть лаконично:
val stub = RecommenderGrpc.newBlockingStub(channel)
val request = UserRequest.newBuilder()
.setUserId("user_123")
.setMaxResults(10)
.build()
val response = stub.getRecommendations(request)
println("Получено ${response.itemsCount} рекомендаций")
2. Real-time стриминг данных
gRPC поддерживает четыре вида стриминга: серверный, клиентский, двунаправленный и простой unary. В Kotlin это особенно удобно с корутинами — вы можете стримить данные как Flow.
Пример: сервер логов, который отправляет новые записи клиенту в реальном времени.
// Серверная часть на Kotlin
class LogServiceImpl : LogServiceGrpcKt.LogServiceCoroutineImplBase() {
override fun streamLogs(request: LogRequest): Flow<LogEntry> = flow {
val logSource = getLogSource(request.logType)
logSource.collect { logEntry ->
emit(logEntry)
}
}
}
// Клиентская часть
val stub = LogServiceGrpcKt.LogServiceCoroutineStub(channel)
stub.streamLogs(LogRequest.newBuilder().setLogType("error").build())
.collect { log ->
println("Новый лог: ${log.message}")
}
Попробуйте сделать то же самое с REST и WebSocket — получится громоздко и медленно.
3. Полиглот-системы (разные языки программирования)
Если одни сервисы написаны на Kotlin, другие на Python, третьи на Go — gRPC с protobuf обеспечивает единый контракт. Вы описываете структуры данных один раз в .proto файле, а кодогенератор создаёт классы для всех языков.
Это особенно актуально для образовательных проектов, где вы можете иметь Kotlin-бэкенд и Python-скрипты для анализа данных.
4. Внутренние API с жёсткими контрактами
Когда API используется только внутри вашей системы, gRPC даёт строгую типизацию и версионирование через package в protobuf. Вы не сможете случайно передать строку вместо числа — компилятор отругает.
Это резко контрастирует с REST, где вы можете получить "500" из-за того, что фронтенд отправил "age: 'twenty'" вместо "age: 20".
5. Мобильные и IoT-приложения (с оговорками)
gRPC-Web позволяет использовать gRPC из браузера. Для мобильных приложений на Android (Kotlin) это тоже хороший выбор — protobuf компактнее JSON, что экономит трафик.
Но тут есть нюанс: если ваше приложение работает через публичный интернет с нестабильным соединением, gRPC может быть избыточным. Для публичных API чаще выбирают REST.
Когда лучше НЕ использовать gRPC в Kotlin
1. Публичные API для внешних разработчиков
Если ваш API будут использовать сторонние разработчики, gRPC создаст лишние сложности. Им придётся генерировать клиенты, разбираться с protobuf, настраивать HTTP/2. REST с JSON — это стандарт де-факто, его понимают все.
2. Простые CRUD-приложения
Если ваше приложение — это просто "создать-прочитать-обновить-удалить", gRPC не даст преимуществ. REST справится быстрее и проще. Не усложняйте архитектуру, если нет реальной нагрузки.
3. Проекты с маленькой командой
gRPC требует дополнительного инструментария: настройка protoc, кодогенерация, понимание HTTP/2. Если в команде 2-3 человека и дедлайн горит, лучше остаться на REST — это быстрее в разработке.
Практический пример: gRPC сервер на Kotlin с корутинами
Давайте соберём простой, но рабочий пример. У нас будет сервис, который принимает запрос с именем пользователя и возвращает приветствие.
Шаг 1. Определяем .proto файл
syntax = "proto3";
package greeter;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}