Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Прозрачный gRPC-proxy один-ко-многим - Андрей С...
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
GopherCon Russia
April 23, 2021
Programming
170
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Прозрачный gRPC-proxy один-ко-многим - Андрей Смирнов
GopherCon Russia
April 23, 2021
More Decks by GopherCon Russia
See All by GopherCon Russia
Go Profiling from Bottom Up - Felix Geisendörfer
gopherconrussia
0
250
Learning Unsung Gotchas of Go - Rashmi Nagpal
gopherconrussia
1
300
Из Python в Go и обратно - Андрей Минкин
gopherconrussia
0
180
Оптимизация работы с PostgreSQL в Go: от 50 до 5000 RPS - Иван Осадчий
gopherconrussia
0
210
Пакет embed: распаковка знаний - Илья Данилкин
gopherconrussia
0
280
За пару мгновений до main() - Олег Ковалев
gopherconrussia
0
170
Тестирование в Go c Ginkgo и Gomega - Александр Егурнов
gopherconrussia
0
150
Building an Autoscaling HTTP Proxy for Kubernetes - Aaron Schlesinger
gopherconrussia
0
160
Designing Pluggable Idiomatic Go Applications – Mark Bates
gopherconrussia
0
81
Other Decks in Programming
See All in Programming
これからAgentCoreを触る方へトレンドはGatewayです
har1101
2
250
トークンをケチるな、設計しろ:GitHub Copilotを賢く使うコンテキスト戦略
ochtum
0
210
Datadog LLM Observabilityで実現する 安全なLLM Usage 管理
3150
0
120
ECSアプリログをFireLensでコスト削減しようとしたけど諦めた話 in Fargate×Node.js
akihisaikeda
2
4.2k
任せる範囲はこう広がった / How the Scope of AI Delegation Has Expanded
nrslib
0
160
「なぜそう決めたのか」を残し続ける仕組み ― Notion AI カスタムエージェント × Slack連携による設計判断の自動記録 - NIKKEI Tech Talk #47
niftycorp
PRO
0
230
Vue × Nuxt × Oxc どこまで使える?実運用の現在地
andpad
0
310
エンジニアと一緒にテストコードの設計と実装を改善した話
mototakatsu
0
230
1B+ /day規模のログを管理する技術
broadleaf
0
120
Oxcを導入して開発体験が向上した話
yug1224
4
340
Skillsは効率化、Agentsは"自分の拡張"——Builder時代のエージェント編成(CC Night 2026)
wemra
1
170
LLM本来の能力を解き放つサンドボックス技術とAI民主化への適用
yukukotani
3
4.6k
Featured
See All Featured
Public Speaking Without Barfing On Your Shoes - THAT 2023
reverentgeek
1
440
The Anti-SEO Checklist Checklist. Pubcon Cyber Week
ryanjones
0
170
Hiding What from Whom? A Critical Review of the History of Programming languages for Music
tomoyanonymous
2
870
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
35
2.5k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
234
17k
Google's AI Overviews - The New Search
badams
0
1k
svc-hook: hooking system calls on ARM64 by binary rewriting
retrage
2
310
Into the Great Unknown - MozCon
thekraken
41
2.6k
Large-scale JavaScript Application Architecture
addyosmani
515
110k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
The World Runs on Bad Software
bkeepers
PRO
72
12k
Deep Space Network (abreviated)
tonyrice
0
210
Transcript
Transparent gRPC Gateway in Go GopherCon Russia’21 Andrey Smirnov, Talos
Systems
About Andrey Smirnov @smira github.com/smira Go developer since 2014 Working
on Talos: revolutionary OS for clusters
Agenda Why gRPC? Why API gateway? Why Go? First iteration
in Go, problems and solutions. Transparent proxying in Go using gRPC. Cutting and concatenating protobuf messages.
Why gRPC? API, but as easy as calling a function
API Gateway/Proxy gRPC gRPC backend gRPC backend API Gateway
Use Cases for gRPC API Gateway Migrating from monolith to
smaller services (or vice versa) Migrating to new API version Common authentication or authorization layer Logging, traceability, … Non-trivial proxy logic: send one request to many backends, combine responses
gRPC API Gateway Implementation TCP loadbalancer HTTP reverse proxy (e.g.
nginx) ... Implement our own in Go (!)
Ping-pong gRPC service message Ping { string value = 1;
} message Pong { string value = 1; } service TestService { rpc PingPong(Ping) returns (Pong) {} }
Easy! (?) func (s *Proxy) connect() { s.conn = grpc.Dial(...)
s.client = pb.NewTestServiceClient(conn) } func (s *Proxy) PingPong(ctx context.Context, ping *pb.Ping) (*pb.Pong, error) { return s.client.PingPong(ctx, ping) }
gRPC metadata gRPC metadata: headers, trailers Go gRPC Client Metadata:
Metadata Go gRPC Server Metadata: Headers Trailers
Metadata handling md, _ := metadata.FromIncomingContext(ctx) outCtx := metadata.NewOutgoingContext(ctx, md)
var header, trailer metadata.MD resp, err := s.client.Ping(outCtx, ping, grpc.Header(&header), grpc.Trailer(&trailer)) grpc.SendHeader(ctx, header) grpc.SetTrailer(ctx, trailer) return resp, err
Streaming Calls Unary calls Client streaming calls Server streaming calls
Bi-directional streaming
Streaming Service message Counter { int32 counter = 1; }
service TestService { rpc Counter(Empty) returns (stream Counter) {} }
Streaming Proxy (½) ctx, cancel := context.WithCancel(srv.Context()) defer cancel() cli,
err := s.client.Counter(ctx, in) if err != nil { return err } ...
Streaming Proxy (½) for { msg, err := cli.Recv() switch
{ case err == io.EOF: return nil case err != nil: return err } err = srv.Send(msg) if err != nil { return err } }
None
Ways out Code generation Libraries, common code, ...
Protobuf Update (v2) message Ping { string value = 1;
int counter = 2; } message Pong { string value = 1; int counter = 2; } service TestService { rpc PingPong(Ping) returns (Pong) {} }
Version Mismatch gRPC backend API Gateway v1 v2 Ping value:
“foo” counter: 42 Ping value: “foo” counter: 42 Pong value: “bar” counter: 24 Pong value: “bar” counter: 24
Solution grpc.CustomCodec(grpc.Codec) grpc.UnknownServiceHandler(grpc.StreamHandler) grpc.NewClientStream(context.Context, *StreamDesc, *ClientConn, method string)
grpc.Codec type Codec interface { // Marshal returns the wire
format of v. Marshal(v interface{}) ([]byte, error) // Unmarshal parses the wire format into v. Unmarshal(data []byte, v interface{}) error }
Raw Codec type frame struct { payload []byte } func
(c *rawCodec) Marshal(v interface{}) ([]byte, error) { out, ok := v.(*frame) if !ok { return fmt.Errorf("expected frame") } return out.payload, nil } func (c *rawCodec) Unmarshal(data []byte, v interface{}) error { dst, ok := v.(*frame) if !ok { return fmt.Errorf("expected frame")} dst.payload = data return nil }
grpc.UnknownServiceHandler grpc.NewServer( grpc.CustomCodec(proxy.Codec()), grpc.UnknownServiceHandler(handler)) func handler(srv interface{}, serverStream grpc.ServerStream) error
{ fullMethodName, ok := grpc.MethodFromServerStream(serverStream) ... }
grpc.NewClientStream func handler(srv interface{}, serverStream grpc.ServerStream) error { conn, err
= grpc.Dial(addr, grpc.WithCodec(proxy.Codec())) clientStream, err = grpc.NewClientStream( ctx, &grpc.StreamDesc{ ServerStreams: true, ClientStreams: true, }, conn, fullMethodName) // copy clientStream <> serverStream }
Transparent gRPC Proxy Flow API Gateway grpcServerStream grpcClientStream Recv() Recv()
Send() Send()
Proxying one → many Aggregating responses Encoding errors Attributing result
to a backend
Response Metadata message ResponseMetadata { string upstream_node = 1; string
upstream_error = 2; } message Pong { ResponseMetadata metadata = 99; string value = 1; }
Enriching Response gRPC backend node-1 API Gateway Pong value: “bar”
Pong value: “bar” metadata: upstream_node: node_1
Protobuf Glue Pong value: “bar” (1) Pong value: “bar” (1)
metadata: (99) upstream_node: node_1 (1) Pong metadata: (99) upstream_node: node_1 (1) (type: bytes, field: 1, length: 3): “bar” (type: bytes, field: 99, length: N): [(type: bytes, field: 1, length: 6): “node_1”] (type: bytes, field: 1, length: 3): “bar” (type: bytes, field: 99, length: N): [(type: bytes, field: 1, length: 6): “node_1”] protobuf serialization: messages:
Embedding Errors gRPC backend API Gateway Ping value: “foo” Pong
metadata: upstream_node: node_1 upstream_error: ECONNREFUSED connection refused
Server Streaming API Gateway Ping value: “foo” Pong metadata: upstream_node:
node_1 upstream_error: ECONNREFUSED Pong value: “bar” metadata: upstream_node: node_2
Unary Calls API Gateway Ping value: “foo” Pong metadata: upstream_node:
node_1 upstream_error: ECONNREFUSED Pong value: “bar” metadata: upstream_node: node_2
Unary Protobuf Definition message ResponseMetadata { string upstream_node = 99;
string upstream_error = 100; } message Pong { ResponseMetadata metadata = 99; string value = 1; } message PongResponse { repeated Pong messages = 1; }
Protobuf Scissors PongResponse messages: (1) - Pong: value: “bar” (1)
(type: bytes, field: 1, length: N): [(type: bytes, field: 1, length: 3): bar] PongResponse messages: (1) - Pong: value: “bar” (1) metadata: (99) u_node: node_1 (1) (type: bytes, field: 1, length: N’): [(type: bytes, field: 1, length: 3): bar (type: bytes: field: 99, length: K): [(type: bytes: field: 1, length: 6): node_1 ]
grpc-proxy library https://github.com/talos-systems/grpc-proxy https://pkg.go.dev/github.com/talos-systems/grpc-proxy Thank you! @smira (https://github.com/smira) Talos Systems
Q&A