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