Google API 라이브러리의 Storage Transfer Service 클라이언트 만들기

Storage Transfer Service에 요청을 하려면 먼저 Storage Transfer Service API가 프로젝트에 사용 설정되어야 하고 애플리케이션이 OAuth 2.0 프로토콜을 사용하여 승인을 설정해야 합니다. 또한 요청이 실패할 경우 재시도를 처리하도록 지수 백오프도 구현하는 것이 좋습니다.

서비스 사용 설정

Google API 라이브러리를 통해 Storage Transfer Service에 액세스하거나 직접 Storage Transfer Service API를 사용하려면 Google Storage Transfer API를 사용 설정해야 합니다. 다음 안내를 따르세요. Google Storage Transfer API를 사용 설정합니다.

API 사용 설정

인증 흐름

모든 Storage Transfer Service 작업이 사람의 개입이나 사용자의 동의를 필요로 하지 않고 이루어지므로, Storage Transfer Service 애플리케이션에서 최선의 인증 흐름은 서비스 계정을 사용하는 서버 간 인증입니다. Google App Engine과 Google Compute Engine에는 활용할 수 있는 서비스 계정이 기본 제공되거나 Google Cloud Console에서 서비스 사용자 인증 정보를 만들 수 있습니다.

서비스 계정을 사용하면 다음 시나리오에서 사용자 대신 Google API 클라이언트 라이브러리가 인증 및 승인 프로세스를 처리합니다.

Google App Engine 애플리케이션

애플리케이션이 App Engine에서 실행되는 경우 App Engine 애플리케이션의 기본 서비스 계정을 사용하여 인증합니다.

참고: 서비스 계정을 사용하고 GOOGLE_APPLICATION_CREDENTIALS 환경 변수를 설정하여 Google Cloud SDK(mvn gcloud:run)를 사용하면 코드를 App Engine 로컬 에뮬레이터에서도 사용할 수 있습니다. 자세한 내용은 로컬 또는 타사 호스트를 참조하세요.

Google Compute Engine 애플리케이션

애플리케이션이 Google Compute Engine 인스턴스에서 실행되는 경우 프로젝트와 연결된 기본 Compute Engine 서비스 계정을 사용하여 인증합니다.

로컬 또는 타사 호스트

로컬 클라이언트를 실행하거나 프로그램을 Google Cloud가 아닌 환경에서 실행할 경우 새로운 서비스 계정을 만들고 JSON 키 파일을 다운로드한 후 환경 변수 GOOGLE_APPLICATION_CREDENTIALS가 JSON 키 파일을 가리키도록 설정합니다. 자세한 내용은 애플리케이션 기본 사용자 인증 정보 작동 방식을 참조하세요.

승인 범위

OAuth 2.0을 사용하여 Storage Transfer Service에 액세스하는 애플리케이션은 cloud-platform 승인 범위를 지정해야 합니다.

범위 의미
https://www.googleapis.com/auth/cloud-platform 전체 액세스 권한

클라이언트 만들기

아래의 샘플 코드를 사용하여 인증 흐름에 설명된 환경에서 Storage Transfer Service 클라이언트를 만들 수 있습니다. 이 코드는 Google 애플리케이션 기본 사용자 인증 정보를 사용하므로, 사용하기에 적절한 서비스 계정을 선택합니다.

서비스 계정에는 다음 역할 중 하나가 할당되어야 합니다.

  • roles/owner
  • roles/editor
  • roles/storagetransfer.admin
  • roles/storagetransfer.user
  • 최소한의 roles/storagetransfer.user 권한을 포함하는 커스텀 역할입니다.

    프로젝트 수준 권한 추가 및 보기에 대한 자세한 내용은 프로젝트에 IAM 권한 사용을 참조하세요.

프로젝트의 권한 페이지로 이동하여 확인할 수 있습니다.

자바

이 샘플에서는 자바용 Google API 클라이언트 라이브러리를 사용합니다. 커스텀 RetryHttpInitializerWrapper 클래스는 재시도 처리 섹션에서 설명됩니다.


package com.google.cloud.storage.storagetransfer.samples;

import com.google.api.client.googleapis.util.Utils;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.services.storagetransfer.v1.Storagetransfer;
import com.google.api.services.storagetransfer.v1.StoragetransferScopes;
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.common.base.Preconditions;
import java.io.IOException;

/** Create a client to make calls to Storage Transfer API. */
public final class TransferClientCreator {

  /**
   * Create a Storage Transfer client using application default credentials and other default
   * settings.
   *
   * @return a Storage Transfer client
   * @throws IOException there was an error obtaining application default credentials
   */
  public static Storagetransfer createStorageTransferClient() throws IOException {
    HttpTransport httpTransport = Utils.getDefaultTransport();
    JsonFactory jsonFactory = Utils.getDefaultJsonFactory();
    GoogleCredentials credential = GoogleCredentials.getApplicationDefault();
    return createStorageTransferClient(httpTransport, jsonFactory, credential);
  }

  /**
   * Create a Storage Transfer client using user-supplied credentials and other settings.
   *
   * @param httpTransport a user-supplied HttpTransport
   * @param jsonFactory a user-supplied JsonFactory
   * @param credential a user-supplied Google credential
   * @return a Storage Transfer client
   */
  public static Storagetransfer createStorageTransferClient(
      HttpTransport httpTransport, JsonFactory jsonFactory, GoogleCredentials credential) {
    Preconditions.checkNotNull(httpTransport);
    Preconditions.checkNotNull(jsonFactory);
    Preconditions.checkNotNull(credential);

    // In some cases, you need to add the scope explicitly.
    if (credential.createScopedRequired()) {
      credential = credential.createScoped(StoragetransferScopes.all());
    }
    // Please use custom HttpRequestInitializer for automatic
    // retry upon failures. We provide a simple reference
    // implementation in the "Retry Handling" section.
    HttpRequestInitializer initializer = new HttpCredentialsAdapter(credential);
    return new Storagetransfer.Builder(httpTransport, jsonFactory, initializer)
        .setApplicationName("storagetransfer-sample")
        .build();
  }
}

Python

이 샘플에서는 Python용 Google API 클라이언트 라이브러리를 사용합니다.

import googleapiclient.discovery

def create_transfer_client():
    return googleapiclient.discovery.build('storagetransfer', 'v1')

재시도 처리

RPC 실패가 발생하면 지수 백오프 전략을 사용하여 재시도를 처리하는 코드를 구현해야 합니다.

자바

이 샘플에서는 자바용 Google API 클라이언트 라이브러리를 사용합니다. RetryHttpInitializerWrapper 클래스가 사용자 대신 재시도를 처리합니다.


package com.google.cloud.storage.storagetransfer.samples;

import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.http.HttpBackOffIOExceptionHandler;
import com.google.api.client.http.HttpBackOffUnsuccessfulResponseHandler;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpUnsuccessfulResponseHandler;
import com.google.api.client.util.ExponentialBackOff;
import com.google.api.client.util.Sleeper;
import com.google.common.base.Preconditions;
import java.io.IOException;
import java.util.logging.Logger;

/**
 * RetryHttpInitializerWrapper will automatically retry upon RPC failures, preserving the
 * auto-refresh behavior of the Google Credentials.
 */
public class RetryHttpInitializerWrapper implements HttpRequestInitializer {

  private static final Logger LOG = Logger.getLogger(RetryHttpInitializerWrapper.class.getName());
  private final Credential wrappedCredential;
  private final Sleeper sleeper;
  private static final int MILLIS_PER_MINUTE = 60 * 1000;

  /**
   * A constructor using the default Sleeper.
   *
   * @param wrappedCredential the credential used to authenticate with a Google Cloud Platform
   *     project
   */
  public RetryHttpInitializerWrapper(Credential wrappedCredential) {
    this(wrappedCredential, Sleeper.DEFAULT);
  }

  /**
   * A constructor used only for testing.
   *
   * @param wrappedCredential the credential used to authenticate with a Google Cloud Platform
   *     project
   * @param sleeper a user-supplied Sleeper
   */
  RetryHttpInitializerWrapper(Credential wrappedCredential, Sleeper sleeper) {
    this.wrappedCredential = Preconditions.checkNotNull(wrappedCredential);
    this.sleeper = sleeper;
  }

  /**
   * Initialize an HttpRequest.
   *
   * @param request an HttpRequest that should be initialized
   */
  public void initialize(HttpRequest request) {
    request.setReadTimeout(2 * MILLIS_PER_MINUTE); // 2 minutes read timeout
    final HttpUnsuccessfulResponseHandler backoffHandler =
        new HttpBackOffUnsuccessfulResponseHandler(new ExponentialBackOff()).setSleeper(sleeper);
    request.setInterceptor(wrappedCredential);
    request.setUnsuccessfulResponseHandler(
        new HttpUnsuccessfulResponseHandler() {
          public boolean handleResponse(
              final HttpRequest request, final HttpResponse response, final boolean supportsRetry)
              throws IOException {
            if (wrappedCredential.handleResponse(request, response, supportsRetry)) {
              // If credential decides it can handle it, the return code or message indicated
              // something specific to authentication, and no backoff is desired.
              return true;
            } else if (backoffHandler.handleResponse(request, response, supportsRetry)) {
              // Otherwise, we defer to the judgement of our internal backoff handler.
              LOG.info("Retrying " + request.getUrl().toString());
              return true;
            } else {
              return false;
            }
          }
        });
    request.setIOExceptionHandler(
        new HttpBackOffIOExceptionHandler(new ExponentialBackOff()).setSleeper(sleeper));
  }
}

Python

재시도 가능한 실패가 발생한 경우 num_retries=n 인수를 라이브러리 execute 메서드에 전달하여 지수 백오프를 재시도할 수 있습니다.

resp = client.projects().subscriptions().pull(
subscription=subscription, body=body).execute(num_retries=3)