Slide 1

Slide 1 text

NAKANOSHIMA.DEV #28 – AWS SDK FOR JAVA © 2022, Amazon Web Services, Inc. or its affiliates. © 2022, Amazon Web Services, Inc. or its affiliates. AWS SDKの裏側を見てみよう! 王 力捷 N A K A N O S H I M A . D E V # 2 8 Amazon Web Services Japan Solutions Architect 〜 AWS SDK for Go (v1) を例にとって 〜

Slide 2

Slide 2 text

© 2022, Amazon Web Services, Inc. or its Affiliates. ⾃⼰紹介 王 ⼒捷(おう りきしょう) • Solutions Architect 好きなAWSサービス • Amazon SageMaker • Amazon DevOps Guru

Slide 3

Slide 3 text

© 2022, Amazon Web Services, Inc. or its Affiliates. アジェンダ o AWS SDK for Go v1 に含まれるコンポーネント紹介 o SDK の基本設定の裏側(Session, Service client) o API リクエストを送るまでの裏側(Request) o AWS SDK を利⽤する際の Tips 3 AWS SDKの中でどのような処理をしているのか考えたことなかったけれど 興味があるという方向けに、ざっくりとして説明していきます!

Slide 4

Slide 4 text

© 2022, Amazon Web Services, Inc. or its Affiliates. AWS SDKの使い⽅のイメージ 4

Slide 5

Slide 5 text

© 2022, Amazon Web Services, Inc. or its Affiliates. AWS SDK の実際の処理 5 (サービス、リージョンごと に異なる)

Slide 6

Slide 6 text

© 2022, Amazon Web Services, Inc. or its Affiliates. 今回扱うAWS SDKのバージョン o AWS SDK for Go v1 (v.1.42.35) o AWS SDK for Go v1 を例として使う理由︓ o 処理が AWS SDK として完結しており、構造を把握しやすい 6

Slide 7

Slide 7 text

© 2022, Amazon Web Services, Inc. or its Affiliates. package main import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/dynamodb" ) func main() { sess := session.NewSession() svc := dynamodb.New(sess) result, err := svc.GetItem(&dynamodb.GetItemInput{ TableName: aws.String(tableName), Key: map[string]*dynamodb.AttributeValue{ "Year": { N: aws.String(movieYear), }, "Title": { S: aws.String(movieName), }, }, }) } AWS SDK for Go v1のコード例 7

Slide 8

Slide 8 text

© 2022, Amazon Web Services, Inc. or its Affiliates. package main import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/dynamodb" ) func main() { sess := session.NewSession() svc := dynamodb.New(sess) result, err := svc.GetItem(&dynamodb.GetItemInput{ TableName: aws.String(tableName), Key: map[string]*dynamodb.AttributeValue{ "Year": { N: aws.String(movieYear), }, "Title": { S: aws.String(movieName), }, }, }) } AWS SDK for Go v1のコード例 8 Session Service client Request

Slide 9

Slide 9 text

© 2022, Amazon Web Services, Inc. or its Affiliates. AWS SDK for Go v1 の内部処理 o 設定読み込み o HTTPクライアントを 設定 9 Session Service client Request o AWS APIエンドポイン トの設定 o AWS API リクエスト送 信に必要な情報の収集 o HTTPリクエストボディ ・ヘッダー作成 o AWS認証情報の取得 o SigV4署名の追加 o HTTPリクエストの送信 o リトライ判定 基本設定 AWSサービス専⽤の クライアント AWS APIリクエストの管理

Slide 10

Slide 10 text

© 2022, Amazon Web Services, Inc. or its Affiliates. Session による基本設定

Slide 11

Slide 11 text

© 2022, Amazon Web Services, Inc. or its Affiliates. Session を作成する処理の全体像 11 └── session.NewSession() // Session作成を呼び出す関数 └── session.NewSessionWithOptions() │ // Option付きでSession作成を呼び出す関数 ├── session.loadSharedEnvConfig() │ // 環境変数から設定(envCfg)を読み取る関数 └── session.newSession() // Session作成を行う関数 ├── defaults.Config() // デフォルト設定(cfg)を生成する変数 ├── session.loadSharedConfig() │ // 共有設定(sharedCfg)を読み込む関数 ├── session.mergeConfigSrcs() // 設定の統合を行う関数 └── defaults.Handlers() // デフォルトハンドラーを生成する変数

Slide 12

Slide 12 text

© 2022, Amazon Web Services, Inc. or its Affiliates. Sessionに反映される4種類の設定(cfg) o cfg: デフォルト設定(⼀部のみ) o userCfg : session.NewSession() 関数を呼んだ際に引数で渡される設定 o envCfg : AWS_REGION 等、環境変数から読み込んだ設定 o sharedCfg : ~/.aws/config や ~/.aws/credentials などの設定ファイル から読み込んだ設定 設定が重複した際の、基本的な優先順位 userCfg > (cfg) > envCfg > sharedCfg これらの設定は session.mergeConfigSrcs() 関数によって統合される 12

Slide 13

Slide 13 text

© 2022, Amazon Web Services, Inc. or its Affiliates. HTTP クライアントは Session で作成される defaults.Config() 関数内で HTTP クライアントが作成される https://github.com/aws/aws-sdk-go/blob/v1.42.35/aws/defaults/defaults.go#L48:L64 13 func Config() *aws.Config { return aws.NewConfig(). WithCredentials(credentials.AnonymousCredentials). WithRegion(os.Getenv("AWS_REGION")). WithHTTPClient(http.DefaultClient). WithMaxRetries(aws.UseServiceDefaultRetries). WithLogger(aws.NewDefaultLogger()). WithLogLevel(aws.LogOff). WithEndpointResolver(endpoints.DefaultResolver()) } HTTP クライアントを共有することで、コネクションの再利用による効率化が 可能に

Slide 14

Slide 14 text

© 2022, Amazon Web Services, Inc. or its Affiliates. Service client によるサービス毎のクライアント 作成

Slide 15

Slide 15 text

© 2022, Amazon Web Services, Inc. or its Affiliates. Service client の作成 o Service client は、AWSサービスごとに作成される。 o AWSサービスごとにクライアントを分ける理由 o AWSサービスごとにAPIエンドポイントが異なるため o AWSサービスごとにメソッドを分けられるため 15 svc := dynamodb.New(sess) サービス毎のライブラリ Session(基本設定)

Slide 16

Slide 16 text

© 2022, Amazon Web Services, Inc. or its Affiliates. Service client を作成する処理の全体像 16 └── dynamodb.New() // DynamoDB 用のService Client作成を呼び出す関数 ├── Session.ClientConfig() // Sessionの設定を取得する関数 └── dynamodb.newClient() // Client作成を行う関数

Slide 17

Slide 17 text

© 2022, Amazon Web Services, Inc. or its Affiliates. 作成される Service client https://github.com/aws/aws-sdk-go/blob/v1.42.35/service/dynamodb/service.go#L61:L98 17 func newClient(cfg aws.Config, handlers request.Handlers, partitionID, endpoint, signingRegion, signingName, resolvedRegion string) *DynamoDB { svc := &DynamoDB{ Client: client.New( cfg, metadata.ClientInfo{ ServiceName: ServiceName, ServiceID: ServiceID, SigningName: signingName, SigningRegion: signingRegion, PartitionID: partitionID, Endpoint: endpoint, APIVersion: "2012-08-10", ResolvedRegion: resolvedRegion, JSONVersion: "1.0", TargetPrefix: "DynamoDB_20120810", }, handlers, ), } (〜省略〜)

Slide 18

Slide 18 text

© 2022, Amazon Web Services, Inc. or its Affiliates. Request による AWS APIリクエスト送信

Slide 19

Slide 19 text

© 2022, Amazon Web Services, Inc. or its Affiliates. Request の利⽤例 19 result, err := svc.GetItem(&dynamodb.GetItemInput{ TableName: aws.String(tableName), Key: map[string]*dynamodb.AttributeValue{ "Year": { N: aws.String(movieYear), }, "Title": { S: aws.String(movieName), }, }, }) API リクエストを送るためのメソッド 内部で Request というコンポーネントが作成され、 API リクエストが作成・送信される API リクエスト の内容

Slide 20

Slide 20 text

© 2022, Amazon Web Services, Inc. or its Affiliates. Request による AWS APIリクエスト送信の流れ 20 URL, メソッド ヘッダー ボディ URL, メソッド ① Request 作成 各種情報 ② HTTP リクエスト ヘッダー, ボディ作成 URL, メソッド ヘッダー ボディ ③ SigV4 署名付与 URL, メソッド ヘッダー ボディ ④ HTTP リクエスト 送信 (含リトライ処理)

Slide 21

Slide 21 text

© 2022, Amazon Web Services, Inc. or its Affiliates. API リクエスト⽣成のイメージ 21 <材料> operation(リクエストの種類) op := &request.Operation{ Name: opGetItem, // 中身は “GetItem” という文字列 HTTPMethod: "POST", HTTPPath: "/", } params(リクエストの内容) &dynamodb.GetItemInput{ TableName: aws.String(tableName), Key: map[string]*dynamodb.AttributeValue{ "Year": { N: aws.String(movieYear), }, "Title": { S: aws.String(movieName), }, }, }

Slide 22

Slide 22 text

© 2022, Amazon Web Services, Inc. or its Affiliates. API リクエスト⽣成のイメージ 22 <生成したい API リクエスト> POST / HTTP/1.1 Host: dynamodb..; Accept-Encoding: identity Content-Length: User-Agent: Content-Type: application/x-amz-json-1.0 Authorization: AWS4-HMAC-SHA256 Credential=, SignedHeaders=, Signature= X-Amz-Date: X-Amz-Target: DynamoDB_20120810.GetItem { "TableName": "Movies", "Key": { "Year": { "N": "2015" }, "Title": { "S": "The Big New Movie" } } } ヘッダー等 ボディ

Slide 23

Slide 23 text

© 2022, Amazon Web Services, Inc. or its Affiliates. API リクエストを送信するまでの処理リスト = Handlers 23 type Handlers struct { Validate HandlerList Build HandlerList BuildStream HandlerList Sign HandlerList Send HandlerList ValidateResponse HandlerList Unmarshal HandlerList UnmarshalStream HandlerList UnmarshalMeta HandlerList UnmarshalError HandlerList Retry HandlerList AfterRetry HandlerList CompleteAttempt HandlerList Complete HandlerList }

Slide 24

Slide 24 text

© 2022, Amazon Web Services, Inc. or its Affiliates. API リクエストを送信するまでの処理の全体像 24 DynamoDB.GetItem() // GetItem リクエストの作成・送信・結果取得を行うメソッド ├── DynamoDB.GetItemRequest() // GetItem 用の Request 作成を行うメソッド │ └── DynamoDB.newRequest() // Client.NewRequest のラッパーメソッド │ └── Client.NewRequest() // 新規 Request を作成 └── Request.Send() // HTTP リクエスト作成・リクエスト送信を行うメソッド ├── Request.Sign() // HTTP リクエスト作成・署名を行うメソッド │ ├── Request.Build() // HTTP リクエストの検証・作成を行うメソッド │ │ └── Request.Handlers.Build.Run() │ │ │ // HTTP リクエスト作成を行う HandlerList の実行 │ │ └── jsonrpc.Build() // HTTP リクエストボディ・ヘッダの作成を行う関数 │ └── (★) Request.Handlers.Sign.Run() │ │ // HTTP リクエストへの署名付与を行う HandlerList の実行 │ └── Signer.signWithBody() // 署名に利用する AWS 認証情報を取得するメソッド │ └── signingCtx.build() // SigV4 署名を生成・付与するメソッド ├── Request.sendRequest() // HTTP リクエストの送信・レスポンスの受信を行うメソッド └── (★) Request.Handlers.AfterRetry.Run(r) // リトライ判定、リトライ間隔設定の実行 └── corehandlers.AfterRetryHandler // リトライ判定、リトライ間隔設定を行う Handler

Slide 25

Slide 25 text

© 2022, Amazon Web Services, Inc. or its Affiliates. Request による AWS APIリクエスト送信の流れ 25 URL, メソッド ヘッダー ボディ URL, メソッド ① Request 作成 各種情報 ② HTTP リクエスト ヘッダー, ボディ作成 URL, メソッド ヘッダー ボディ ③ SigV4 署名付与 URL, メソッド ヘッダー ボディ ④ HTTP リクエスト 送信 (含リトライ処理)

Slide 26

Slide 26 text

© 2022, Amazon Web Services, Inc. or its Affiliates. Request SigV4 署名付与の流れ 1. HTTP リクエストの内容 (リクエスト先、ヘッダー、ボディなど) を整形し、 SHA256 などのハッシュアルゴリズムでハッシュ値を計算 2. 1 で計算したハッシュ値とハッシュアルゴリズム名などのメタ情報を合わせ て署名⽂字列を⽣成 3. HMAC アルゴリズムを利⽤し、AWS認証情報やリージョン名などの情報を ハッシュキーとして署名⽂字列からSigV4署名を⽣成 4. HTTP リクエストヘッダもしくはクエリ⽂字列としてSigV4署名を付与 26

Slide 27

Slide 27 text

© 2022, Amazon Web Services, Inc. or its Affiliates. SigV4署名ヘッダの例 27 Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east- 1/iam/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5 d7

Slide 28

Slide 28 text

© 2022, Amazon Web Services, Inc. or its Affiliates. AWS 認証情報の取得① AWS 認証情報の取得は、SigV4の署名作成時に⾏われる https://github.com/aws/aws-sdk-go/blob/v1.42.35/aws/signer/v4/v4.go#L315:L374 28 var err error ctx.credValues, err = v4.Credentials.GetWithContext(requestContext(r)) if err != nil { return http.Header{}, err } ctx.sanitizeHostForHeader() ctx.assignAmzQueryValues() if err := ctx.build(v4.DisableHeaderHoisting); err != nil { return nil, err }

Slide 29

Slide 29 text

© 2022, Amazon Web Services, Inc. or its Affiliates. AWS 認証情報の取得② 29 func (c *Credentials) GetWithContext(ctx Context) (Value, error) { // Check if credentials are cached, and not expired. select { case curCreds, ok := <-c.asyncIsExpired(): if ok { return curCreds, nil } case <-ctx.Done(): return Value{}, awserr.New("RequestCanceled", "request context canceled", ctx.Err()) } resCh := c.sf.DoChan("", func() (interface{}, error) { return c.singleRetrieve(&suppressedContext{ctx}) }) select { case res := <-resCh: return res.Val.(Value), res.Err case <-ctx.Done(): return Value{}, awserr.New("RequestCanceled", "request context canceled", ctx.Err()) } } IUUQTHJUIVCDPNBXTBXTTELHPCMPCWBXTDSFEFOUJBMTDSFEFOUJBMTHP--

Slide 30

Slide 30 text

© 2022, Amazon Web Services, Inc. or its Affiliates. API リクエスト送信のリトライ処理 o エクスポネンシャルバックオフ が採⽤されている o 1 回⽬の再試⾏の前に 50 ミリ秒、2 回⽬の前に 100 ミリ秒、3 回⽬の前 に 200 ミリ秒、というように指数関数的に待機時間を増やす 30

Slide 31

Slide 31 text

© 2022, Amazon Web Services, Inc. or its Affiliates. エクスポネンシャルバックオフの実装 (defaultRetryer) 31 // Logic to cap the retry count based on the minDelay provided actualRetryCount := int(math.Log2(float64(minDelay))) + 1 if actualRetryCount < 63-retryCount { delay = time.Duration(1< maxDelay { delay = getJitterDelay(maxDelay / 2) } } else { delay = getJitterDelay(maxDelay / 2) } return delay + initialDelay

Slide 32

Slide 32 text

© 2020, Amazon Web Services, Inc. or its Affiliates. All rights reserved. AWS SDK for Go (v1) を利⽤する際の Tips

Slide 33

Slide 33 text

© 2022, Amazon Web Services, Inc. or its Affiliates. AWS SDK for Go (v1) を利⽤する際の Tips 33 o Session, Service client は使い回すことが推奨される o Session を使い回すことで、HTTP Client, AWS認証情報を再利⽤できる o Service client を使い回すことで、必要なhandlersのセットを共⽤できる o EC2などで利⽤する際認証情報は⾃動で更新されるので、開発者側で気にする 必要はないです︕

Slide 34

Slide 34 text

© 2022, Amazon Web Services, Inc. or its Affiliates. (参考資料) このセッションは、以下のブログ記事をもとにお話ししました。 o AWS SDK の裏側を⾒てみよう ! ~AWS SDK for Go (v1) のコードとともに (前編) - builders.flash o AWS SDK の裏側を⾒てみよう ! ~AWS SDK for Go (v1) のソースコードと 共に (後編) - builders.flash 34

Slide 35

Slide 35 text

© 2020, Amazon Web Services, Inc. or its Affiliates. All rights reserved. © 2020, Amazon Web Services, Inc. or its Affiliates. All rights reserved. Thank you!