Фреймворк gRPC всё чаще используется в микросервисных системах, но при тестировании выясняется: готовых инструментов и примеров заглушек заметно меньше, чем для REST. В этой статье разберём, как с нуля поднять gRPC-заглушку на Java: от .proto-файлов и кода до проверки через Postman и нагрузочных тестов.
Что такое gRPC и как он работает
gRPC (Google Remote Procedure Call) — это фреймворк для удаленных вызовов процедур, разработанный компанией Google. Он позволяет одному сервису вызывать методы другого сервиса так, как будто это обычные локальные функции.
За счет строгих контрактов и бинарного протокола gRPC дает высокую производительность и удобную интеграцию между сервисами — именно поэтому он часто используется в микросервисной архитектуре.
Одно из ключевых преимуществ gRPC — использование Protocol Buffers (Protobuf). Protobuf — это бинарный формат сериализации данных, разработанный Google. Он позволяет описать структуру сообщений в proto-файле и автоматически сгенерировать клиентский и серверный код для разных языков программирования.
По сути, proto-файл — это контракт взаимодействия между сервисами. В нем определяется:
- какие сообщения существуют (структуры данных);
- какие поля входят в каждое сообщение;
- какие RPC-методы доступны;
- какие аргументы они принимают;
- какие ответы возвращают.
На основе proto-файла генерируется типобезопасный (type-safe) код клиента и сервера. Поэтому при разработке заглушек proto выступает единственным источником правды о формате запросов и ответов — и именно от него нужно отталкиваться при реализации и тестировании.
Если проводить аналогию, Protobuf можно рассматривать как более быстрый и компактный аналог JSON или XML, но в бинарном формате. Такой подход делает передачу данных значительно эффективнее — особенно при машинной обработке и сетевых взаимодействиях.
Клиент и сервер взаимодействуют в рамках gRPC по следующему принципу:

- Клиентское приложение вызывает gRPC-метод — для разработчика это выглядит как обычный вызов локальной функции.
- Аргументы RPC сериализуются в бинарный формат Protocol Buffers, в результате чего формируется компактный двоичный payload.
- gRPC-runtime упаковывает payload в gRPC-фреймы поверх HTTP/2 (заголовки, метаданные, служебные поля) и передает их транспортному уровню.
- Транспортный слой клиента отправляет HTTP/2-запрос на сервер.
- На стороне сервера транспортный уровень принимает запрос и передает gRPC-фреймы в gRPC-runtime.
- gRPC-runtime выполняет десериализацию payload обратно в объекты языка программирования (Java, Go, Python и др.). После этого данные готовы для обработки серверным приложением.
- Серверное приложение выполняет RPC-метод, формирует результат и возвращает ответ, который проходит тот же путь обратно к клиенту.
Для чего нужна gRPC-заглушка
gRPC-заглушка — это упрощенная реализация сервиса, которая повторяет его контракт, но содержит минимальную или тестовую бизнес-логику. Такая заглушка позволяет эмулировать поведение реального сервиса и использовать его в тестировании.
На практике заглушки чаще всего применяются в нескольких сценариях:
Тестирование клиента без готового сервера
Иногда клиентская часть уже разрабатывается или тестируется, а сервер еще не готов или нестабилен. Заглушка позволяет продолжать тестирование, используя тот же .proto-контракт, что и у будущего сервиса.
Изоляция тестируемого сервиса
При интеграционном тестировании важно исключить влияние внешних зависимостей. Заглушка позволяет контролировать ответы сервиса и воспроизводить нужные сценарии — например, ошибки, задержки или нестандартные ответы.
Отладка интеграций
С помощью заглушки удобно проверять корректность формирования запросов и обработки ответов. Это полезно при работе с бинарным протоколом gRPC, где ошибки сложнее диагностировать вручную.
Нагрузочное тестирование
Заглушки часто используются как тестовые стенды для нагрузочного тестирования. Они позволяют проверить производительность клиента или промежуточных сервисов без необходимости нагружать реальную систему.
Как создать gRPC-заглушку
Узнаем данные о контракте
Первым делом попросите у разработчика .proto-файлы для тех методов, которые вы планируете заглушать. Именно из них мы узнаем контракт: какие сервисы и RPC-методы есть, какие сообщения они принимают и возвращают.
Пример .proto может выглядеть так:
syntax = "proto3";
option java_multiple_files = true;
option java_package = "org.example";
service SynapseAvailableSaldo {
rpc FindRegister (SaldoRq) returns (AvailableSaldoRs);
rpc AvailBalanceRegisterExt (SaldoRq) returns (stream AvailableSaldoRs);
}
message AvailableSaldoRs {
string Payload = 1;
}
message SaldoRq {
string Payload = 1;
}
Разбираем proto-файл на составные
Первым делом попросите у разработчика .proto-файлы для тех методов, которые вы планируете заглушать. Именно из них мы узнаем контракт: какие сервисы и RPC-методы есть, какие сообщения они принимают и возвращают.
Пример .proto может выглядеть так:
syntax = "proto3";
option java_multiple_files = true;
option java_package = "org.example";
service SynapseAvailableSaldo {
rpc FindRegister (SaldoRq) returns (AvailableSaldoRs);
rpc AvailBalanceRegisterExt (SaldoRq) returns (stream AvailableSaldoRs);
}
message AvailableSaldoRs {
string Payload = 1;
}
message SaldoRq {
string Payload = 1;
}
Разбираем proto-файл на составные
Дальше разберемся, из чего он состоит — по порядку.
syntax = "proto3"— версия Protocol Buffers. На практике это «дефолтный стандарт» для современных gRPC-сервисов.-
option java_multiple_files = true— с этой опцией Java-код, сгенерированный из .proto, будет разбит на несколько классов/файлов, а не собран в один «монолит». Удобнее навигировать и поддерживать. -
service SynapseAvailableSaldo— имя gRPC-сервиса. Именно от него будет зависеть, какой stub и какой base class сгенерируются для клиента/сервера. -
rpc FindRegister (SaldoRq) returns (AvailableSaldoRs)— описание RPC-метода:FindRegister— название метода,SaldoRq— тип входного сообщения (запрос),AvailableSaldoRs— тип выходного сообщения (ответ).
Сообщения (message), которые мы передаем в методы и получаем в ответ, описаны ниже. Иногда разработчики выносят их в отдельные .proto-файлы и подключают через import, но по смыслу это ничего не меняет: всё, что нужно для заглушки, всегда лежит в контракте .proto.
message AvailableSaldoRs {
string Payload = 1;
}
message SaldoRq {
string Payload = 1;
}
Это структуры сообщений, которые используются в RPC-методах — именно их клиент будет отправлять на сервер и получать в ответ.
В примере используется тип string, но Protocol Buffers поддерживает разные типы данных: double, bool, int32, int64, bytes и другие. Кроме того, сообщения могут содержать вложенные структуры, повторяющиеся поля (repeated) и перечисления (enum).
Отдельно стоит обратить внимание на запись вида:
string Payload = 1;
Число 1 — это уникальный идентификатор поля в структуре сообщения. Это не индекс и не порядковый номер, а постоянный тег поля, который используется при бинарной сериализации. Полей может быть сколько угодно, главное — чтобы их номера не повторялись внутри одного сообщения.
После того как у нас появились proto-файлы, основная работа уже сделана: большая часть кода будет автоматически сгенерирована при компиляции. Нам останется только реализовать серверную часть заглушки и задать нужное поведение методов.
Готовим файл с зависимостями
Для проекта нам понадобится pom.xml, где будут описаны зависимости и плагины.Минимально нам нужны три «опорных» компонтента:
-
grpc-spring-boot-starter— чтобы поднять gRPC-сервер на Spring Boot и быстро стартовать заглушку без ручной настройки Netty/каналов. -
os-maven-plugin— чтобы Maven корректно определил ОС и подтянул подходящий бинарникprotoc. -
protobuf-maven-plugin— тот самый плагин, который занимается кодогенерацией на основе.proto-файлов.
Разбираем конфигурацию protobuf-maven-plugin:
-
<protoSourceRoot>— путь к вашим.proto-файлам. Обычно это что-то вродеsrc/main/proto. -
<outputDirectory>— куда Maven будет складывать сгенерированные классы (как правило, внутрьtarget/generated-sources/...). -
<protocArtifact>— версияprotoc(компилятора.proto) + привязка кos-maven-plugin, чтобы автоматически выбрать бинарник под вашу ОС. -
<pluginArtifact>— gRPC-плагин дляprotoc, который генерирует gRPC Java-код (stubs, base-классы сервисов и т.д.). -
<clearOutputDirectory>— нужно ли очищать директорию генерации перед стартом.true— чистит папку перед генерацией (чаще всего это нормально и даже полезно),false— оставляет старые файлы (может привести к мусору и конфликтам при изменении контрактов).
В секции <goals> обычно важно указать два шага:
-
compile— генерирует protobuf-классы (модели сообщений). -
compile-custom— генерирует gRPC-код (стабы и базовые классы сервисов).
Это принципиально: ваш класс с бизнес-логикой заглушки будет наследоваться от сгенерированного base-класса сервиса. Если после mvn clean (или очистки target) код не будет заново сгенерирован, проект просто перестанет собираться и запускаться — потому что базовый класс исчезнет.
❗ Заглушка в примерах далее разрабатывалась на Java с использованием IntelliJ IDEA.
Компилируем проект
После того как вы получили .proto-файлы и добавили необходимые зависимости в pom.xml, проект нужно скомпилировать, чтобы сгенерировать код. В указанной в конфигурации protobuf-maven-plugin директории должны появиться новые классы — именно они будут использоваться как основа для реализации заглушки.
У IntelliJ IDEA есть особенность: даже после успешной сборки IDE иногда не видит сгенерированные классы. В этом случае нужно вручную обновить структуру проекта:
ПКМ по проекту → Maven → Generate Sources and Update Folders
После этого IntelliJ подхватит сгенерированные файлы, и проект начнет корректно собираться и запускаться.

Описываем логику
Дальше переходим к самому интересному — логике заглушки. В gRPC есть четыре типа вызовов (в зависимости от того, кто и сколько сообщений отправляет: клиент, сервер или оба). Ниже в таблице — все варианты.

Дальше разберем два самых популярных сценария взаимодействия клиента и сервера в gRPC: Unary call и ServerStreaming.
Шаг 1. Создаем реализацию сервиса
Создаем класс заглушки и наследуемся от сгенерированного base-класса сервиса. В моем примере это:
SynapseAvailableSaldoGrpc.SynapseAvailableSaldoImplBase
У вас имя будет другим — зависит от названия service в .proto. Главное правило простое: базовый класс почти всегда заканчивается на ImplBase. Он генерируется автоматически плагином на основе .proto-контракта.
Шаг 2. Переопределяем RPC-методы
Далее переопределяем методы, которые описаны в .proto. В нашем случае это:
-
FindRegister— Unary call (один запрос → один ответ) -
AvailBalanceRegisterExt— Server Streaming (один запрос → поток ответов)
Вызовы Unary call
В примере логика намеренно максимально простая: нужно было сформировать ответ из строки (payload) и отдать его клиенту — ровно это и делает код внутри переопределенных методов.
package org.example.service;
import io.grpc.stub.StreamObserver;
import net.devh.boot.grpc.server.service.GrpcService;
import org.example.AvailableSaldoRs;
import org.example.SaldoRq;
import org.example.SynapseAvailableSaldoGrpc;
@GrpcService
public class GrpcMock extends SynapseAvailableSaldoGrpc.SynapseAvailableSaldoImplBase {
@Override
public void findRegister(SaldoRq request, StreamObserver<AvailableSaldoRs> responseObserver) {
String rs = "123";
AvailableSaldoRs response = AvailableSaldoRs.newBuilder()
.setPayload(rs)
.build();
responseObserver.onNext(response);
responseObserver.onCompleted();
}
}
Далее нам нужно запустить нашу заглушку – для этого понадобится метод main.
package org.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class GrpcApp {
public static void main(String[] args) {
SpringApplication.run(GrpcApp.class, args);
}
}
По умолчанию gRPC-заглушка запускается на порту 9090, но при необходимости порт можно изменить в файле application.properties:
grpc.server.port=9090
Вызовы Server Streaming
В случае Server Streaming всё устроено практически так же, как и в Unary-вызовах. Главное отличие видно уже в .proto-файле:
rpc AvailBalanceRegisterExt (SaldoRq) returns (stream AvailableSaldoRs)
Здесь появляется ключевое словоstream, которое означает, что сервер будет отправлять несколько сообщений в ответ на один запрос клиента.
Реализация метода при этом меняется незначительно — вместо одного вызова onNext() сервер может вызывать его несколько раз, отправляя поток сообщений, и завершает передачу методом onCompleted().
В качестве примера приведем простую реализацию: сервер инициализирует массив значений и отправляет клиенту каждый элемент по очереди с паузой в одну секунду. Такой пример хорошо демонстрирует сам принцип работы Server Streaming и удобен для тестирования клиента.
@Override
public void availBalanceRegisterExt(SaldoRq request, StreamObserver<AvailableSaldoRs> responseObserver) {
String[] rs = {"1", "2", "3"};
for (int i = 0; i < rs.length; i++) {
AvailableSaldoRs response = AvailableSaldoRs.newBuilder()
.setPayload(rs[i])
.build();
responseObserver.onNext(response);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
responseObserver.onCompleted();
}
Как это будет выглядеть при дебаге в Postman:

На скриншоте видно, что выбран метод AvailBalanceRegisterExt, в котором реализован Server Streaming — сервер отправляет поток ответов на один запрос клиента.
Также можно заметить, что ответы приходят с интервалом примерно в одну секунду — это соответствует логике метода, где между отправками используется Thread.sleep(1000). Сообщения продолжают поступать до тех пор, пока сервер не завершит поток вызовом onCompleted().
В gRPC стриминг может происходить в нескольких вариантах взаимодействия:
- клиент → сервер (Client Streaming)
- сервер → клиент (Server Streaming)
- клиент ↔ сервер (Bidirectional Streaming)
Во всех случаях в .proto-файле для методов со стримингом используется ключевое слово stream — либо в типе запроса, либо в типе ответа (или в обоих сразу).
Проверка работы заглушки
Для тестирования gRPC-методов можно использовать привычные инструменты — например Postman (также подойдут Insomnia или BloomRPC).
Чтобы проверить метод:
- Создайте новый запрос и выберите тип gRPC.
- Загрузите ваш
.proto-файл в Postman. - После загрузки контракта станет доступен список сервисов и методов.
- Выберите нужный RPC-метод и сформируйте тело запроса.
- Отправьте запрос и проверьте ответ от заглушки.
После загрузки .proto-файла Postman автоматически подсказывает структуру запроса, что удобно при первичной проверке заглушки и отладке контрактов.

В конечном итоге финальный запрос может выглядеть таким образом:

Прокидывая наш запрос, мы получаем ответ, который прописали в методе:

Нагрузочное тестирование gRPC
Для нагрузочного тестирования на проектах часто используют JMeter, однако у него есть ограничение: в открытом доступе практически нет gRPC-плагинов, которые полноценно поддерживают все виды стриминга. Большинство доступных решений работают только с Unary-вызовами.
Полная поддержка всех четырех типов gRPC-вызовов есть в инструменте нагрузочного тестирования «Бумк». С его помощью можно собрать и запустить тесты для любого типа gRPC-запросов: Unary, Server Streaming, ClientStreaming и Bidirectional Streaming.
Чтобы создать тест с использованием gRPC:
- Перейдите по ссылке: https://platform.pflb.us/authorize
- Создайте новый тест:
- нажмите New test (1)
- выберите протокол gRPC (2)
- Загрузите
.proto-файл — из него автоматически подтянутся доступные сервисы и методы.
После этого можно настроить параметры нагрузки и запустить тестирование gRPC-заглушки или реального сервиса.

После добавления .proto-файла закройте окно импорта и переходите к созданию use case.
Затем откройте созданный use case в режиме редактирования, добавьте шаг gRPC Request и переходите к настройке запроса.
В параметрах запроса необходимо указать основные настройки:
- Тип вызова (Request type) — выбираете нужный вид gRPC-запроса (Unary, Server Streaming, Client Streaming или Bidirectional Streaming).
- Host — адрес gRPC-сервера (например,
localhost:9090для локальной заглушки). - Method — RPC-метод, который будет вызываться (подтягивается из загруженного
.proto-файла).
После этого можно заполнить тело запроса и переходить к настройке сценария нагрузки.


Далее настройка запросов может быть весьма мобильна для ваших нужд, можете добавить необходимые хедеры, тело запроса, также есть возможность настройки извлечения данных из ответа на запрос, добавление assertion и таймеров

Дальнейшая настройка запросов достаточно гибкая и зависит от ваших задач. В конфигурации gRPC-запроса можно:
- добавить необходимые headers и метаданные,
- настроить тело запроса,
- задать правила извлечения данных из ответа (например, для передачи значений в следующие шаги),
- добавить assertions для проверки корректности ответов,
- настроить таймеры и параметры нагрузки.
Рекомендую отдельно ознакомиться с подробным гайдом по использованию «Бумк» — инструмент довольно мощный и может значительно упростить процесс нагрузочного тестирования gRPC-сервисов.
На этом разработку заглушки можно считать завершенной. Теперь, если вам потребуется протестировать сервис с использованием gRPC — будь то функциональное или нагрузочное тестирование — у вас будет готовый инструмент и понимание того, как его быстро развернуть и настроить.