Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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

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

Shintaro Kanno

August 03, 2022
Tweet

More Decks by Shintaro Kanno

Other Decks in Programming

Transcript

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  8. 1. 4つのRPCについて

    View Slide

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

    View Slide

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

    View Slide

  11. 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)
    }
    }

    View Slide

  12. 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
    ○ ミドルウェアの戻り値
    ○ 戻り値で関数を返す

    View Slide

  13. 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 : コンテキスト

    View Slide

  14. 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 : クライアントから受け取
    るリクエスト

    View Slide

  15. 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 : サーバー情報

    View Slide

  16. 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 : 後続の処理

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  20. 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)
    }
    }
    実際に作ったミドルウェア

    View Slide

  21. 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)
    }
    }
    実際に作ったミドルウェア

    View Slide

  22. 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)
    }
    }
    実際に作ったミドルウェア

    View Slide

  23. 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)
    }
    }
    実際に作ったミドルウェア

    View Slide

  24. 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)
    }
    }
    実際に作ったミドルウェア

    View Slide

  25. Thank you!

    View Slide