← Назад к вопросам

В чем разница между server-streaming и unary в gRPC?

2.0 Middle🔥 241 комментариев
#Сетевые протоколы и API

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Разница между Unary и Server-Streaming в gRPC

Основные концепции

gRPC использует различные типы RPC для взаимодействия между клиентом и сервером. Два наиболее распространённых типа — это Unary RPC (односторонний) и Server-Streaming RPC (серверный поток).

Unary RPC (Односторонний)

Это классический подход "запрос-ответ", аналогичный традиционным HTTP REST API:

  • Клиент отправляет один запрос
  • Сервер возвращает один ответ
  • Взаимодействие завершается после получения ответа
// Пример Unary RPC в протокольном файле .proto
service UserService {
  rpc GetUser(UserRequest) returns (UserResponse);
}

// Go реализация метода
func (s *Server) GetUser(ctx context.Context, req *pb.UserRequest) (*pb.UserResponse, error) {
  // Обработка одного запроса
  user := s.findUser(req.UserId)
  
  // Возврат одного ответа
  return &pb.UserResponse{
    Id:    user.Id,
    Name:  user.Name,
    Email: user.Email,
  }, nil
}

Server-Streaming RPC (Серверный поток)

В этом подходе:

  • Клиент отправляет один запрос
  • Сервер возвращает поток (sequence) ответов
  • Сервер продолжает отправлять данные до завершения потока
// Пример Server-Streaming RPC в .proto
service LogService {
  rpc StreamLogs(LogRequest) returns (stream LogMessage);
}

// Go реализация метода
func (s *Server) StreamLogs(req *pb.LogRequest, stream pb.LogService_StreamLogsServer) error {
  // Получение логов из источника
  logs := s.logSource.GetLogs(req.Filter)
  
  // Отправка каждого лога как отдельного сообщения в потоке
  for _, log := range logs {
    // Отправка через поток
    if err := stream.Send(&pb.LogMessage{
      Timestamp: log.Timestamp,
      Content:   log.Content,
      Level:     log.Level,
    }); err != nil {
      return err
    }
    
    // Можно добавить задержку для контроля потока
    time.Sleep(100 * time.Millisecond)
  }
  
  // Завершение потока (return nil)
  return nil
}

Ключевые различия

Структура взаимодействия

ХарактеристикаUnary RPCServer-Streaming RPC
Количество запросов11
Количество ответов1Множество (поток)
ПродолжительностьКратковременноеДлительное (до завершения потока)
Состояние соединенияЗакрывается после ответаОстается открытым

Использование в Go

Клиентская сторона для Unary

// Обычный Unary вызов
resp, err := client.GetUser(ctx, &pb.UserRequest{UserId: "123"})
if err != nil {
  log.Fatal(err)
}
fmt.Printf("User: %v\n", resp.Name)

Клиентская сторона для Server-Streaming

// Server-Streaming вызов
stream, err := client.StreamLogs(ctx, &pb.LogRequest{Filter: "error"})
if err != nil {
  log.Fatal(err)
}

// Обработка потока ответов
for {
  msg, err := stream.Recv()
  if err == io.EOF {
    // Поток завершен
    break
  }
  if err != nil {
    log.Fatal(err)
  }
  fmt.Printf("Log: %s - %s\n", msg.Timestamp, msg.Content)
}

Преимущества каждого подхода

Unary RPC:

  • Простота реализации и использования
  • Низкие накладные расходы для коротких операций
  • Прямой возврат результата без управления потоком
  • Идеально для операций типа CRUD: создание, чтение, обновление, удаление

Server-Streaming RPC:

  • Эффективная передача больших данных без разбиения на множественные запросы
  • Реальная потоковая передача для живых данных (логи, мониторинг, события)
  • Снижение нагрузки на клиент — клиент обрабатывает данные по мере поступления
  • Подходит для: потоковой передачи файлов, живых данных, длительных процессов

Сравнение производительности

Latency (Время ответа)

  • Unary: Клиент получает ответ сразу после обработки сервером
  • Server-Streaming: Первый элемент потока может быть получен быстро, но полный поток требует времени

Memory Usage (Использование памяти)

  • Unary: Сервер формирует полный ответ перед отправкой
  • Server-Streaming: Сервер может отправлять данные по мере их готовности, снижая пиковое использование памяти

Network Efficiency (Эффективность сети)

  • Unary: Множественные вызовы могут создать overhead при передаче связанных данных
  • Server-Streaming: Одное соединение используется для передачи последовательности данных, что может быть более эффективно

Практические примеры применения

Когда использовать Unary:

  • Авторизация пользователя (один запрос → один ответ с токеном)
  • Получение конфигурации системы
  • Вычисление результата (калькуляция, преобразование данных)
  • Транзакции баз данных (создание, обновление записи)
// Unary для аутентификации
rpc Authenticate(AuthRequest) returns (AuthResponse);

// Использование в middleware
func authenticate(ctx context.Context, req *pb.AuthRequest) (*pb.AuthResponse, error) {
  token, err := authService.GenerateToken(req.Username, req.Password)
  return &pb.AuthResponse{Token: token, ExpiresIn: 3600}, err
}

Когда использовать Server-Streaming:

  • Передача логов системы в реальном времени
  • Мониторинг метрик (CPU, память, сеть)
  • Потоковая передача больших файлов (видео, базы данных)
  • Подписка на события (уведомления, изменения состояния)
// Server-Streaming для мониторинга
rpc MonitorSystem(MonitorRequest) returns (stream MetricData);

// Реализация мониторинга
func monitorSystem(req *pb.MonitorRequest, stream pb.MonitoringService_MonitorSystemServer) error {
  ticker := time.NewTicker(1 * time.Second)
  defer ticker.Stop()
  
  for i := 0; i < req.DurationSeconds; i++ {
    <-ticker.C
    
    metrics := collectSystemMetrics()
    if err := stream.Send(metrics); err != nil {
      return err
    }
  }
  
  return nil
}

Итоговые рекомендации

Выбор между Unary и Server-Streaming зависит от:

  1. Характера данных — единичный объект vs последовательность
  2. Требований к времени — мгновенный ответ vs продолжительная передача
  3. Эффективности ресурсов — память и сетевые затраты
  4. Семантики операции — действие vs наблюдение

В Go разработке важно правильно выбирать тип RPC, учитывая эти факторы, чтобы обеспечить оптимальную производительность и удобство использования вашего gRPC API.

В чем разница между server-streaming и unary в gRPC? | PrepBro