为 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 应用的最佳身份验证流程是使用服务帐号的服务器到服务器模式。App Engine 和 Compute Engine 内置了您可以使用的服务帐号,您也可以在 Google Cloud Console 中创建服务凭据。

使用服务帐号时,Google API 客户端库会在以下场景中处理身份验证和授权流程:

App Engine 应用

如果您的应用在 App Engine 上运行,它将使用 App Engine 应用的默认服务帐号进行身份验证。

只要您使用的是 Google Cloud CLI (mvn gcloud:run),则通过使用服务帐号并设置 GOOGLE_APPLICATION_CREDENTIALS 环境变量,该代码也可与 App Engine 本地模拟器搭配使用。

如需了解详情,请参阅本地或第三方主机部分。

Compute Engine 应用

如果您的应用在 Compute Engine 实例上运行,它将使用与项目关联的默认 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 应用默认凭据,因此它会选择使用相应服务帐号。

服务帐号必须获得以下角色之一:

您可以转到项目的权限页面进行查看。

Java

本示例使用 Java 版 Google API 客户端库“重试处理”部分介绍了自定义 RetryHttpInitializerWrapper 类。

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 CreateTransferClient {

  /**
   * 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) 失败的情况下,您应该实现代码,以便使用截断指数退避算法策略处理重试。

Java

本示例使用 Java 版 Google API 客户端库RetryHttpInitializerWrapper 类可为您处理重试尝试。

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


"""Command-line sample that gets a transfer job using retries
"""

import googleapiclient.discovery

def get_transfer_job_with_retries(project_id, job_name, retries):
    """Check the latest transfer operation associated with a transfer job."""
    storagetransfer = googleapiclient.discovery.build("storagetransfer", "v1")

    transferJob = (
        storagetransfer.transferJobs()
        .get(projectId=project_id, jobName=job_name)
        .execute(num_retries=retries)
    )
    print(
        "Fetched transfer job: "
        + transferJob.get("name")
        + " using {} retries".format(retries)
    )