Ответ
В gRPC существует четыре основных типа удаленного вызова процедур (RPC), которые определяют, как клиент и сервер обмениваются сообщениями:
-
Unary RPC (Унарный вызов)
- Принцип: Классическая модель "запрос-ответ". Клиент отправляет один запрос и ждет один ответ от сервера.
- Аналогия: Похож на обычный вызов функции или стандартный HTTP GET/POST запрос.
.protoсинтаксис:rpc GetUser(UserRequest) returns (UserResponse);- Применение: Получение данных по ID, создание или обновление записи, любая простая операция.
-
Server-streaming RPC (Серверная потоковая передача)
- Принцип: Клиент отправляет один запрос, а сервер в ответ присылает поток (stream) сообщений. Клиент читает из потока, пока он не будет закрыт сервером.
- Аналогия: Подписка на новостную ленту или получение видео по частям.
.protoсинтаксис:rpc ListUsers(ListRequest) returns (stream UserResponse);- Применение: Передача большого объема данных (например, строк из базы данных), которые можно обрабатывать по частям; рассылка уведомлений или обновлений в реальном времени.
-
Client-streaming RPC (Клиентская потоковая передача)
- Принцип: Клиент отправляет на сервер поток сообщений. После того как клиент завершил отправку, сервер обрабатывает весь поток и возвращает один ответ.
- Применение: Загрузка больших файлов, отправка телеметрии или логов.
-
Bidirectional-streaming RPC (Двунаправленная потоковая передача)
- Принцип: Клиент и сервер могут отправлять друг другу сообщения в виде потоков независимо друг от друга.
- Применение: Создание интерактивных чатов, онлайн-игры, системы совместного редактирования документов.
Сравнительная таблица (Unary vs Server-streaming)
| Характеристика | Unary RPC | Server-streaming RPC |
|---|---|---|
| Запросы клиента | 1 | 1 |
| Ответы сервера | 1 | Много (поток) |
| Направление потока | Нет | Сервер → Клиент |
| Основное применение | Простые, атомарные операции | Передача больших объемов данных, нотификации, подписки |
Пример Server-streaming на Go (серверная часть):
// ListUsers отправляет поток пользователей в ответ на один запрос
func (s *Server) ListUsers(req *pb.ListRequest, stream pb.UserService_ListUsersServer) error {
// Предположим, users - это срез с данными
for _, user := range users {
// Отправляем каждого пользователя как отдельное сообщение в потоке
if err := stream.Send(&pb.UserResponse{User: user}); err != nil {
return err // Ошибка при отправке, например, если клиент отключился
}
}
return nil // Успешное завершение потока
} Ответ 18+ 🔞
Так, слушай, разбираем эту вашу gRPC-хуйню. Четыре типа вызовов, как четыре сорта пива в баре — каждый для своего случая, а если перепутаешь, будет пиздец.
1. Unary RPC (Прямой вызов, как в лоб) Тут всё просто, как три копейки. Клиент пингует сервер одним сообщением и ждёт, пока тот не плюнет одним ответом обратно. Как в старом добром HTTP, только быстрее. Идеально, когда нужно просто спросить «как дела?» и получить «нормально».
rpc GetUser(UserRequest) returns (UserResponse);
2. Server-streaming RPC (Сервер — болтун) Клиент один раз пишет «привет», а сервер в ответ начинает нести хуйню непрерывным потоком, пока не закончится или клиент не скажет «хватит, заебал». Представь, что ты спросил у друга «как дела?», а он начал рассказывать про работу, тёщу и пробки — вот это оно.
rpc ListUsers(ListRequest) returns (stream UserResponse);
3. Client-streaming RPC (Клиент — зануда) Полная противоположность. Теперь клиент долбит сервер потоком данных, а сервер терпеливо слушает, кивает и в конце выдаёт один финальный вердикт. Как если бы ты час жаловался на жизнь психологу, а он под конец сказал бы «ну, держись».
4. Bidirectional-streaming RPC (Полный бардак) А это уже ёперный театр. Обе стороны могут нести ахинею одновременно и независимо. Как разговор двух пьяных философов в баре — оба говорят, никто не слушает, но вроде как диалог. Используется для чатов или игр, где все друг другу должны срочно сообщить какую-то хуйню.
Unary vs Server-streaming: кто на ком стоял?
| Параметр | Unary (Простой) | Server-streaming (Болтливый) |
|---|---|---|
| Клиент стучит | Один раз и ждёт | Один раз и ждёт (но потом жалеет) |
| Сервер палит | Один ответ и свободен | Поток ответов, пока не кончатся аргументы |
| Куда течёт | Никуда, статика | Из сервера в клиента, как из ведра |
| Зачем это | Для простых вопросов вроде «сколько время?» | Чтобы завалить клиента данными или спамить уведомлениями |
Пример Server-streaming на Go (смотри, не обосрись)
Вот как сервер превращается в болтуна:
// ListUsers — функция, где сервер начинает трепаться без остановки
func (s *Server) ListUsers(req *pb.ListRequest, stream pb.UserService_ListUsersServer) error {
// Допустим, users — это куча пользователей, которых надо вывалить на клиента
for _, user := range users {
// Шлём каждого юзера как отдельное сообщение. По одному, без спешки.
if err := stream.Send(&pb.UserResponse{User: user}); err != nil {
return err // Если клиент сдох или отключился — закругляемся
}
}
return nil // Всё, поток кончился, можно идти пить чай
}
Вот и вся магия. Главное — не перепутай типы, а то вместо одного ответа получишь поток охуенных подробностей, которые тебе нахуй не сдались.