更新:我已经缩小了一点问题,所以我删除了不必要的代码和示例:
更新2:离开cron工作后,每隔12小时运行一段时间(每次结束都成功,但BQ没有写任何内容)我们震惊地发现,大约一周后,其中一个cron作业成功写入BigQuery,以及Stackdriver日志声明“此请求导致为您的应用程序启动新进程(...)”,如下所示。以下工作再次停止写作。现在我想知道这是否以某种方式连接到缓存的应用程序状态(有一些有效期)或凭据到期日期,这会阻止在第一次之后进一步写入BigQuery,但不会导致错误。
问题描述:
我正在尝试在App Engine中设置一个cron作业(标准)来查询并将数据写回BigQuery(数据集与部署的应用程序在同一个项目中)并且cron作业成功执行但只写入关于部署后第一次执行的BigQuery,之后它们仍然成功执行但不写。
我发现的主要区别在于Stackdriver日志,对于正确写入的执行,有额外的调试和信息,对于后续的没有这样的消息:
2018-04-19 04:44:03.933 CEST
Converted retries value: 3 -> Retry(total=3, connect=None, read=None, redirect=None, status=None) (/base/data/home/apps/e~<redacted>/lib/urllib3/util/retry.py:200)
2018-04-19 04:44:04.154 CEST
Making request: POST https://accounts.google.com/o/oauth2/token (/base/data/home/apps/e~<redacted>/lib/google/auth/transport/requests.py:117)
2018-04-19 04:44:04.160 CEST
Starting new HTTPS connection (1): accounts.google.com (/base/data/home/apps/e~<redacted>/lib/urllib3/connectionpool.py:824)
2018-04-19 04:44:04.329 CEST
https://accounts.google.com:443 "POST /o/oauth2/token HTTP/1.1" 200 None (/base/data/home/apps/e~<redacted>/lib/urllib3/connectionpool.py:396)
2018-04-19 04:44:04.339 CEST
Starting new HTTPS connection (1): www.googleapis.com (/base/data/home/apps/e~<redacted>/lib/urllib3/connectionpool.py:824)
2018-04-19 04:44:04.802 CEST
https://www.googleapis.com:443 "POST /bigquery/v2/projects/<redacted>/jobs HTTP/1.1" 200 None (/base/data/home/apps/e~<redacted>/lib/urllib3/connectionpool.py:396)
2018-04-19 04:44:04.813 CEST
This request caused a new process to be started for your application, and thus caused your application code to be loaded for the first time. This request may thus take longer and use more CPU than a typical request for your application.
我试过了:
为默认的appengine服务帐户添加BigQuery DataOwner和用户权限,但没有效果。
有人提到标准应用引擎并不完全支持google.cloud库,所以我尝试使用OAuth2 / httplib2 / googleapiclient凭据进行身份验证,但这是我第一次尝试使用而且我没有了解如何将各个部分组合在一起,并且没有google.cloud库我不知道如何为BQ编写正确的查询
其他凭据设置方法如下所示,但似乎连接到BQ不是问题,它们都连接和写入(一次),只是在已经部署的应用引擎中重复它。
以下是完整实施:
的app.yaml:
runtime: python27
api_version: 1
threadsafe: true
handlers:
- url: /bigquerycron
script: bigquerycron.app
login: admin
libraries:
- name: ssl
version: latest
env_variables:
GAE_USE_SOCKETS_HTTPLIB : 'true'
bigquerycron.py
from __future__ import absolute_import
from google.cloud import bigquery
import webapp2
class MainPage(webapp2.RequestHandler):
def get(self):
self.response.headers['Content-Type'] = 'text/plain'
self.response.write('CRON test page')
def writeDataTest(dataset_id = '<redacted>',table_id='<redacted>'):
client = bigquery.Client.from_service_account_json("credentials.json")
job_config = bigquery.QueryJobConfig()
table_ref = client.dataset(dataset_id).table(table_id)
job_config.destination = table_ref
job_config.write_disposition = 'WRITE_APPEND'
query_job = client.query(
"""SELECT CURRENT_DATETIME() AS Datetime, 'CRON' as Source""", job_config=job_config)
writeDataTest()
app = webapp2.WSGIApplication([
('/bigquerycron', MainPage),
], debug=True)
cron.yaml:
cron:
- url: /bigquerycron
schedule: every 30 minutes
答案 0 :(得分:2)
在这种特定情况下,凭据不是问题,原因在于对App Engine的工作方式的误解只是函数调用的位置。 bigquery的函数调用应在MainPage类定义内移动,固定的bigquerycron.py如下所示(仅移动一行代码):
from __future__ import absolute_import
from google.cloud import bigquery
import webapp2
class MainPage(webapp2.RequestHandler):
def get(self):
self.response.headers['Content-Type'] = 'text/plain'
self.response.write('CRON test page')
writeDataTest()
def writeDataTest(dataset_id = '<redacted>',table_id='<redacted>'):
client = bigquery.Client.from_service_account_json("credentials.json")
job_config = bigquery.QueryJobConfig()
table_ref = client.dataset(dataset_id).table(table_id)
job_config.destination = table_ref
job_config.write_disposition = 'WRITE_APPEND'
query_job = client.query(
"""SELECT CURRENT_DATETIME() AS Datetime, 'CRON' as Source""", job_config=job_config)
app = webapp2.WSGIApplication([
('/bigquerycron', MainPage),
], debug=True)
OP中的版本实际上只向BigQuery写入一次,第一次加载App Engine应用程序时,所有后续调用仅执行MainPage类,在这种情况下,该操作什么也不做,因为实际的BigQuery代码不在其内。
另外,在不使用google-cloud-python库的情况下重写该应用程序将是有益的,GAE Standard(https://github.com/GoogleCloudPlatform/google-cloud-python/issues/1893)不支持该库。这是特别不幸的,因为甚至python(https://cloud.google.com/bigquery/docs/)的官方bigquery文档也使用了该库。有多种解决方法可以继续使用,包括链接的github问题中提到的一些解决方法,也可以在此处进行: Using gcloud-python in GAE和本示例中使用了类似的解决方法。
但是如前所述,最好使用专用于Python的Google API客户端库: https://developers.google.com/api-client-library/python/
答案 1 :(得分:0)
我怀疑如果您删除app.yaml的“login:admin”部分,它将会有效。
如果这是问题,请确保您拥有正确的X-Appengine header setup
以下是task queues和cron jobs的一些文档。
答案 2 :(得分:0)
虽然我不确定原因,但我认为授权App引擎的服务帐户不足以访问BigQuery。
为了授权您的应用访问BigQuery,您可以执行以下两种方法之一:
在app.yaml文件中,配置一个environment variable,指向具有BigQuery正确授权配置的服务帐户密钥文件:
env_variables: GOOGLE_APPLICATION_CREDENTIALS = [YOURKEYFILE]上传.json
您的代码执行从存储桶中获取授权服务帐户密钥,然后在Cloud storage Client library的帮助下加载它。看到您的运行时是python,您应该使用的代码如下:
...
来自google.cloud导入存储空间
...
def download_key():
&#34;&#34;&#34;从存储桶中下载密钥。&#34;&#34;&#34;
storage_client = storage.Client()
bucket = storage_client.get_bucket(&#39; YOURBUCKET&#39;)
blob = bucket.blob(&#39; Keynameinthebucket.json&#39;)
blob.download_to_filename(&#39; Keynameinyourapp.json&#39)
...
#within code:
download_key()
客户= bigquery.Client.from_service_account_json(&#39; keynameinyourapp.json&#39)