Featured image of post gRPC can you use it without Protobuf?

gRPC can you use it without Protobuf?

Exploring gRPC with JSON - Code Examples in Python, C# and GO

๐Ÿ•ฐ๏ธ A Brief History (Because All Good Stories Need One)

Back in 2015, Google launched gRPC to let services communicate easily over the web. It uses HTTP/2 for faster and more reliable connections and Protobuf for efficient, binary serialization. More on this here.

But what if you donโ€™t want Protobuf? Enter gRPC with alternative serialization….

๐Ÿค” Why Protobuf vs Without Protobuf?

FeatureWith ProtobufWithout Protobuf
PerformanceBlazing fast โšก๏ธSlower (e.g., JSON serialization)
Type SafetyStrong typing with schemasLoose typing, more errors
InteroperabilityCross-language compatibleLanguage-specific quirks

This article is mostly for Fun and Learning..

There is likely not a good reason to use JSON.. but you CAN! And thats the point..

๐Ÿ› ๏ธ Letโ€™s Get to the Code!

1. Python: Protobuf gRPC Server

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import grpc
from concurrent import futures
import example_pb2
import example_pb2_grpc

class Greeter(example_pb2_grpc.GreeterServicer):
    def SayHello(self, request, context):
        return example_pb2.HelloReply(message=f"Hello, {request.name}!")

def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    example_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
    server.add_insecure_port('[::]:50051')
    server.start()
    server.wait_for_termination()

serve()

2. Python: Protobuf gRPC Client

1
2
3
4
5
6
7
8
import grpc
import example_pb2
import example_pb2_grpc

channel = grpc.insecure_channel('localhost:50051')
stub = example_pb2_grpc.GreeterStub(channel)
response = stub.SayHello(example_pb2.HelloRequest(name="Pythonista"))
print(response.message)

3. Python: gRPC with JSON

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import grpc
import json
from concurrent import futures
from grpc.experimental import json_format

class GreeterService:
    def SayHello(self, request, context):
        data = json.loads(request.data)
        return json_format.MessageToJson({"message": f"Hello, {data['name']}!"})

def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    server.add_insecure_port('[::]:50051')
    server.start()
    server.wait_for_termination()

serve()

4. C#: Protobuf gRPC Server

1
2
3
4
5
6
7
public class GreeterService : Greeter.GreeterBase
{
    public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
    {
        return Task.FromResult(new HelloReply { Message = $"Hello, {request.Name}!" });
    }
}

5. C#: Protobuf gRPC Client

1
2
3
4
var channel = GrpcChannel.ForAddress("http://localhost:50051");
var client = new Greeter.GreeterClient(channel);
var response = client.SayHello(new HelloRequest { Name = "C# Guru" });
Console.WriteLine(response.Message);

6. C#: gRPC with JSON

1
2
3
4
5
HttpClient client = new HttpClient();
var content = new StringContent("{\"name\":\"C# Guru\"}", Encoding.UTF8, "application/json");
var response = await client.PostAsync("http://localhost:50051/greet", content);
var result = await response.Content.ReadAsStringAsync();
Console.WriteLine(result);

7. Go: Protobuf gRPC Server

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import (
	"context"
	"fmt"
	"google.golang.org/grpc"
	"net"
	pb "example"
)

type server struct{}

func (s *server) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) {
	return &pb.HelloReply{Message: "Hello, " + req.Name + "!"}, nil
}

func main() {
	lis, _ := net.Listen("tcp", ":50051")
	s := grpc.NewServer()
	pb.RegisterGreeterServer(s, &server{})
	s.Serve(lis)
}

8. Go: Protobuf gRPC Client

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package main

import (
	"context"
	"fmt"
	"google.golang.org/grpc"
	pb "example"
)

func main() {
	conn, _ := grpc.Dial("localhost:50051", grpc.WithInsecure())
	defer conn.Close()
	c := pb.NewGreeterClient(conn)
	resp, _ := c.SayHello(context.Background(), &pb.HelloRequest{Name: "Go Dev"})
	fmt.Println(resp.Message)
}

9. Go: gRPC with JSON

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package main

import (
	"bytes"
	"fmt"
	"net/http"
)

func main() {
	data := `{"name":"Go Dev"}`
	resp, _ := http.Post("http://localhost:50051/greet", "application/json", bytes.NewBuffer([]byte(data)))
	defer resp.Body.Close()
	body, _ := io.ReadAll(resp.Body)
	fmt.Println(string(body))
}

10. Protobuf Definition (Proto3)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
syntax = "proto3";
service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest {
  string name = 1;
}
message HelloReply {
  string message = 1;
}

๐Ÿ”‘ Key Ideas

ConceptExplanation
gRPCRPC framework using HTTP/2 & Protobuf
ProtobufBinary serialization for performance
JSON in gRPCAlternative to Protobuf for flexibility
Cross-platformWorks across multiple languages

๐Ÿ“š References