Ответ
В 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 // Успешное завершение потока
}