Slide 1

Slide 1 text

gRPCミドルウェアを作ってみよう

Slide 2

Slide 2 text

名前 : 官野 慎太朗 業務 : 都内のIT事業会社にて勤務 技術経験: - JavaScript/TypeScript, Go - MySQL, PostgreSQL - Docker, Kubernetes - Azure, Aws - Git, RestfulAPI, gRPC, etc… @shinshin8 @doctorkanno572

Slide 3

Slide 3 text

gRPCのミドルウェアを使ったこ とはありますか? ?

Slide 4

Slide 4 text

go-grpc-middleware(github.com/grpc-ecosystem/go-grpc-middleware) ● gRPC開発に必要なインターセプ ター、ヘルパー、ユーティリティなどを 提供 ● 認証、ロギング、リカバリ、リトライなど の機能を実現

Slide 5

Slide 5 text

提供されている以外の機能が欲しい・・・

Slide 6

Slide 6 text

! 自前でgRPCミドルウェアを 作ってみよう

Slide 7

Slide 7 text

1. 4つのRPCについて 2. Unary Server Interceptorの基本構造 3. Google Maps APIを使ったgRPCミドルウェアの実装 Agenda

Slide 8

Slide 8 text

1. 4つのRPCについて

Slide 9

Slide 9 text

● Unary RPC ○ クライアントからの1つのリクエストに対し、サーバーが1つのレスポンスをクライアントに返す。 ● Server streaming RPC ○ クライアントからの1つのリクエストに対し、サーバーが複数のレスポンスをクライアントに返す。 ● Client streaming RPC ○ クライアントからの複数のリクエストに対し、サーバーが1つのレスポンスをクライアントに返す。 ● Bidirectional RPC ○ 通信が確立後、クライアントとサーバー間で任意のタイミングでのやりとりを行う。

Slide 10

Slide 10 text

1. 2. Unary Server Interceptor の基本構造

Slide 11

Slide 11 text

Unary Server Interceptorの基本的な構成 func UnaryInterceptor() grpc.UnaryServerInterceptor { return func ( ctx context.Context, req interface {}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler ) ( resp interface {}, err error ) { // ~~~ implementation ~~~~ return handler(ctx, req) } }

Slide 12

Slide 12 text

Unary Server Interceptorの基本的な構成 func UnaryInterceptor() grpc.UnaryServerInterceptor { return func ( ctx context.Context, req interface {}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler ) ( resp interface {}, err error ) { // ~~~ implementation ~~~~ return handler(ctx, req) } } ● grpc.UnaryServerInterceptor ○ ミドルウェアの戻り値 ○ 戻り値で関数を返す

Slide 13

Slide 13 text

4つのパラメータ func UnaryInterceptor() grpc.UnaryServerInterceptor { return func ( ctx context.Context, ← ★ req interface {}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler ) ( resp interface {}, err error ) { // ~~~ implementation ~~~~ return handler(ctx, req) } } ● ctx : コンテキスト

Slide 14

Slide 14 text

4つのパラメータ func UnaryInterceptor() grpc.UnaryServerInterceptor { return func ( ctx context.Context, req interface {}, ← ★ info *grpc.UnaryServerInfo, handler grpc.UnaryHandler ) ( resp interface {}, err error ) { // ~~~ implementation ~~~~ return handler(ctx, req) } } ● ctx : コンテキスト ● req : クライアントから受け取 るリクエスト

Slide 15

Slide 15 text

4つのパラメータ func UnaryInterceptor() grpc.UnaryServerInterceptor { return func ( ctx context.Context, req interface {}, info *grpc.UnaryServerInfo, ← ★ handler grpc.UnaryHandler ) ( resp interface {}, err error ) { // ~~~ implementation ~~~~ return handler(ctx, req) } } ● ctx : コンテキスト ● req : クライアントから受け取 るリクエスト ● info : サーバー情報

Slide 16

Slide 16 text

4つのパラメータ func UnaryInterceptor() grpc.UnaryServerInterceptor { return func ( ctx context.Context, req interface {}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler ← ★ ) ( resp interface {}, err error ) { // ~~~ implementation ~~~~ return handler(ctx, req) } } ● ctx : コンテキスト ● req : クライアントから受け取 るリクエスト ● info : サーバー情報 ● handler : 後続の処理

Slide 17

Slide 17 text

意識したい処理の流れ func ( ~~~~~~~~~ ) ( ~~~~~~~~~ ) { // ミドルウェアで行いたい処理を実装 return handler(ctx, req) } 処理結果を後続の処理に受け渡す 後続の処理結果を扱う func ( ~~~~~~~~~ ) ( ~~~~~~~~~ ) { resp, err := handler(ctx, req) if err != nil { // エラーハンドリング } // 後続の処理結果をもとに実装 }

Slide 18

Slide 18 text

3. Google Maps APIを使った gRPCミドルウェアの実装

Slide 19

Slide 19 text

今回作ったもの ● Google Maps APIを使ってcontextに緯度経度を追加するミドルウェア ● Google Maps Geocode及びGeolocation APIの操作を可能にするパッケージを使用 ○ https://github.com/martinlindhe/google-geolocate ● クライアントからのリクエストcontextに位置情報が追加されている前提 ● 実際に作ったもの ○ https://github.com/shinshin8/golang-grpc-middleware

Slide 20

Slide 20 text

func GeocodeUnaryServerInterceptor(apiKey string) grpc.UnaryServerInterceptor { return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { client := geo.NewGoogleGeo(apiKey) geocode := geoctx.GetGeoInfo(ctx) res, _ := client.Geocode(geocode) if err != nil { geoctx.SetGeoLocate(ctx, 0, 0) } else { geoctx.SetGeoLocate(ctx, res.Lng, res.Lat) } return handler(ctx, err) } } 実際に作ったミドルウェア

Slide 21

Slide 21 text

func GeocodeUnaryServerInterceptor(apiKey string) grpc.UnaryServerInterceptor { return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { client := geo.NewGoogleGeo(apiKey) ←★ 引数で受け取った Google Maps API Keyかクライアントを作成 geocode := geoctx.GetGeoInfo(ctx) res, _ := client.Geocode(geocode) if err != nil { geoctx.SetGeoLocate(ctx, 0, 0) } else { geoctx.SetGeoLocate(ctx, res.Lng, res.Lat) } return handler(ctx, err) } } 実際に作ったミドルウェア

Slide 22

Slide 22 text

func GeocodeUnaryServerInterceptor(apiKey string) grpc.UnaryServerInterceptor { return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { client := geo.NewGoogleGeo(apiKey) geocode := geoctx.GetGeoInfo(ctx) ←★ contextから位置情報を取得 ※geoctx.GetGeoInfoは今回作った機能 res, _ := client.Geocode(geocode) if err != nil { geoctx.SetGeoLocate(ctx, 0, 0) } else { geoctx.SetGeoLocate(ctx, res.Lng, res.Lat) } return handler(ctx, err) } } 実際に作ったミドルウェア

Slide 23

Slide 23 text

func GeocodeUnaryServerInterceptor(apiKey string) grpc.UnaryServerInterceptor { return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { client := geo.NewGoogleGeo(apiKey) geocode := geoctx.GetGeoInfo(ctx) res, _ := client.Geocode(geocode) ← ★ 取得した位置情報から Geocodeし、緯度経度を取得 if err != nil { geoctx.SetGeoLocate(ctx, 0, 0) } else { geoctx.SetGeoLocate(ctx, res.Lng, res.Lat) } return handler(ctx, err) } } 実際に作ったミドルウェア

Slide 24

Slide 24 text

func GeocodeUnaryServerInterceptor(apiKey string) grpc.UnaryServerInterceptor { return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { client := geo.NewGoogleGeo(apiKey) geocode := geoctx.GetGeoInfo(ctx) res, _ := client.Geocode(geocode) if err != nil { geoctx.SetGeoLocate(ctx, 0, 0) } else { geoctx.SetGeoLocate(ctx, res.Lng, res.Lat) ← ★ 緯度経度をcontextに付与 ※geoctx.SetGeoLocateは今回作った機能 } return handler(ctx, err) } } 実際に作ったミドルウェア

Slide 25

Slide 25 text

Thank you!