の使用AWS SDK for Go - AWS SDK for Go v2

の使用AWS SDK for Go

アプリケーションで AWS SDK for Go を使用してプログラミングを行う際の、一般的かつ推奨される方法について説明します。

サービスクライアントの作成

サービスクライアントは、サービスクライアントの Go パッケージに含まれている New または NewFromConfig 関数を使用して作成できます。これらの関数はいずれも、サービス API を呼び出すメソッドを含む Client 構造体型を返します。NewNewFromConfig はどちらもクライアントの作成用に同じ設定オプションを提供しますが、作成パターンに若干の違いがあります。詳細については、以降のセクションで説明します。

NewFromConfig

NewFromConfig 関数は、aws.Config を使用してサービスクライアントを作成するための一貫したインターフェイスを提供します。aws.Config は、config.LoadDefaultConfig を使用してロードできます。aws.Config の作成の詳細については、「SDK を設定」を参照してください。次の例では、aws.Config および NewFromConfig 関数を使用して Amazon S3 サービスクライアントを作成する方法を示しています。

import "context" import "github.com/aws/aws-sdk-go-v2/config" import "github.com/aws/aws-sdk-go-v2/service/s3" // ... cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { panic(err) } client := s3.NewFromConfig(cfg)

設定のオーバーライド

NewFromConfig には、クライアントの設定 Options 構造体を変更できる関数型引数を 1 つ以上渡すことができます。これにより、リージョンの変更や、Amazon S3 の UseAccelerate オプションといったサービス固有のオプションの変更など、特定のオーバーライドが可能になります。例:

import "context" import "github.com/aws/aws-sdk-go-v2/config" import "github.com/aws/aws-sdk-go-v2/service/s3" // ... cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { panic(err) } client := s3.NewFromConfig(cfg, func(o *s3.Options) { o.Region = "us-west-2" o.UseAccelerate = true })

クライアントの Options 値に対するオーバーライドは、NewFromConfig に渡される関数型引数の順序によって決まります。

注記

New は、クライアントのより高度な作成方法と見なされています。NewFromConfig を使用してクライアントを作成することをお勧めします。aws.Config 構造体を使用した作成が可能になるためです。これにより、アプリケーションで必要な各サービスクライアントのために Options 構造体インスタンスを個別に作成する必要がなくなります。

New 関数はクライアントコンストラクタです。クライアントパッケージの Options 構造体のみを使用してクライアントを作成するためのインターフェイスを提供します。例えば、New を使用して Amazon S3 クライアントを作成するには、次のようにします。

import "github.com/aws/aws-sdk-go-v2/aws" import "github.com/aws/aws-sdk-go-v2/credentials" import "github.com/aws/aws-sdk-go-v2/service/s3" // ... client := s3.New(s3.Options{ Region: "us-west-2", Credentials: aws.NewCredentialsCache(credentials.NewStaticCredentialsProvider(accessKey, secretKey, "")), })

設定のオーバーライド

New には、クライアントの設定 Options 構造体を変更できる関数型引数を 1 つ以上渡すことができます。これにより、リージョンの変更や、Amazon S3 の UseAccelerate オプションといったサービス固有のオプションの変更など、特定のオーバーライドが可能になります。例:

import "github.com/aws/aws-sdk-go-v2/aws" import "github.com/aws/aws-sdk-go-v2/credentials" import "github.com/aws/aws-sdk-go-v2/service/s3" // ... options := s3.Options{ Region: "us-west-2", Credentials: aws.NewCredentialsCache(credentials.NewStaticCredentialsProvider(accessKey, secretKey, "")), } client := s3.New(options, func(o *s3.Options) { o.Region = "us-east-1" o.UseAccelerate = true })

クライアントの Options 値に対するオーバーライドは、New に渡される関数型引数の順序によって決まります。

サービスオペレーションの呼び出し

サービスクライアントのインスタンスを作成した後、それを使用してサービスのオペレーションを呼び出すことができます。例えば、Amazon S3 の GetObject オペレーションを呼び出すには、次のようにします。

response, err := client.GetObject(context.TODO(), &s3.GetObjectInput{ Bucket: aws.String("amzn-s3-demo-bucket"), Key: aws.String("obj-key"), })

サービスオペレーションを呼び出すと、SDK は入力を同期的に検証し、リクエストをシリアル化し、認証情報で署名して AWS に送信します。その後、レスポンスまたはエラーを逆シリアル化します。ほとんどの場合、サービスオペレーションを直接呼び出すことができます。各サービスオペレーションのクライアントメソッドは、オペレーションのレスポンス構造体とエラーのインターフェイス型を返します。サービスオペレーションのレスポンス構造体にアクセスする前に、error 型を必ず確認してエラーの有無を判断する必要があります。

サービスオペレーションへのパラメータの受け渡し

サービスの各オペレーションは、それぞれのメソッドで context.Context 値を受け取り、リクエストの期限などを設定できます。これらの期限は SDK によって適用されます。また、各サービスオペレーションは、それぞれのサービスの Go パッケージに含まれている <OperationName>Input 構造体を受け取ります。API の入力パラメータは、オペレーションの入力構造体を使用して渡します。

オペレーションの入力構造体には、標準の Go 型 (数値型、boolean 型、文字列型、マップ型、リスト型など) のパラメータを含めることができます。より複雑な API オペレーションでは、入力パラメータのより複雑なモデル化を行う場合があります。サービス固有の構造体や列挙型などは、そのサービスの types Go パッケージに含まれています。

また、サービスによっては、Go 型のデフォルト値とユーザーが明示的に設定した値かを区別する場合があります。そのような場合、対象の型へのポインタ参照で値を渡す必要があります。数値、boolean、文字列などの標準的な Go 型では、変換を容易にするために、aws パッケージには <Type>From<Type> という便利な関数が用意されています。例えば、aws.String を使用すると、string 型を *string 型に変換して、文字列へのポインタを必要とする入力パラメータに渡すことができます。逆に、aws.ToString を使用すると、*string 型から string 型に変換できます。同時に、nil ポインタの参照先取得 (dereference) を防ぐこともできます。これらの To<Type> 関数は、サービスのレスポンスを処理する際に便利です。

次に示しているのは、Amazon S3 クライアントを使用して GetObject API を呼び出し、types パッケージと aws.<Type> ヘルパーを使用して入力を作成する方法の例です。

import "context" import "github.com/aws/aws-sdk-go-v2/config" import "github.com/aws/aws-sdk-go-v2/service/s3" import "github.com/aws/aws-sdk-go-v2/service/s3/types" // ... cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { panic(err) } client := s3.NewFromConfig(cfg) resp, err := client.GetObject(context.TODO(), &s3.GetObjectInput{ Bucket: aws.String("amzn-s3-demo-bucket"), Key: aws.String("keyName"), RequestPayer: types.RequestPayerRequester, })

オペレーション呼び出し時のクライアントオプションのオーバーライド

関数型引数を使用してクライアントを作成する際にクライアントのオペレーションのオプションを変更できるのと同様に、サービスのオペレーションメソッドの呼び出し時にも 1 つ以上の関数型引数を渡してクライアントのオプションを変更できます。このアクションはスレッドセーフであり、クライアント上の他の並行オペレーションには影響しません。

例えば、クライアントのリージョンを「us-west-2」から「us-east-1」に変更するには、次のようにします。

cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion("us-west-2")) if err != nil { log.Printf("error: %v", err) return } client := s3.NewFromConfig(cfg) params := &s3.GetObjectInput{ // ... } resp, err := client.GetObject(context.TODO(), params, func(o *Options) { o.Region = "us-east-1" })

オペレーションレスポンスの処理

サービスの各オペレーションメソッドには、レスポンスメンバーを含む出力構造体が関連付けられています。出力構造体の名前は <OperationName>Output というパターンに従っています。オペレーションによっては、出力構造体にメンバーが定義されていない場合もあります。サービスのオペレーションを呼び出した後は、error 引数の型を必ず確認して、そのオペレーション実行中のエラーの有無を判断する必要があります。返されるエラーは、クライアント側の入力検証エラーから、クライアントに返されるサービス側のエラーレスポンスまで多岐にわたります。クライアントから nil 以外のエラーが返された場合は、オペレーションの出力構造体にアクセスしないようにしてください。

例えば、オペレーションエラーをログに記録し、呼び出し元の関数から早期に制御を戻すには、次のようにします。

response, err := client.GetObject(context.TODO()) if err != nil { log.Printf("GetObject error: %v", err) return }

特定のエラータイプの確認方法を含むエラー処理の詳細については、TODO (参照先は今後提供予定) を参照してください。

io.ReadCloser を含むレスポンス

API オペレーションによっては、出力メンバーとして io.ReadCloser を含むレスポンス構造体を返す場合もあります。これに該当するのは、HTTP レスポンス本文に一部の出力要素を含める API オペレーションの場合です。

例えば、Amazon S3 の GetObject オペレーションでは、オブジェクトのペイロードにアクセスするための io.ReadCloser である Body メンバーを含むレスポンスを返します。

警告

出力メンバーの内容を使用したかどうかにかかわらず、io.ReadCloser のすべての出力メンバーに対しては Close() を必ず実行するようにしてください。この処理を実行しないと、リソースリークが発生し、以降呼び出されるオペレーションでレスポンス本文の読み取りに問題が発生する可能性があります。

resp, err := s3svc.GetObject(context.TODO(), &s3.GetObjectInput{...}) if err != nil { // handle error return } // Make sure to always close the response Body when finished defer resp.Body.Close() decoder := json.NewDecoder(resp.Body) if err := decoder.Decode(&myStruct); err != nil { // handle error return }

レスポンスメタデータ

サービスのすべてのオペレーションの出力構造体には、型 middleware.Metadatamiddleware.Metadata メンバーが含まれています。ResultMetadata は、サービスによってモデル化されていないレスポンス情報を SDK のミドルウェアが取得するために使用されます。これには RequestID などのメタデータが含まれます。例えば、AWS サポートによるリクエストのトラブルシューティングに役立つように、サービスのレスポンスに関連付けられた RequestID を取得するには、次のようにします。

import "fmt" import "log" import "github.com/aws/aws-sdk-go-v2/aws/middleware" import "github.com/aws/aws-sdk-go-v2/service/s3" // .. resp, err := client.GetObject(context.TODO(), &s3.GetObjectInput{ // ... }) if err != nil { log.Printf("error: %v", err) return } requestID, ok := middleware.GetRequestIDMetadata(resp.ResultMetadata) if !ok { fmt.Println("RequestID not included with request") } fmt.Printf("RequestID: %s\n", requestID)

サービスクライアントの同時使用

同じサービスクライアントを複数の goroutine で同時に使用して、複数のリクエストを送信できます。サービスクライアントは任意の数の goroutine で使用できます。

次の例では、Amazon S3 サービスクライアントを複数の goroutine で使用しています。この例では、2 つのオブジェクトを Amazon S3 バケットに同時にアップロードします。

import "context" import "log" import "strings" import "github.com/aws/aws-sdk-go-v2/config" import "github.com/aws/aws-sdk-go-v2/service/s3" // ... cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { log.Printf("error: %v", err) return } client := s3.NewFromConfig(cfg) type result struct { Output *s3.PutObjectOutput Err error } results := make(chan result, 2) var wg sync.WaitGroup wg.Add(2) go func() { defer wg.Done() output, err := client.PutObject(context.TODO(), &s3.PutObjectInput{ Bucket: aws.String("amzn-s3-demo-bucket"), Key: aws.String("foo"), Body: strings.NewReader("foo body content"), }) results <- result{Output: output, Err: err} }() go func() { defer wg.Done() output, err := client.PutObject(context.TODO(), &s3.PutObjectInput{ Bucket: aws.String("amzn-s3-demo-bucket"), Key: aws.String("bar"), Body: strings.NewReader("bar body content"), }) results <- result{Output: output, Err: err} }() wg.Wait() close(results) for result := range results { if result.Err != nil { log.Printf("error: %v", result.Err) continue } fmt.Printf("etag: %v", aws.ToString(result.Output.ETag)) }

オペレーションページネーターの使用

通常、アイテムのリストを取得するときは、出力構造体でトークンやマーカーを確認して、AWS サービスがすべての結果を返したかどうかを判断する必要があります。トークンやマーカーが存在する場合は、それを使用して次のページの結果をリクエストします。これらのトークンやマーカーを自分で管理する代わりに、サービスパッケージで提供されているページネーター型を使用できます。

ページネーターヘルパーは、サポートされているサービスオペレーションで使用でき、サービスクライアントの Go パッケージに含まれています。サポートされているオペレーションのページネーターを作成するには、New<OperationName>Paginator 関数を使用します。ページネーターコンストラクタは、サービスの Client、オペレーションの <OperationName>Input 入力パラメータ、および他のオプションのページネーター設定を構成するための関数型引数 (任意) を引数として受け取ります。

返されるオペレーションページネーター型を使用すれば、ページ分割されたオペレーションを簡単に反復処理できます。最終ページに到達するか、アプリケーションが探している項目を見つけるまで処理を続行できます。ページネーター型には HasMorePagesNextPage の 2 つのメソッドがあります。HasMorePages は、最初のページがまだ取得されていない場合や、追加のページが存在する場合に boolean 値の true を返します。オペレーションの最初または後続のページを取得するには、NextPage オペレーションを呼び出す必要があります。NextPagecontext.Context を受け取り、オペレーションの出力および対応するエラーを返します。クライアントのオペレーションメソッドの戻り値と同様に、返されるレスポンス構造体を使用する前に、返されるエラーを必ず確認する必要があります。「オペレーションレスポンスの処理」を参照してください。

次の例では、ListObjectsV2 ページネーターを使用して、ListObjectV2 オペレーションから最大 3 ページ分のオブジェクトキーのリストを取得しています。各ページには最大 10 個のキーが含まれており、これは Limit ページネーターオプションで定義されています。

import "context" import "log" import "github.com/aws/aws-sdk-go-v2/config" import "github.com/aws/aws-sdk-go-v2/aws" import "github.com/aws/aws-sdk-go-v2/service/s3" // ... cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { log.Printf("error: %v", err) return } client := s3.NewFromConfig(cfg) params := &s3.ListObjectsV2Input{ Bucket: aws.String("amzn-s3-demo-bucket"), } paginator := s3.NewListObjectsV2Paginator(client, params, func(o *s3.ListObjectsV2PaginatorOptions) { o.Limit = 10 }) pageNum := 0 for paginator.HasMorePages() && pageNum < 3 { output, err := paginator.NextPage(context.TODO()) if err != nil { log.Printf("error: %v", err) return } for _, value := range output.Contents { fmt.Println(*value.Key) } pageNum++ }

クライアントオペレーションメソッドと同様に、NextPage に関数型引数を 1 つ以上渡すことで、リクエストリージョンなどのクライアントオプションを変更できます。オペレーションを呼び出すときのクライアントオプションのオーバーライドの詳細については、「オペレーション呼び出し時のクライアントオプションのオーバーライド」を参照してください。

ウェーターを使用する

非同期の AWS API とやり取りする場合、特定のリソースに対してさらなるアクションを実行するために、そのリソースが使用可能になるまで待機する必要があることがよくあります。

例えば、Amazon DynamoDB の CreateTable API は TableStatus として CREATING をすぐに返しますが、ステータスが ACTIVE に変わるまでは、読み書きオペレーションを呼び出すことはできません。

テーブルのステータスを定期的にポーリングするロジックを自分で記述するのは、手間がかかりエラーの原因にもなります。ウェーターは、この複雑な処理を簡略化し、ポーリングを自動的に実行するシンプルな API です。

例えば、DynamoDB テーブルが作成されて書き込みオペレーションが可能かどうかをポーリングするために、ウェーターを使用できます。

import "context" import "fmt" import "log" import "time" import "github.com/aws/aws-sdk-go-v2/aws" import "github.com/aws/aws-sdk-go-v2/config" import "github.com/aws/aws-sdk-go-v2/service/dynamodb" // ... cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { log.Printf("error: %v", err) return } client := dynamodb.NewFromConfig(cfg) // we create a waiter instance by directly passing in a client // that satisfies the waiters client Interface. waiter := dynamodb.NewTableExistsWaiter(client) // params is the input to api operation used by the waiter params := &dynamodb.DescribeTableInput { TableName: aws.String("test-table") } // maxWaitTime is the maximum wait time, the waiter will wait for // the resource status. maxWaitTime := 5 * time.Minutes // Wait will poll until it gets the resource status, or max wait time // expires. err := waiter.Wait(context.TODO(), params, maxWaitTime) if err != nil { log.Printf("error: %v", err) return } fmt.Println("Dynamodb table is now ready for write operations")

ウェーター設定のオーバーライド

デフォルトでは、SDK は AWS サービスが各 API に対して最適と定義した最小および最大の遅延値を使用します。ウェーターの作成時やオペレーションの呼び出し時に関数型オプションを指定することで、ウェーター設定をオーバーライドできます。

例えば、ウェーターの作成時に設定をオーバーライドするには、次のようにします。

import "context" import "fmt" import "log" import "time" import "github.com/aws/aws-sdk-go-v2/aws" import "github.com/aws/aws-sdk-go-v2/config" import "github.com/aws/aws-sdk-go-v2/service/dynamodb" // ... cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { log.Printf("error: %v", err) return } client := dynamodb.NewFromConfig(cfg) // we create a waiter instance by directly passing in a client // that satisfies the waiters client Interface. waiter := dynamodb.NewTableExistsWaiter(client, func (o *dynamodb.TableExistsWaiterOptions) { // override minimum delay to 10 seconds o.MinDelay = 10 * time.Second // override maximum default delay to 300 seconds o.MaxDelay = 300 * time.Second })

各ウェーターの Wait 関数でも関数型オプションを受け取ることができます。上記の例と同様に、Wait リクエストごとにウェーター設定をオーバーライドできます。

// params is the input to api operation used by the waiter params := &dynamodb.DescribeTableInput { TableName: aws.String("test-table") } // maxWaitTime is the maximum wait time, the waiter will wait for // the resource status. maxWaitTime := 5 * time.Minutes // Wait will poll until it gets the resource status, or max wait time // expires. err := waiter.Wait(context.TODO(), params, maxWaitTime, func (o *dynamodb.TableExistsWaiterOptions) { // override minimum delay to 5 seconds o.MinDelay = 5 * time.Second // override maximum default delay to 120 seconds o.MaxDelay = 120 * time.Second }) if err != nil { log.Printf("error: %v", err) return } fmt.Println("Dynamodb table is now ready for write operations")

高度なウェーター設定のオーバーライド

再試行可能なカスタム関数を指定することで、ウェーターのデフォルト動作をカスタマイズすることもできます。ウェーター固有のオプションでは、オペレーションのミドルウェアをカスタマイズするための APIOptions も提供されています。

例えば、高度なウェーター設定のオーバーライドを定義するには、次のようにします。

import "context" import "fmt" import "log" import "time" import "github.com/aws/aws-sdk-go-v2/aws" import "github.com/aws/aws-sdk-go-v2/config" import "github.com/aws/aws-sdk-go-v2/service/dynamodb" import "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" // ... cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { log.Printf("error: %v", err) return } client := dynamodb.NewFromConfig(cfg) // custom retryable defines if a waiter state is retryable or a terminal state. // For example purposes, we will configure the waiter to not wait // if table status is returned as `UPDATING` customRetryable := func(ctx context.Context, params *dynamodb.DescribeTableInput, output *dynamodb.DescribeTableOutput, err error) (bool, error) { if output.Table != nil { if output.Table.TableStatus == types.TableStatusUpdating { // if table status is `UPDATING`, no need to wait return false, nil } } } // we create a waiter instance by directly passing in a client // that satisfies the waiters client Interface. waiter := dynamodb.NewTableExistsWaiter(client, func (o *dynamodb.TableExistsWaiterOptions) { // override the service defined waiter-behavior o.Retryable = customRetryable })