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
}
|