17/09/2020 - GO
Bu örneğimizde istemci ve sunucu Golang uygulamaları için gRPC tekli engelleyici oluşturacağız. Kavram, HTTP yönlendirici ve HTTP sunucusu için ara yazılım oluşturmakla aynıdır.
Örnek basit. İstemci, yanıt almanın ne kadar sürdüğünü öğrenmek için timer
engelleyici kullanacaktır. Daha sonra kimliğini ayarlamak için identity
engelleyicisini kullanacaktır. Bu daha sonra sunucunun verify
ismindeki engelleyicisi tarafından kontrol edilecek. Sunucuda ayrıca log
adında bir engelleyici bulunur ve bu da sadece bir şeyi günlüğe kaydeder.
├── Makefile
├── client
│ └── main.go
├── go.mod
├── hello.pb.go
├── hello.proto
├── interceptor
│ ├── Identity.go
│ ├── log.go
│ ├── timer.go
│ └── verify.go
├── message
│ ├── client.go
│ └── server.go
└── server
└── main.go
hello.pb.go
dosyasını yaratmak için make compile
komutunu çalıştırın.
.PHONY: compile
compile:
protoc --go_out=plugins=grpc:. --go_opt=paths=source_relative hello.proto
.PHONY: client
client:
go run --race client/main.go
.PHONY: server
server:
go run --race server/main.go
syntax = "proto3";
package message;
option go_package = "generated;generated";
message MessageRequest {
string text = 1;
}
message MessageResponse {
string text = 1;
}
service MessageService {
rpc SendMessage (MessageRequest) returns (MessageResponse) {}
}
package main
import (
"context"
"log"
"time"
"google.golang.org/grpc"
"sport/interceptor"
"sport/message"
)
func main() {
log.Println("client")
opts := []grpc.DialOption{
grpc.WithInsecure(),
grpc.WithChainUnaryInterceptor(
interceptor.TimerUnaryClient,
interceptor.Identity{ID: "client-1"}.UnaryClient,
),
}
conn, err := grpc.Dial(":50051", opts...)
if err != nil {
log.Fatalln(err)
}
defer conn.Close()
messageClient := message.NewClient(conn, time.Second*10)
res, err := messageClient.SendMessage(context.Background(), "Hello")
log.Println("ERRR:", err)
log.Println("RESP:", res)
}
package interceptor
import (
"context"
"log"
"time"
"google.golang.org/grpc"
)
func TimerUnaryClient(
ctx context.Context,
method string,
req interface{},
reply interface{},
cc *grpc.ClientConn,
invoker grpc.UnaryInvoker,
opts ...grpc.CallOption,
) error {
start := time.Now()
log.Println("Timer: 1")
time.Sleep(time.Second)
err := invoker(ctx, method, req, reply, cc, opts...)
log.Println("Timer: 2")
time.Sleep(time.Second)
end := time.Since(start)
log.Printf("%s method call took %s\n", method, end)
return err
}
package interceptor
import (
"context"
"log"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
)
type Identity struct {
ID string
}
func (i Identity) UnaryClient(
ctx context.Context,
method string,
req interface{},
reply interface{},
cc *grpc.ClientConn,
invoker grpc.UnaryInvoker,
opts ...grpc.CallOption,
) error {
md := metadata.Pairs()
md.Set("client-id", i.ID)
ctx = metadata.NewOutgoingContext(ctx, md)
log.Println("Identity: 1")
time.Sleep(time.Second)
err := invoker(ctx, method, req, reply, cc, opts...)
log.Println("Identity: 2")
time.Sleep(time.Second)
return err
}
package message
import (
"context"
"time"
"google.golang.org/grpc"
generated "sport"
)
type Client struct {
messageClient generated.MessageServiceClient
timeout time.Duration
}
func NewClient(conn grpc.ClientConnInterface, timeout time.Duration) Client {
return Client{
messageClient: generated.NewMessageServiceClient(conn),
timeout: timeout,
}
}
func (c Client) SendMessage(ctx context.Context, message string) (*generated.MessageResponse, error) {
ctx, cancel := context.WithDeadline(ctx, time.Now().Add(c.timeout))
defer cancel()
return c.messageClient.SendMessage(ctx, &generated.MessageRequest{Text: message})
}
package main
import (
"log"
"net"
"google.golang.org/grpc"
"sport/interceptor"
"sport/message"
generated "sport"
)
func main() {
log.Println("server")
listener, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalln(err)
}
opts := []grpc.ServerOption{
grpc.ChainUnaryInterceptor(
interceptor.VerifyUnaryServer,
interceptor.LogUnaryServer,
),
}
grpcServer := grpc.NewServer(opts...)
messageServer := message.NewServer()
generated.RegisterMessageServiceServer(grpcServer, messageServer)
log.Fatalln(grpcServer.Serve(listener))
}
package interceptor
import (
"context"
"log"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)
func VerifyUnaryServer(
ctx context.Context,
req interface{},
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
) (resp interface{}, err error) {
log.Println("Verify: 1")
time.Sleep(time.Second)
md, ok := metadata.FromIncomingContext(ctx)
if !ok || len(md["client-id"]) == 0 {
return nil, status.Errorf(codes.InvalidArgument, "missing metadata")
}
if md["client-id"][0] != "client-1" {
return nil, status.Error(codes.PermissionDenied, "unexpected client")
}
res, err := handler(ctx, req)
log.Println("Verify: 2")
time.Sleep(time.Second)
return res, err
}
package interceptor
import (
"context"
"log"
"time"
"google.golang.org/grpc"
)
func LogUnaryServer(
ctx context.Context,
req interface{},
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
) (resp interface{}, err error) {
log.Println("Log: 1")
time.Sleep(time.Second)
log.Println("Logging")
res, err := handler(ctx, req)
log.Println("Log: 2")
time.Sleep(time.Second)
return res, err
}
package message
import (
"context"
generated "sport"
)
type Server struct {
generated.UnimplementedMessageServiceServer
}
func NewServer() Server {
return Server{}
}
func (s Server) SendMessage(ctx context.Context, req *generated.MessageRequest) (*generated.MessageResponse, error) {
return &generated.MessageResponse{Text: req.GetText()}, nil
}
Önce make server
daha sonra make client
komutunu çalıştırın. Her iki komut çıktısının kombinasyonu aşağıdaki gibi görünecektir. Bu, önleyicilerin hangi sırayla nasıl çalıştıklarını gösterir.
CLIENT SERVER
2020/09/14 19:11:46 Timer: 1
2020/09/14 19:11:47 Identity: 1
2020/09/14 19:11:48 Verify: 1
2020/09/14 19:11:49 Log: 1
2020/09/14 19:11:50 Log: 2
2020/09/14 19:11:51 Verify: 2
2020/09/14 19:11:52 Identity: 2
2020/09/14 19:11:53 Timer: 2