【问题标题】:What are best practices for using BigQuery from App Engine Standard?在 App Engine Standard 中使用 BigQuery 的最佳做法是什么?
【发布时间】:2018-02-01 21:45:41
【问题描述】:

我使用的是 App Engine 标准环境(自动缩放),这意味着我有 10 分钟的时间限制取消请求。

目标是定期从 BigQuery 查询数据,并为每条记录在任务队列中创建一个任务,以便可以在后台处理记录。

https://cloud.google.com/bigquery/create-simple-app-api 状态的说明等待这样的工作:

// Create a job ID so that we can safely retry.
JobId jobId = JobId.of(UUID.randomUUID().toString());
Job queryJob = bigquery.create(JobInfo.newBuilder(queryConfig).setJobId(jobId).build());

// Wait for the query to complete.
queryJob = queryJob.waitFor();

问题是 10 分钟的限制,因为 BigQuery 查询是在后台处理的,可能需要一些时间才能获得结果,所以我可能无法在同一个端点调用中处理响应。

  • 有没有办法在查询准备就绪时从 URL 接收来自 BigQuery 的回调?
  • 在 App Engine Standard 中是否有更智能的方式来处理来自 BigQuery 的数据?

我知道我可以配置 App Engine 以延长每个请求的最长时间,但这几乎不是解决方案。

【问题讨论】:

  • 很遗憾,无法从 BigQuery 获得回电。你对查询/作业的结果做了什么?也就是说,您能否再解释一下为什么需要等待工作完成。
  • @GrahamPolley 我们的服务器将使用数据流式传输到 BigQuery(与电话通话分钟数相比),然后使用查询(例如按参与者)合并数据。我计划在任务队列中为每个这样的合并记录创建一个任务,以便进一步处理它。它与 Google 的 App Engine 计费系统非常相似,在该系统中,数据是从服务器收集、整合,然后在大约 5 分钟的时间间隔内决定给定资源是否可用。
  • 因此,本质上您需要安排一些 BigQuery 作业/查询来处理一些数据,并将结果写入新表。对吗?
  • 是的,这是正确的,到目前为止,这也有效(我已对聚合和重复数据删除进行了编码)。问题是需要在 App Engine 标准环境中进一步处理数据。我猜从表中提取数据并将记录标记为已提取不是一个好的选择。我错过了每次创建记录时都会调用 URL 的钩子或其他东西,因此 App Engine 可以逐条记录进一步处理数据。
  • 不太清楚你所说的 10 分钟限制是什么意思。 GAE 请求处理程序有 only 60s 返回响应,否则返回 500。另一方面,Task Schedulers 有 10 分钟(如果您选择手动自动缩放)或长达 24 小时(基本和自动缩放)。这意味着您可以使用请求处理程序添加计划任务以在运行 BQ 查询的后台运行。

标签: google-app-engine google-bigquery


【解决方案1】:

最好的选择是像 BQ 那样处理长时间运行的任务:提供作业 ID 并允许客户端查询它,在查询尚未完成时返回 202,并在结果完成后返回 200准备好被客户端消费了。

此外,202 可以返回一个主体,因此您可以为客户端设置不同的状态(例如“排队”、“运行”、“处理结果”……)。

在服务器端,您启动一​​个查询,并在 BQ 返回一个作业 ID 后立即将其存储在某个持久存储中(我会选择 Datastore,但也可以是 memcache,一个 cloudSQL 实例,甚至是GCS 中的文件)。

然后你只需要创建一个cron job 来检查 BQ 的未完成查询的状态,并相应地更新它们在持久存储中的状态。 BQ 作业完成后,您可以检索结果并存储它们,以便在客户端检查您的服务时准备好。

例如,这是您应该在您的应用程序中执行的 BQ API 查询(这里使用 curl 提供示例,您可以稍后使用 idiomatic libraries 翻译成任何语言):

  1. 创建作业,从响应中检索作业 ID,并将其存储:

    PROJECT=$(gcloud config get-value project)
    QUERY='SELECT * FROM `bigquery-samples.wikipedia_benchmark.Wiki1k` limit 0'
    curl -H"Authorization: Bearer $(gcloud auth print-access-token)" -H'content-type:application/json' https://www.googleapis.com/bigquery/v2/projects/$PROJECT/jobs -d"
    {
     \"configuration\": {
      \"query\": {
       \"query\": \"$QUERY\",
       \"useLegacySql\": false
      }
     },
     \"jobReference\": {
      \"projectId\": \"$PROJECT\"
     }
    }"|jq -r .jobReference.jobId >> running_jobs
    
  2. 继续查询 BQ API 以了解作业状态。 (这可能是你的 cron 工作):

    for job in $(cat running_jobs); do
      if [ $(curl -H"Authorization: Bearer $(gcloud auth print-access-token)" https://www.googleapis.com/bigquery/v2/projects/$PROJECT/jobs/$job|jq -r .status.state) = "DONE" ]; then
        # here your processing part including your callback
        # then remove the job from the list of running jobs
        sed -i "/$job/d" ./running_jobs
      fi
    done
    

你可以在cloud shell试试这个。

【讨论】:

    【解决方案2】:

    一种可行的方法是在您的应用代码中处理作业生命周期管理,而不是依赖自动为您执行此操作的包装器(在作业完成之前阻塞)。

    来自Running jobs

    以编程方式运行作业:

    1. 通过使用唯一的job ID generated by your client code 调用jobs.insert 方法来开始工作。服务器生成作业 如果您省略它,则为您提供 ID,但最好的做法是生成 客户端的作业 ID,以允许可靠地重试 jobs.insert 打电话。

      当你调用jobs.insert 方法时,包含一个作业资源 包含指定作业类型的子属性的表示 — loadqueryextractcopy

    2. 通过使用作业 ID 调用 jobs.get 来检查作业状态,并检查 status.state 值以了解作业状态。什么时候 status.stateDONE,作业已停止运行;然而,一个 DONE状态不代表作业成功完成,只是 它不再运行。

      注意:有一些管理作业状态的包装函数 对你的要求。例如,运行 jobs.query 创建一个作业并 在指定的时间段内定期轮询DONE 状态。

    3. 检查作业是否成功。如果作业具有status.errorResult 属性,则作业失败。 status.errorResult 属性持有 描述失败工作中出了什么问题的信息。如果 status.errorResult 不在,作业成功完成, 尽管可能存在一些非致命错误,例如问题 在加载作业中导入几行。非致命错误返回 作业的status.errors 列表。

    您可以启动后台作业,然后重复调用查询对于作业的状态(例如在延迟的推送队列任务中),当它完成时,您实际上开始处理结果。

    Introduction to BigQuery JobsManaging BigQuery Jobs 可能也很有趣。

    注意:答案仅基于文档,我还不是 BigQuery 用户。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-19
      相关资源
      最近更新 更多