HTTP/2 & gRPC
Multiplexed connections and binary protocols that dramatically reduce request overhead.
Overview
HTTP/2 (RFC 7540, 2015) multiplexes multiple requests over a single TCP connection, eliminating head-of-line blocking and reducing connection overhead from HTTP/1.1. gRPC (Google, 2015) uses HTTP/2 as its transport and Protocol Buffers as its serialisation format, providing strongly-typed, high-performance RPC with streaming support. Both are binary protocols significantly faster than HTTP/1.1 with JSON.
Origin
HTTP/1.1 (1997) required one request per TCP connection without pipelining, causing browsers to open 6 connections per domain. SPDY (Google, 2009) introduced multiplexing and compression; it became the basis for HTTP/2 (IETF, 2015). Google released gRPC in February 2015 as an open-source version of their internal Stubby RPC framework. HTTP/3 (2022) replaced TCP with QUIC to eliminate TCP head-of-line blocking.
Examples
gRPC service definition and TypeScript server implementation
// order_service.proto
// syntax = "proto3";
// package orders.v1;
//
// service OrderService {
// rpc GetOrder (GetOrderRequest) returns (Order);
// rpc ListOrders (ListOrdersRequest) returns (stream Order);
// rpc CreateOrder (CreateOrderRequest) returns (Order);
// }
//
// message GetOrderRequest { string order_id = 1; }
// message Order {
// string id = 1;
// string customer_id = 2;
// int64 total_cents = 3;
// string status = 4;
// google.protobuf.Timestamp created_at = 5;
// }
// Generated with: npx grpc_tools_node_protoc ...
import * as grpc from '@grpc/grpc-js';
import { OrderServiceImplementation } from './generated/order_grpc_pb';
import { Order, GetOrderRequest } from './generated/order_pb';
const implementation: OrderServiceImplementation = {
getOrder(call: grpc.ServerUnaryCall<GetOrderRequest, Order>, callback) {
const orderId = call.request.getOrderId();
const order = db.orders.findById(orderId);
if (!order) {
callback({ code: grpc.status.NOT_FOUND, message: 'Order not found' });
return;
}
const response = new Order();
response.setId(order.id);
response.setTotalCents(order.totalCents);
callback(null, response);
},
};Protocol Buffers serialisation is 3-10x smaller than equivalent JSON and 5-10x faster to serialise/deserialise. The .proto file is the source of truth for the API contract; code is generated for both client and server, preventing interface drift.
HTTP/2 server push and connection reuse in Node.js
import http2 from 'http2';
import fs from 'fs';
const server = http2.createSecureServer({
key: fs.readFileSync('./key.pem'),
cert: fs.readFileSync('./cert.pem'),
allowHTTP1: true, // Fallback for HTTP/1.1 clients
});
server.on('stream', (stream, headers) => {
const path = headers[':path'];
if (path === '/') {
// Server Push: push CSS and JS before browser requests them
stream.pushStream({ ':path': '/static/app.css' }, (err, pushStream) => {
if (!err) {
pushStream.respondWithFile('./public/static/app.css', {
'content-type': 'text/css',
'cache-control': 'public, max-age=31536000',
});
}
});
stream.respondWithFile('./public/index.html', {
'content-type': 'text/html; charset=utf-8',
':status': 200,
});
}
});
server.listen(443);
// HTTP/2 multiplexes all streams on one TLS connection
// Multiple concurrent requests do not open multiple connectionsServer push (HTTP/2 PUSH_PROMISE) proactively sends resources the browser will need, eliminating the waterfall of HTML parse -> discover CSS link -> request CSS. In practice, server push is often misconfigured; the Early Hints header (HTTP 103) is a simpler alternative.
Use Cases
- 01Internal microservice communication where gRPC's generated clients, streaming, and binary encoding outperform REST+JSON
- 02Mobile APIs where Protocol Buffers' smaller payload size reduces data usage on metered connections
- 03Bidirectional streaming with gRPC for real-time server-to-client updates (stock prices, live leaderboards)
- 04High-throughput service-to-service calls where JSON parsing overhead is measurable and Protocol Buffers decode faster
When Not to Use
- //Do not use gRPC for public browser APIs; browser JavaScript does not support gRPC directly (requires grpc-web and an Envoy proxy)
- //Do not use gRPC when the team needs simple curl-based debugging; gRPC requires grpcurl or BloomRPC; REST+JSON is easier to inspect and test
- //Do not use HTTP/2 server push speculatively; it can cause wasted bandwidth if the client already has the pushed resource cached
Technical Notes
- HTTP/2 head-of-line blocking: HTTP/2 eliminates application-level HOL blocking (multiple streams on one connection) but TCP HOL blocking remains (one lost packet stalls all streams). HTTP/3 uses QUIC (UDP) to eliminate TCP HOL blocking; Cloudflare and Google have widely deployed HTTP/3
- gRPC streaming types: unary (single request/response), server streaming (one request, stream of responses), client streaming (stream of requests, one response), bidirectional streaming (both sides stream simultaneously)
- Protocol Buffers field numbering: field numbers 1-15 encode in one byte; 16-2047 in two bytes. Reserve numbers 1-15 for the most frequently occurring fields to minimise serialised message size
- gRPC-Gateway (grpc-ecosystem/grpc-gateway) generates a RESTful JSON gateway from .proto service definitions, allowing the same backend to serve both gRPC and REST clients with a single source of truth
More in Performance