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

Для чего нужен gRPC?

2.7 Senior🔥 111 комментариев
#REST API и микросервисы

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

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

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

gRPC: Remote Procedure Call

Что такое gRPC

gRPC (gRPC Remote Procedure Call) — это **высокопроизводительный RPC фреймворк**, разработанный Google. Позволяет вызывать методы другого сервера так, как будто это локальные методы.

Зачем это нужно

Проблема REST API:

  • Текст (JSON) — медленнее, чем бинарный формат
  • HTTP/1.1 — не оптимален для микросервисов
  • Много overhead'а на сериализацию/десериализацию
  • Нет встроенного потокового взаимодействия

Решение: gRPC

  • Использует Protocol Buffers (бинарный формат) — 3-10x быстрее JSON
  • Использует HTTP/2 — множественные потоки в одном соединении
  • Двусторонняя потоковая передача (streaming)
  • Встроенная поддержка load balancing
  • Меньше latency

Установка

<dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-netty-shaded</artifactId>
    <version>1.53.0</version>
</dependency>
<dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-protobuf</artifactId>
    <version>1.53.0</version>
</dependency>
<dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-stub</artifactId>
    <version>1.53.0</version>
</dependency>

Protocol Buffers (protobuf)

Definition файл user.proto:

syntax = "proto3";

package com.example.grpc;

message User {
    int32 id = 1;
    string name = 2;
    string email = 3;
}

message GetUserRequest {
    int32 user_id = 1;
}

message CreateUserRequest {
    string name = 1;
    string email = 2;
}

service UserService {
    // Unary RPC: один запрос, один ответ
    rpc GetUser(GetUserRequest) returns (User) {}
    
    // Server streaming: один запрос, поток ответов
    rpc ListUsers(google.protobuf.Empty) returns (stream User) {}
    
    // Client streaming: поток запросов, один ответ
    rpc CreateUsers(stream CreateUserRequest) returns (google.protobuf.Empty) {}
    
    // Bidirectional streaming: два потока одновременно
    rpc Chat(stream Message) returns (stream Message) {}
}

Компилирование:

# Maven
mvn compile

# Gradle
gradle build

Генерируется код:

  • UserProto.java — сообщения
  • UserServiceGrpc.java — service interface

Реализация сервера

import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.stub.StreamObserver;

public class UserServiceImpl extends UserServiceGrpc.UserServiceImplBase {
    
    // Unary RPC
    @Override
    public void getUser(UserOuterClass.GetUserRequest request,
                        StreamObserver<UserOuterClass.User> responseObserver) {
        int userId = request.getUserId();
        
        // Получить пользователя из БД
        UserOuterClass.User user = UserOuterClass.User.newBuilder()
            .setId(userId)
            .setName("John Doe")
            .setEmail("john@example.com")
            .build();
        
        responseObserver.onNext(user);
        responseObserver.onCompleted();
    }
    
    // Server streaming
    @Override
    public void listUsers(com.google.protobuf.Empty request,
                          StreamObserver<UserOuterClass.User> responseObserver) {
        // Отправить несколько пользователей
        for (int i = 1; i <= 5; i++) {
            UserOuterClass.User user = UserOuterClass.User.newBuilder()
                .setId(i)
                .setName("User " + i)
                .setEmail("user" + i + "@example.com")
                .build();
            responseObserver.onNext(user);
        }
        responseObserver.onCompleted();
    }
    
    // Client streaming
    @Override
    public StreamObserver<UserOuterClass.CreateUserRequest> createUsers(
            StreamObserver<com.google.protobuf.Empty> responseObserver) {
        
        return new StreamObserver<UserOuterClass.CreateUserRequest>() {
            @Override
            public void onNext(UserOuterClass.CreateUserRequest request) {
                // Получение каждого запроса от клиента
                System.out.println("Creating user: " + request.getName());
            }
            
            @Override
            public void onError(Throwable t) {
                t.printStackTrace();
            }
            
            @Override
            public void onCompleted() {
                responseObserver.onNext(com.google.protobuf.Empty.getDefaultInstance());
                responseObserver.onCompleted();
            }
        };
    }
}

// Запуск сервера
public class GrpcServer {
    public static void main(String[] args) throws Exception {
        Server server = ServerBuilder.forPort(50051)
            .addService(new UserServiceImpl())
            .build();
        
        server.start();
        System.out.println("Server started on port 50051");
        server.awaitTermination();
    }
}

Клиент

import io.grpc.Channel;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;

public class GrpcClient {
    public static void main(String[] args) throws InterruptedException {
        // Подключение к серверу
        ManagedChannel channel = ManagedChannelBuilder
            .forAddress("localhost", 50051)
            .usePlaintext()
            .build();
        
        UserServiceGrpc.UserServiceBlockingStub stub = 
            UserServiceGrpc.newBlockingStub(channel);
        
        // Unary RPC
        UserOuterClass.GetUserRequest request = 
            UserOuterClass.GetUserRequest.newBuilder()
                .setUserId(1)
                .build();
        
        UserOuterClass.User user = stub.getUser(request);
        System.out.println("User: " + user.getName() + " (" + user.getEmail() + ")");
        
        // Server streaming
        Iterator<UserOuterClass.User> users = stub.listUsers(
            com.google.protobuf.Empty.getDefaultInstance()
        );
        
        while (users.hasNext()) {
            UserOuterClass.User u = users.next();
            System.out.println("User: " + u.getName());
        }
        
        // Client streaming
        StreamObserver<UserOuterClass.CreateUserRequest> requestObserver = 
            stub.createUsers(new StreamObserver<com.google.protobuf.Empty>() {
                @Override
                public void onNext(com.google.protobuf.Empty value) {}
                
                @Override
                public void onError(Throwable t) {
                    t.printStackTrace();
                }
                
                @Override
                public void onCompleted() {
                    System.out.println("All users created");
                }
            });
        
        requestObserver.onNext(UserOuterClass.CreateUserRequest.newBuilder()
            .setName("Alice")
            .setEmail("alice@example.com")
            .build());
        
        requestObserver.onNext(UserOuterClass.CreateUserRequest.newBuilder()
            .setName("Bob")
            .setEmail("bob@example.com")
            .build());
        
        requestObserver.onCompleted();
        
        // Завершение
        channel.shutdown();
        channel.awaitTermination(10, java.util.concurrent.TimeUnit.SECONDS);
    }
}

Асинхронный клиент

public class AsyncGrpcClient {
    public static void main(String[] args) {
        ManagedChannel channel = ManagedChannelBuilder
            .forAddress("localhost", 50051)
            .usePlaintext()
            .build();
        
        // Асинхронный stub
        UserServiceGrpc.UserServiceStub stub = 
            UserServiceGrpc.newStub(channel);
        
        UserOuterClass.GetUserRequest request = 
            UserOuterClass.GetUserRequest.newBuilder()
                .setUserId(1)
                .build();
        
        stub.getUser(request, new StreamObserver<UserOuterClass.User>() {
            @Override
            public void onNext(UserOuterClass.User user) {
                System.out.println("Received: " + user.getName());
            }
            
            @Override
            public void onError(Throwable t) {
                System.err.println("Error: " + t.getMessage());
            }
            
            @Override
            public void onCompleted() {
                System.out.println("Call completed");
            }
        });
        
        // Не завершай сразу
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {}
        
        channel.shutdown();
    }
}

Spring Boot интеграция

<dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-spring-boot-starter</artifactId>
    <version>2.14.0.RELEASE</version>
</dependency>
@GrpcService
public class UserServiceGrpcImpl extends UserServiceGrpc.UserServiceImplBase {
    
    @Autowired
    private UserRepository userRepository;
    
    @Override
    public void getUser(UserOuterClass.GetUserRequest request,
                        StreamObserver<UserOuterClass.User> responseObserver) {
        User dbUser = userRepository.findById(request.getUserId()).orElse(null);
        
        if (dbUser != null) {
            UserOuterClass.User grpcUser = UserOuterClass.User.newBuilder()
                .setId(dbUser.getId())
                .setName(dbUser.getName())
                .setEmail(dbUser.getEmail())
                .build();
            
            responseObserver.onNext(grpcUser);
        }
        
        responseObserver.onCompleted();
    }
}

Конфигурация:

grpc.server.port=50051

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

  1. Микросервисная архитектура — общение между сервисами
  2. Высокая нагрузка — нужна максимальная производительность
  3. Real-time приложения — streaming данные
  4. IoT и мобильные — мало bandwidth
  5. Внутренний API — не для публичных API

Преимущества vs REST

┌────────────────┬──────────┬─────────┐
│ Параметр       │ gRPC     │ REST    │
├────────────────┼──────────┼─────────┤
│ Сериализация   │ 3-10x    │ 1x      │
│ Размер         │ 7x ↓     │ -       │
│ Latency        │ 7x ↓     │ -       │
│ Streaming      │ ✓        │ ✗       │
│ Простота       │ Средняя  │ Высокая │
│ Browser        │ ✗        │ ✓       │
└────────────────┴──────────┴─────────┘

Недостатки

  • Не работает напрямую в browser'е (нужен gateway)
  • Крутая кривая обучения
  • Отладка сложнее, чем REST
  • Protocol Buffers нужно учить
  • Менее популярен чем REST

Инструменты для тестирования

# Evans — интерактивный gRPC клиент
evans -r localhost:50051

# grpcurl — аналог curl для gRPC
grpcurl -plaintext localhost:50051 list
grpcurl -plaintext -d '{"user_id": 1}' localhost:50051 com.example.grpc.UserService.GetUser

Вывод

gRPC — это отличный выбор для микросервисов и высоконагруженных систем, но не замена REST. Для публичных API лучше REST, для внутреннего взаимодействия — gRPC.