gRPC is a high-performance, open-source universal RPC framework developed by Google. It uses HTTP/2 for transport, Protocol Buffers as the interface description language, and provides features such as authentication, bidirectional streaming and flow control, blocking or nonblocking bindings, and cancellation and timeouts.
Protocol Buffers (protobuf) is the default serialization mechanism for gRPC. Here's a basic example:
syntax = "proto3";
package example;
message Person {
string name = 1;
int32 age = 2;
repeated string hobbies = 3;
}
message GetPersonRequest {
string id = 1;
}
service PersonService {
rpc GetPerson(GetPersonRequest) returns (Person);
}
A gRPC service is defined in a .proto file:
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
rpc SayHello(HelloRequest) returns (HelloReply);
rpc LotsOfReplies(HelloRequest) returns (stream HelloReply);
rpc LotsOfGreetings(stream HelloRequest) returns (HelloReply);
rpc BidiHello(stream HelloRequest) returns (stream HelloReply);
Example in Go:
type server struct{}
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}
func main() {
lis, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
gRPC uses status codes for error handling:
import "google.golang.org/grpc/codes"
import "google.golang.org/grpc/status"
func (s *server) SomeRPC(ctx context.Context, req *pb.Request) (*pb.Response, error) {
if someError {
return nil, status.Errorf(codes.InvalidArgument, "Invalid argument: %v", err)
}
// Normal processing
}
Metadata is information about a particular RPC call in the form of key-value pairs.
Sending metadata (client-side):
md := metadata.Pairs(
"key1", "value1",
"key2", "value2",
)
ctx := metadata.NewOutgoingContext(context.Background(), md)
response, err := client.SomeRPC(ctx, request)
Receiving metadata (server-side):
func (s *server) SomeRPC(ctx context.Context, req *pb.Request) (*pb.Response, error) {
md, ok := metadata.FromIncomingContext(ctx)
if ok {
// Use metadata
}
// Process request
}
Interceptors allow you to add custom behavior to RPC calls.
Unary interceptor:
func unaryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
log.Printf("before handling. Info: %+v", info)
resp, err := handler(ctx, req)
log.Printf("after handling. resp: %+v", resp)
return resp, err
}
s := grpc.NewServer(grpc.UnaryInterceptor(unaryInterceptor))
Setting a deadline:
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
response, err := client.SomeRPC(ctx, request)
if err != nil {
statusErr, ok := status.FromError(err)
if ok && statusErr.Code() == codes.DeadlineExceeded {
log.Println("Deadline exceeded!")
}
}
gRPC supports client-side load balancing. You can use DNS for service discovery and implement custom load balancing policies.
conn, err := grpc.Dial(
"service:50051",
grpc.WithBalancerName("round_robin"),
grpc.WithInsecure(),
)
Using TLS:
creds, err := credentials.NewClientTLSFromFile("cert.pem", "")
if err != nil {
log.Fatalf("Failed to load credentials: %v", err)
}
conn, err := grpc.Dial("example.com:50051", grpc.WithTransportCredentials(creds))
2024 © All rights reserved - buraxta.com