⚡ Performance First
gRPC is 5-10x faster than REST due to binary encoding and HTTP/2. Perfect for microservices.
gRPC (gRPC Remote Procedure Calls) is a high-performance RPC framework that lets you call remote functions as if they were local.
| Feature | REST/JSON | gRPC |
|---|---|---|
| Speed | Slower (text parsing) | Faster (binary) |
| Payload Size | Larger (text) | Smaller (binary) |
| Streaming | Limited | Full support |
| Type Safety | Runtime | Compile-time |
| Code Generation | Manual | Automatic |
| Browser Support | Excellent | Limited |
Protocol Buffers (protobuf) is a binary serialization format. Think of it as a more efficient alternative to JSON.
JSON (text-based):
1{2 "id": 12345,3 "name": "John Doe",4 "email": "john@example.com",5 "age": 306}Size: ~80 bytes
Protocol Buffers (binary):
1[encoded binary data]Size: ~25 bytes (3x smaller!)
.proto files define your service contract. They’re like API documentation + code generator input.
1syntax = "proto3";2
3package user_service;4
5// Message definition (like a struct/class)6message User {7 int32 id = 1;8 string name = 2;9 string email = 3;10 int32 age = 4;11}12
13message GetUserRequest {14 int32 user_id = 1;15}16
17message GetUserResponse {18 User user = 1;19}20
21// Service definition22service UserService {23 // Unary RPC: one request, one response24 rpc GetUser(GetUserRequest) returns (GetUserResponse);25
26 // Server streaming: one request, multiple responses27 rpc ListUsers(GetUserRequest) returns (stream User);28
29 // Client streaming: multiple requests, one response30 rpc CreateUsers(stream User) returns (CreateUsersResponse);31
32 // Bidirectional streaming: multiple requests, multiple responses33 rpc ChatUsers(stream ChatMessage) returns (stream ChatMessage);34}Field numbers (1, 2, 3…) are important:
Common types:
int32, int64 - Integersfloat, double - Floating pointbool - Booleanstring - UTF-8 stringbytes - Raw bytesrepeated - Arrays/listsmap - Key-value pairsOne request, one response. Like a function call.
1service UserService {2 rpc GetUser(GetUserRequest) returns (GetUserResponse);3}Flow:
1Client → Request → Server2Client ← Response ← ServerOne request, multiple responses. Server sends stream of data.
1service UserService {2 rpc ListUsers(GetUserRequest) returns (stream User);3}Flow:
1Client → Request → Server2Client ← User 1 ← Server3Client ← User 2 ← Server4Client ← User 3 ← Server5...Use cases:
Multiple requests, one response. Client sends stream of data.
1service UserService {2 rpc CreateUsers(stream User) returns (CreateUsersResponse);3}Flow:
1Client → User 1 → Server2Client → User 2 → Server3Client → User 3 → Server4Client ← Response ← ServerUse cases:
Multiple requests, multiple responses. Both sides stream.
1service ChatService {2 rpc Chat(stream ChatMessage) returns (stream ChatMessage);3}Flow:
1Client → Message 1 → Server2Client ← Message 2 ← Server3Client → Message 3 → Server4Client ← Message 4 ← Server5...Use cases:
1import grpc2from concurrent import futures3import user_pb24import user_pb2_grpc5
6class UserService(user_pb2_grpc.UserServiceServicer):7 def GetUser(self, request, context):8 """Unary RPC implementation"""9 user_id = request.user_id10
11 # Fetch user from database12 user = user_repository.find_by_id(user_id)13
14 if not user:15 context.set_code(grpc.StatusCode.NOT_FOUND)16 context.set_details("User not found")17 return user_pb2.GetUserResponse()18
19 # Return response20 return user_pb2.GetUserResponse(21 user=user_pb2.User(22 id=user.id,23 name=user.name,24 email=user.email,25 age=user.age26 )27 )28
29 def ListUsers(self, request, context):30 """Server streaming implementation"""31 users = user_repository.find_all()32
33 for user in users:34 yield user_pb2.User(35 id=user.id,36 name=user.name,37 email=user.email,38 age=user.age39 )40
41 def CreateUsers(self, request_iterator, context):42 """Client streaming implementation"""43 created_count = 044
45 for user_request in request_iterator:46 # Create user47 user_repository.create(48 name=user_request.name,49 email=user_request.email,50 age=user_request.age51 )52 created_count += 153
54 return user_pb2.CreateUsersResponse(55 created_count=created_count56 )57
58 def ChatUsers(self, request_iterator, context):59 """Bidirectional streaming implementation"""60 for message in request_iterator:61 # Process message62 response = process_message(message)63
64 # Send response65 yield response66
67def serve():68 server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))69 user_pb2_grpc.add_UserServiceServicer_to_server(70 UserService(), server71 )72 server.add_insecure_port('[::]:50051')73 server.start()74 server.wait_for_termination()75
76if __name__ == '__main__':77 serve()1import io.grpc.Server;2import io.grpc.ServerBuilder;3import io.grpc.stub.StreamObserver;4import user.UserServiceGrpc;5import user.UserOuterClass;6
7public class UserService extends UserServiceGrpc.UserServiceImplBase {8
9 @Override10 public void getUser(11 UserOuterClass.GetUserRequest request,12 StreamObserver<UserOuterClass.GetUserResponse> responseObserver) {13 // Unary RPC implementation14 int userId = request.getUserId();15
16 // Fetch user from database17 User user = userRepository.findById(userId)18 .orElseThrow(() -> Status.NOT_FOUND19 .withDescription("User not found")20 .asRuntimeException());21
22 // Build response23 UserOuterClass.GetUserResponse response = UserOuterClass.GetUserResponse.newBuilder()24 .setUser(UserOuterClass.User.newBuilder()25 .setId(user.getId())26 .setName(user.getName())27 .setEmail(user.getEmail())28 .setAge(user.getAge())29 .build())30 .build();31
32 responseObserver.onNext(response);33 responseObserver.onCompleted();34 }35
36 @Override37 public void listUsers(38 UserOuterClass.GetUserRequest request,39 StreamObserver<UserOuterClass.User> responseObserver) {40 // Server streaming implementation41 List<User> users = userRepository.findAll();42
43 for (User user : users) {44 UserOuterClass.User protoUser = UserOuterClass.User.newBuilder()45 .setId(user.getId())46 .setName(user.getName())47 .setEmail(user.getEmail())48 .setAge(user.getAge())49 .build();50
51 responseObserver.onNext(protoUser);52 }53
54 responseObserver.onCompleted();55 }56
57 @Override58 public StreamObserver<UserOuterClass.User> createUsers(59 StreamObserver<UserOuterClass.CreateUsersResponse> responseObserver) {60 // Client streaming implementation61 return new StreamObserver<UserOuterClass.User>() {62 private int createdCount = 0;63
64 @Override65 public void onNext(UserOuterClass.User user) {66 // Create user67 userRepository.create(68 user.getName(),69 user.getEmail(),70 user.getAge()71 );72 createdCount++;73 }74
75 @Override76 public void onError(Throwable t) {77 responseObserver.onError(t);78 }79
80 @Override81 public void onCompleted() {82 UserOuterClass.CreateUsersResponse response =83 UserOuterClass.CreateUsersResponse.newBuilder()84 .setCreatedCount(createdCount)85 .build();86
87 responseObserver.onNext(response);88 responseObserver.onCompleted();89 }90 };91 }92
93 public static void main(String[] args) throws Exception {94 Server server = ServerBuilder.forPort(50051)95 .addService(new UserService())96 .build()97 .start();98
99 server.awaitTermination();100 }101}1import grpc2import user_pb23import user_pb2_grpc4
5def get_user(user_id):6 """Unary RPC client"""7 with grpc.insecure_channel('localhost:50051') as channel:8 stub = user_pb2_grpc.UserServiceStub(channel)9
10 request = user_pb2.GetUserRequest(user_id=user_id)11 response = stub.GetUser(request)12
13 return response.user14
15def list_users():16 """Server streaming client"""17 with grpc.insecure_channel('localhost:50051') as channel:18 stub = user_pb2_grpc.UserServiceStub(channel)19
20 request = user_pb2.GetUserRequest()21
22 for user in stub.ListUsers(request):23 print(f"User: {user.name}")24
25def create_users(users_data):26 """Client streaming client"""27 with grpc.insecure_channel('localhost:50051') as channel:28 stub = user_pb2_grpc.UserServiceStub(channel)29
30 def user_generator():31 for user_data in users_data:32 yield user_pb2.User(33 name=user_data['name'],34 email=user_data['email'],35 age=user_data['age']36 )37
38 response = stub.CreateUsers(user_generator())39 print(f"Created {response.created_count} users")1import io.grpc.ManagedChannel;2import io.grpc.ManagedChannelBuilder;3import user.UserServiceGrpc;4import user.UserOuterClass;5
6public class GrpcClient {7 private final ManagedChannel channel;8 private final UserServiceGrpc.UserServiceBlockingStub blockingStub;9
10 public GrpcClient(String host, int port) {11 this.channel = ManagedChannelBuilder.forAddress(host, port)12 .usePlaintext()13 .build();14 this.blockingStub = UserServiceGrpc.newBlockingStub(channel);15 }16
17 public UserOuterClass.User getUser(int userId) {18 // Unary RPC client19 UserOuterClass.GetUserRequest request = UserOuterClass.GetUserRequest.newBuilder()20 .setUserId(userId)21 .build();22
23 UserOuterClass.GetUserResponse response = blockingStub.getUser(request);24 return response.getUser();25 }26
27 public void listUsers() {28 // Server streaming client29 UserOuterClass.GetUserRequest request = UserOuterClass.GetUserRequest.newBuilder().build();30
31 blockingStub.listUsers(request).forEachRemaining(user -> {32 System.out.println("User: " + user.getName());33 });34 }35
36 public void createUsers(List<UserData> usersData) {37 // Client streaming client38 UserServiceGrpc.UserServiceStub asyncStub = UserServiceGrpc.newStub(channel);39
40 StreamObserver<UserOuterClass.User> requestObserver = asyncStub.createUsers(41 new StreamObserver<UserOuterClass.CreateUsersResponse>() {42 @Override43 public void onNext(UserOuterClass.CreateUsersResponse response) {44 System.out.println("Created " + response.getCreatedCount() + " users");45 }46
47 @Override48 public void onError(Throwable t) {49 t.printStackTrace();50 }51
52 @Override53 public void onCompleted() {54 // Done55 }56 }57 );58
59 for (UserData userData : usersData) {60 UserOuterClass.User user = UserOuterClass.User.newBuilder()61 .setName(userData.getName())62 .setEmail(userData.getEmail())63 .setAge(userData.getAge())64 .build();65
66 requestObserver.onNext(user);67 }68
69 requestObserver.onCompleted();70 }71}gRPC uses status codes (similar to HTTP but different):
| Code | Meaning | HTTP Equivalent |
|---|---|---|
OK | Success | 200 |
INVALID_ARGUMENT | Bad request | 400 |
NOT_FOUND | Resource not found | 404 |
ALREADY_EXISTS | Conflict | 409 |
PERMISSION_DENIED | Forbidden | 403 |
UNAUTHENTICATED | Unauthorized | 401 |
RESOURCE_EXHAUSTED | Rate limited | 429 |
INTERNAL | Server error | 500 |
1import grpc2from grpc import StatusCode3
4def GetUser(self, request, context):5 try:6 user = user_repository.find_by_id(request.user_id)7
8 if not user:9 context.set_code(StatusCode.NOT_FOUND)10 context.set_details("User not found")11 return user_pb2.GetUserResponse()12
13 return user_pb2.GetUserResponse(user=user)14
15 except ValueError as e:16 context.set_code(StatusCode.INVALID_ARGUMENT)17 context.set_details(str(e))18 return user_pb2.GetUserResponse()19
20 except Exception as e:21 context.set_code(StatusCode.INTERNAL)22 context.set_details("Internal server error")23 return user_pb2.GetUserResponse()1@Override2public void getUser(3 UserOuterClass.GetUserRequest request,4 StreamObserver<UserOuterClass.GetUserResponse> responseObserver) {5 try {6 User user = userRepository.findById(request.getUserId())7 .orElseThrow(() -> Status.NOT_FOUND8 .withDescription("User not found")9 .asRuntimeException());10
11 UserOuterClass.GetUserResponse response = buildResponse(user);12 responseObserver.onNext(response);13 responseObserver.onCompleted();14
15 } catch (IllegalArgumentException e) {16 responseObserver.onError(Status.INVALID_ARGUMENT17 .withDescription(e.getMessage())18 .asRuntimeException());19
20 } catch (Exception e) {21 responseObserver.onError(Status.INTERNAL22 .withDescription("Internal server error")23 .asRuntimeException());24 }25}Typical performance differences:
| Metric | REST/JSON | gRPC |
|---|---|---|
| Latency | 10-50ms | 2-10ms |
| Throughput | 1K-10K req/s | 10K-100K req/s |
| Payload Size | 100% | 30-50% |
| CPU Usage | Higher | Lower |
Why gRPC is faster:
At the code level, gRPC translates to service interfaces, message types, and streaming handlers.
1# Design service interface2class UserServiceInterface:3 """Service interface defining contract"""4
5 def get_user(self, user_id: int) -> User:6 """Unary: Get single user"""7 pass8
9 def list_users(self, filters: dict) -> Iterator[User]:10 """Server streaming: List users"""11 pass12
13 def create_users(self, users: Iterator[User]) -> int:14 """Client streaming: Create multiple users"""15 pass16
17# Implementation18class UserService(UserServiceInterface):19 def __init__(self, repository: UserRepository):20 self.repository = repository21
22 def get_user(self, user_id: int) -> User:23 return self.repository.find_by_id(user_id)24
25 def list_users(self, filters: dict) -> Iterator[User]:26 return self.repository.find_all(filters)27
28 def create_users(self, users: Iterator[User]) -> int:29 count = 030 for user in users:31 self.repository.create(user)32 count += 133 return count1// Service interface2interface UserService {3 User getUser(int userId);4 Stream<User> listUsers(Map<String, String> filters);5 int createUsers(Stream<User> users);6}7
8// Implementation9class UserServiceImpl implements UserService {10 private final UserRepository repository;11
12 public UserServiceImpl(UserRepository repository) {13 this.repository = repository;14 }15
16 @Override17 public User getUser(int userId) {18 return repository.findById(userId).orElseThrow();19 }20
21 @Override22 public Stream<User> listUsers(Map<String, String> filters) {23 return repository.findAll(filters).stream();24 }25
26 @Override27 public int createUsers(Stream<User> users) {28 AtomicInteger count = new AtomicInteger();29 users.forEach(user -> {30 repository.create(user);31 count.incrementAndGet();32 });33 return count.get();34 }35}⚡ Performance First
gRPC is 5-10x faster than REST due to binary encoding and HTTP/2. Perfect for microservices.
🔄 Streaming Support
gRPC supports four streaming types: unary, server, client, and bidirectional. REST can’t do this.
📊 Strong Typing
Protocol Buffers provide compile-time type safety. Schema defines contract, code is generated.
🏗️ Service Contracts
.proto files define service contracts. Generate client/server code in any language automatically.