gRPC: обзор
gRPC — высокопроизводительный RPC-фреймворк от Google, использующий HTTP/2 и Protocol Buffers для бинарной передачи данных между сервисами.
Зачем нужно
В микросервисной архитектуре сервисы часто общаются через REST + JSON. gRPC заменяет это решение более эффективным: HTTP/2 мультиплексирует запросы, protobuf сериализует данные компактнее JSON, а схема .proto генерирует типизированный код на любом языке. Это снижает латентность и потребление ресурсов в высоконагруженных системах.
Где используется
- Коммуникация между микросервисами (Google, Netflix, Uber)
- gRPC-Web — в браузере через gRPC-Web прокси
- Мобильные клиенты с жёсткими требованиями к трафику
- Стриминг: потоковая передача данных между сервисами
gRPC vs REST
| Критерий | REST | gRPC |
|---|---|---|
| Протокол | HTTP/1.1 | HTTP/2 |
| Формат | JSON (текст) | Protobuf (бинарный) |
| Схема | OpenAPI (опционально) | .proto (обязательно) |
| Стриминг | SSE/WebSocket (отдельно) | Встроен (4 типа) |
| Браузер | Нативно | Через gRPC-Web прокси |
| Читаемость | Высокая | Требует инструментов |
Четыре типа вызовов gRPC
service UserService {
// Unary — один запрос, один ответ (как REST)
rpc GetUser(GetUserRequest) returns (User);
// Server Streaming — один запрос, поток ответов
rpc ListUsers(ListUsersRequest) returns (stream User);
// Client Streaming — поток запросов, один ответ
rpc UploadUsers(stream CreateUserRequest) returns (UploadResult);
// Bidirectional Streaming — поток в обе стороны
rpc Chat(stream ChatMessage) returns (stream ChatMessage);
}
Пример на Node.js (@grpc/grpc-js)
Сервер
// npm install @grpc/grpc-js @grpc/proto-loader
const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
const packageDef = protoLoader.loadSync('user.proto', {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true,
});
const proto = grpc.loadPackageDefinition(packageDef).myapp;
// Реализация сервиса
const server = new grpc.Server;
server.addService(proto.UserService.service, {
// Unary
getUser: (call, callback) => {
const user = db.users.find(u => u.id === call.request.id);
if (!user) {
return callback({ code: grpc.status.NOT_FOUND, message: 'User not found' });
}
callback(null, user);
},
// Server Streaming
listUsers: (call) => {
const users = db.users.getAll;
users.forEach(user => call.write(user));
call.end();
},
});
server.bindAsync('0.0.0.0:50051', grpc.ServerCredentials.createInsecure, () => {
console.log('gRPC server running on port 50051');
});
Клиент
const client = new proto.UserService(
'localhost:50051',
grpc.credentials.createInsecure
);
// Unary вызов
client.getUser({ id: 42 }, (err, user) => {
if (err) console.error(err);
else console.log(user.name);
});
// Server Streaming
const stream = client.listUsers({ page: 1, limit: 100 });
stream.on('data', user => console.log(user.name));
stream.on('end', () => console.log('Стриминг завершён'));
stream.on('error', err => console.error(err));
Частые ошибки
- gRPC недоступен в браузере напрямую — нужен Envoy или grpc-web прокси
- Изменение номеров полей в
.proto— ломает обратную совместимость - Не обрабатывают gRPC status codes (NOT_FOUND, UNAUTHENTICATED, etc.) на клиенте
- Не настраивают TLS в продакшне —
createInsecureтолько для разработки