Какие существуют типы RPC в gRPC и в чем разница между Unary и Server-streaming?

Ответ

В gRPC существует четыре основных типа удаленного вызова процедур (RPC), которые определяют, как клиент и сервер обмениваются сообщениями:

  1. Unary RPC (Унарный вызов)

    • Принцип: Классическая модель "запрос-ответ". Клиент отправляет один запрос и ждет один ответ от сервера.
    • Аналогия: Похож на обычный вызов функции или стандартный HTTP GET/POST запрос.
    • .proto синтаксис:
      rpc GetUser(UserRequest) returns (UserResponse);
    • Применение: Получение данных по ID, создание или обновление записи, любая простая операция.
  2. Server-streaming RPC (Серверная потоковая передача)

    • Принцип: Клиент отправляет один запрос, а сервер в ответ присылает поток (stream) сообщений. Клиент читает из потока, пока он не будет закрыт сервером.
    • Аналогия: Подписка на новостную ленту или получение видео по частям.
    • .proto синтаксис:
      rpc ListUsers(ListRequest) returns (stream UserResponse);
    • Применение: Передача большого объема данных (например, строк из базы данных), которые можно обрабатывать по частям; рассылка уведомлений или обновлений в реальном времени.
  3. Client-streaming RPC (Клиентская потоковая передача)

    • Принцип: Клиент отправляет на сервер поток сообщений. После того как клиент завершил отправку, сервер обрабатывает весь поток и возвращает один ответ.
    • Применение: Загрузка больших файлов, отправка телеметрии или логов.
  4. 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 // Успешное завершение потока
}