Ваши сервисы наверняка говорят между собой на gRPC — а ваш инструмент нагрузочного тестирования, скорее всего, нет.
HTTP/2-фрейминг, бинарный protobuf-формат на проводе, схемы, запертые в .proto-файлах, и четыре формы RPC (unary плюс три вида стриминга) делают gRPC протоколом, на который большинство нагрузочных инструментов лишь разводит руками. Встроенный клиент k6 останавливается на unary-вызовах — для стримов нужны расширения; JMeter нужны плагины; и оба пути заканчиваются одинаково: protobuf-кодогенерацией в вашем тестовом пайплайне, сгенерированными стабами, вечно отстающими от сервера на версию, и отсутствием честного способа измерить стрим.
Начиная с v0.7.0, perfscale гоняет gRPC нативно. Семь actions std/grpc* живут в open-source-движке, рядом со std/http, std/tcp, std/udp и std/ws*. И как WebSocket, gRPC бесплатен для всех — actions, динамический загрузчик схем и метрики лежат в OSS-репозитории под двойной лицензией MIT/Apache-2.0.
Никакой кодогенерации, никаких стабов, никакого рассинхрона
Обычно больше всего болит схема. Классические инструменты хотят, чтобы вы запустили protoc, сгенерировали клиентские стабы и вечно держали их в синхроне с сервером. perfscale пропускает всё это: вызовы динамические. Схема приходит в рантайме — либо из reflection-сервиса сервера, либо из base64 FileDescriptorSet, который вы собрали один раз, — а payload'ы — обычный JSON, маппящийся по стандартным правилам protobuf-JSON.
На опечатку в "package.Service/Method" движок отвечает подсказкой «возможно, вы имели в виду…». Битый descriptor set падает сразу, до любого сетевого I/O. Ничего не надо генерировать, коммитить или поддерживать.
Стриминг — первоклассный
std/grpc-connect@v1 открывает живой HTTP/2-канал, который доживает до конца итерации. Unary-вызовы (std/grpc-call@v1) и стримы едут по тому же соединению, так что коннект и загрузка схемы оплачиваются раз на итерацию, а не на вызов.
Дальше std/grpc-stream-open@v1 открывает client-streaming, bidi или server-streaming вызов; std/grpc-stream-send@v1 шлёт сообщения — repeat + interval_ms превращают один шаблон в поток, токены ${seq}, ${uuid}, ${rand}, ${choice} раскрываются на каждую отправку; std/grpc-stream-recv@v1 читает до стоп-правила; std/grpc-stream-close@v1 делает half-close и дочитывает до финального статуса.
Метрики, которые что-то значат
Стриминг ломает модель «запрос/ответ», на которую рассчитано большинство метрик задержки. Поэтому perfscale сообщает четыре gRPC-серии:
grpc_req_duration— гистограмма латенси unary-вызовов.grpc_msg_rtt— application-level RTT сообщения. На unary-вызове равен длительности запроса; на стриме это время от отправки до совпавшего ответа — записывается, только когда сработало ваше until-правило, поэтому p95 что-то значит.grpc_msgs_sent/grpc_msgs_received— обычный throughput сообщений.grpc_req_failed— RPC, не удовлетворившиеexpect_status.
gRPC-шаги никогда не протекают в http_req_duration. Серии grpc_* — это вся история, и они стоят в той же сводке рядом с перцентилями HTTP/TCP/UDP/WebSocket.
Тот же YAML, масштаб платформы
Всё вышеописанное работает из CLI уже сегодня. В репозитории лежит готовый examples/grpc.test.yaml плюс локальный echo-сервер с reflection, так что первый прогон занимает минуты.
Тот же YAML без изменений работает на платформе: распределённые машины подают нагрузку, прогон автоматически распознаётся как gRPC, и дашборд переключается на gRPC-плитки — скорости отправки/приёма, счётчики сообщений, перцентили message-RTT, перцентили длительности вызова. Тест как есть встаёт в CI через GitHub Action или шаблоны GitLab CI.
С чего начать
- Нагрузочное тестирование gRPC, шаг за шагом — полный туториал: unary-вызов, живой канал, bidi-стрим, ассерты, CI.
- gRPC concepts — каждый параметр семи actions, источники схем, референс токенов.
- gRPC из CLI — компактный walkthrough.
Если ваш service mesh плавится на пике — лучше узнать об этом из YAML-файла, чем от дежурного.
