When building high-performance microservices in Go, one question inevitably comes up:
Should you use REST or gRPC?
This isn’t just an architectural debate it directly impacts latency, throughput, infrastructure cost, and scalability. In this post, we’ll break down REST vs gRPC performance using real benchmarks, practical Go examples, and production insights.
🧠 Understanding the Core Difference
Before diving into benchmarks, it’s important to understand why performance differs.
REST
- Uses HTTP/1.1 (typically)
- Data format: JSON (text-based)
- Stateless, resource-oriented
gRPC
- Uses HTTP/2
- Data format: Protocol Buffers (binary)
- Supports streaming (bi-directional)
Why This Matters
- Binary serialization is more compact and faster to parse
- HTTP/2 enables multiplexing multiple requests over a single connection
- Reduced payload size = faster network transfer
⚙️ Benchmark Setup (Go)
Reference repository:
👉 https://github.com/chimanjain/go-rest-grpc-bencmark
This project benchmarks:
- REST API (JSON over HTTP)
- gRPC API (Protobuf over HTTP/2)
Typical Test Conditions
- Concurrent clients
- Small to medium payload sizes
- High request volume
- Controlled environment for fair comparison
📊 Benchmark Results (What Actually Happens)
Across benchmarks (including the referenced repo), a few consistent patterns emerge.
🔥 Key Observations
-
gRPC shows:
- Lower latency
- Higher throughput
- Better CPU efficiency
-
REST:
- Performs well at low scale
- Degrades faster under heavy load due to parsing and connection overhead
🧾 Why gRPC Is Faster
| Factor | REST | gRPC |
|---|---|---|
| Serialization | JSON (text) | Protobuf (binary) |
| Transport | HTTP/1.1 | HTTP/2 |
| Payload Size | Larger | Smaller |
| Parsing Cost | Higher | Lower |
| Streaming | Limited | Native |
🧪 Sample Go Implementations
🌐 REST Example (net/http)
package main
import (
"encoding/json"
"net/http"
)
type User struct {
ID string `json:"id"`
Name string `json:"name"`
}
func getUser(w http.ResponseWriter, r *http.Request) {
user := User{ID: "1", Name: "John"}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(user)
}
⚡ gRPC Example
package main
import (
"context"
pb "example/proto"
)
type server struct {
pb.UnimplementedUserServiceServer
}
func (s *server) GetUser(ctx context.Context, req *pb.UserRequest) (*pb.UserResponse, error) {
return &pb.UserResponse{
Id: "1",
Name: "John",
}, nil
}
⚖️ Key Difference
- REST: manual serialization using
encoding/json - gRPC: strongly typed, auto-generated code via
.protofiles
📈 Performance Deep Dive
1. Latency
- gRPC typically achieves lower p99 latency
-
Especially noticeable with:
- High concurrency
- Small payloads
Reason: smaller payloads + faster serialization
2. Throughput
- gRPC supports more requests per second
- HTTP/2 multiplexing reduces connection overhead
3. CPU Usage
- JSON parsing is CPU-intensive
- Protobuf significantly reduces CPU overhead
4. Network Efficiency
- Protobuf messages are smaller than JSON
- Less bandwidth usage leads to faster transfers
🤔 When REST Performs Just Fine
Despite the performance gap, REST is still a solid choice in many scenarios:
- Low to moderate traffic
- Large payloads (compression reduces differences)
- Public APIs and browser-based clients
- Faster development and easier debugging
In many real-world systems, the performance difference is not critical.
🧩 Real-World Tradeoffs
Choose gRPC when:
- Internal microservices communication
- High-throughput or low-latency systems
- Real-time streaming (e.g., chat, telemetry)
- Strong contract enforcement is needed
Choose REST when:
- Building public-facing APIs
- Browser compatibility is required
- Simplicity and ecosystem support matter
Common Industry Pattern
Use gRPC internally and REST externally
This gives you performance where it matters and compatibility where it’s needed.
🧠 Key Takeaways
-
gRPC is faster by design due to:
- HTTP/2
- Protobuf serialization
- Efficient connection handling
-
REST is:
- Simpler
- More widely supported
- “Fast enough” for many applications
🏁 Final Thoughts
Performance decisions should always be context-driven.
- Building high-scale backend systems? → gRPC
- Building public APIs? → REST
The best architecture often combines both.
🔗 References
- https://github.com/chimanjain/go-rest-grpc-bencmark
- https://grpc.io/docs/what-is-grpc/introduction/
- https://pkg.go.dev/net/http
- https://developers.google.com/protocol-buffers
💬 If you’ve run your own benchmarks in Go, feel free to share your results!
Top comments (1)
Por favor sube más comparaciones, gracias a ti ahora conozco algo de lo que no tenía ni idea