Overview
gRPC is a modern open source high performance Remote Procedure Call (RPC) framework that can run in any environment. It can efficiently connect services in and across data centers with pluggable support for load balancing, tracing, health checking and authentication. It is also applicable in last mile of distributed computing to connect devices, mobile applications and browsers to backend services.
Sample Code
package main
import (
"context"
"encoding/json"
"flag"
"fmt"
"log"
"net"
"os"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/encoding"
)
const (
defaultAddress = "127.0.0.1:50051"
serviceName = "sample.Greeter"
methodName = "/sample.Greeter/SayHello"
)
func init() {
encoding.RegisterCodec(jsonCodec{})
}
type HelloRequest struct {
Name string `json:"name"`
}
type HelloReply struct {
Message string `json:"message"`
Timestamp string `json:"timestamp"`
}
type jsonCodec struct{}
func (jsonCodec) Marshal(v any) ([]byte, error) {
return json.Marshal(v)
}
func (jsonCodec) Unmarshal(data []byte, v any) error {
return json.Unmarshal(data, v)
}
func (jsonCodec) Name() string {
return "json"
}
type greeterService interface {
SayHello(context.Context, *HelloRequest) (*HelloReply, error)
}
type greeterServer struct{}
func (greeterServer) SayHello(_ context.Context, req *HelloRequest) (*HelloReply, error) {
name := req.Name
if name == "" {
name = "friend"
}
return &HelloReply{
Message: fmt.Sprintf("Hello, %s!", name),
Timestamp: time.Now().Format(time.RFC3339),
}, nil
}
var greeterServiceDesc = grpc.ServiceDesc{
ServiceName: serviceName,
HandlerType: (*greeterService)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "SayHello",
Handler: sayHelloHandler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "sample",
}
func sayHelloHandler(srv any, ctx context.Context, dec func(any) error, interceptor grpc.UnaryServerInterceptor) (any, error) {
req := new(HelloRequest)
if err := dec(req); err != nil {
return nil, err
}
baseHandler := func(ctx context.Context, req any) (any, error) {
return srv.(greeterService).SayHello(ctx, req.(*HelloRequest))
}
if interceptor == nil {
return baseHandler(ctx, req)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: methodName,
}
return interceptor(ctx, req, info, baseHandler)
}
func main() {
mode := flag.String("mode", "server", "server or client")
addr := flag.String("addr", defaultAddress, "address to listen on or connect to")
name := flag.String("name", "gRPC", "name to send in client mode")
flag.Parse()
logger := log.New(os.Stderr, "grpc-sample: ", log.LstdFlags)
switch *mode {
case "server":
if err := runServer(*addr, logger); err != nil {
logger.Fatal(err)
}
case "client":
if err := runClient(*addr, *name, logger); err != nil {
logger.Fatal(err)
}
default:
logger.Fatalf("unsupported mode %q, use server or client", *mode)
}
}
func runServer(addr string, logger *log.Logger) error {
lis, err := net.Listen("tcp", addr)
if err != nil {
return fmt.Errorf("listen on %s: %w", addr, err)
}
server := grpc.NewServer(grpc.ForceServerCodec(jsonCodec{}))
server.RegisterService(&greeterServiceDesc, greeterServer{})
logger.Printf("server listening on %s", addr)
return server.Serve(lis)
}
func runClient(addr, name string, logger *log.Logger) error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
conn, err := grpc.DialContext(
ctx,
addr,
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithBlock(),
grpc.WithDefaultCallOptions(grpc.ForceCodec(jsonCodec{})),
)
if err != nil {
return fmt.Errorf("connect to %s: %w", addr, err)
}
defer conn.Close()
req := &HelloRequest{Name: name}
resp := new(HelloReply)
if err := conn.Invoke(ctx, methodName, req, resp); err != nil {
return fmt.Errorf("invoke %s: %w", methodName, err)
}
logger.Printf("response: %s (%s)", resp.Message, resp.Timestamp)
return nil
}
References
| Reference | URL |
|---|---|
| Introduction to gRPC | https://grpc.io/docs/what-is-grpc/introduction/ |
