Bla*_*sin 2 google-app-engine python-2.7 google-glass google-mirror-api
我正在使用mirror api开发一个玻璃器皿项目.
我的应用类似于新闻应用,可在文章发布时向所有订阅用户提供文章时间线卡.随着我的用户群的增长,这将成为一个问题,因为,正如应用程序现在所说的那样,它为每个文章卡的每个用户进行一次API调用.根据用户的设置,我们每天最多可以发送50篇文章.鉴于Google每天只有1000个镜像API调用的限制,我已经只有20个用户就可以实现这一目标.我知道当我的应用程序获得批准时,这会增加,但我仍然希望优化我的代码.
问题1:我是否正确假设实施批处理将减少我在此处无法找到的API调用数量:https://developers.google.com/glass/batch 假设我进行一次批量调用以发布单个时间轴项目到我的用户的玻璃时间轴的1000,是否计为1镜像API请求,或1000镜像API请求?如果是后者,(问题1.1)我是否正确假设批处理的唯一好处是减少我的应用程序的流量?
我遇到的问题是,如果用户在批处理运行时从(https://security.google.com/settings/security/permissions)撤消对我的应用的访问权限,则会引发异常并失败.它最终将用户(仍有权限)记录下来并强制重新进行身份验证.从我的测试来看,我不认为它在整个批次中都失败了,所以我不确定它是否将时间线卡交给所有未删除访问权限的其他用户.
目前,在没有批处理的情况下,我能够捕获此错误并在用户撤消访问权限时将其从数据库中删除. 问题2:如何在代码中捕获此错误并确定它对应的用户. 示例代码如下:
def _insert_item_all_users(self):
"""Insert a timeline item to all authorized users."""
logging.info('Inserting timeline item to all users')
users = Credentials.all()
total_users = users.count()
if total_users > 10:
return 'Total user count is %d. Aborting broadcast to save your quota' % (
total_users)
body = {
'text': 'Hello Everyone!',
'notification': {'level': 'DEFAULT'}
}
batch_responses = _BatchCallback()
batch = BatchHttpRequest(callback = batch_responses.callback)
for user in users:
creds = StorageByKeyName(
Credentials, user.key().name(), 'credentials').get()
mirror_service = util.create_service('mirror', 'v1', creds)
batch.add(
mirror_service.timeline().insert(body = body),
request_id = user.key().name())
batch.execute(httplib2.Http())
return 'Successfully sent cards to %d users (%d failed).' % (
batch_responses.success, batch_responses.failure)
Run Code Online (Sandbox Code Playgroud)
我得到的错误是这样的:
INFO 2014-01-15 22:42:06,031 client.py:699] Failed to retrieve access token: {
"error" : "invalid_grant"
}
Run Code Online (Sandbox Code Playgroud)
访问令牌刷新失败,并引发了一个 oauth2client.client.AccessTokenRefreshError
重现步骤:
应用项目日志
--Send timeline card to all users (before revoking access)
INFO 2014-01-15 22:41:41,217 appengine.py:276] make: Got type <class 'google.appengine.api.datastore_types.Blob'>
INFO 2014-01-15 22:41:41,217 appengine.py:289] validate: Got type <class 'oauth2client.client.OAuth2Credentials'>
INFO 2014-01-15 22:41:41,217 discovery.py:190] URL being requested: https://www.googleapis.com/discovery/v1/apis/mirror/v1/rest?userIp=127.0.0.1
INFO 2014-01-15 22:41:41,999 appengine.py:276] make: Got type <class 'google.appengine.api.datastore_types.Blob'>
INFO 2014-01-15 22:41:41,999 appengine.py:289] validate: Got type <class 'oauth2client.client.OAuth2Credentials'>
INFO 2014-01-15 22:41:41,999 client.py:680] Refreshing access_token
INFO 2014-01-15 22:41:42,519 appengine.py:276] make: Got type <class 'google.appengine.api.datastore_types.Blob'>
INFO 2014-01-15 22:41:42,519 appengine.py:289] validate: Got type <class 'oauth2client.client.OAuth2Credentials'>
INFO 2014-01-15 22:41:42,521 appengine.py:289] validate: Got type <class 'oauth2client.client.OAuth2Credentials'>
INFO 2014-01-15 22:41:42,521 appengine.py:265] get: Got type <class 'model.Credentials'>
INFO 2014-01-15 22:41:42,523 main_handler.py:275] Inserting timeline item to all users
INFO 2014-01-15 22:41:42,529 appengine.py:276] make: Got type <class 'google.appengine.api.datastore_types.Blob'>
INFO 2014-01-15 22:41:42,530 appengine.py:289] validate: Got type <class 'oauth2client.client.OAuth2Credentials'>
INFO 2014-01-15 22:41:42,532 appengine.py:276] make: Got type <class 'google.appengine.api.datastore_types.Blob'>
INFO 2014-01-15 22:41:42,532 appengine.py:289] validate: Got type <class 'oauth2client.client.OAuth2Credentials'>
INFO 2014-01-15 22:41:42,532 discovery.py:190] URL being requested: https://www.googleapis.com/discovery/v1/apis/mirror/v1/rest?userIp=127.0.0.1
INFO 2014-01-15 22:41:42,945 discovery.py:709] URL being requested: https://www.googleapis.com/mirror/v1/timeline?alt=json
INFO 2014-01-15 22:41:42,946 appengine.py:276] make: Got type <class 'google.appengine.api.datastore_types.Blob'>
INFO 2014-01-15 22:41:42,946 appengine.py:289] validate: Got type <class 'oauth2client.client.OAuth2Credentials'>
INFO 2014-01-15 22:41:42,949 appengine.py:276] make: Got type <class 'google.appengine.api.datastore_types.Blob'>
INFO 2014-01-15 22:41:42,950 appengine.py:289] validate: Got type <class 'oauth2client.client.OAuth2Credentials'>
INFO 2014-01-15 22:41:42,950 discovery.py:190] URL being requested: https://www.googleapis.com/discovery/v1/apis/mirror/v1/rest?userIp=127.0.0.1
INFO 2014-01-15 22:41:43,666 discovery.py:709] URL being requested: https://www.googleapis.com/mirror/v1/timeline?alt=json
WARNING 2014-01-15 22:41:43,666 util.py:125] execute() takes at most 1 positional argument (2 given)
INFO 2014-01-15 22:41:44,641 module.py:617] default: "POST / HTTP/1.1" 302 -
INFO 2014-01-15 22:41:44,648 appengine.py:276] make: Got type <class 'google.appengine.api.datastore_types.Blob'>
INFO 2014-01-15 22:41:44,648 appengine.py:289] validate: Got type <class 'oauth2client.client.OAuth2Credentials'>
INFO 2014-01-15 22:41:44,649 discovery.py:190] URL being requested: https://www.googleapis.com/discovery/v1/apis/mirror/v1/rest?userIp=127.0.0.1
INFO 2014-01-15 22:41:45,092 appengine.py:276] make: Got type <class 'google.appengine.api.datastore_types.Blob'>
INFO 2014-01-15 22:41:45,093 appengine.py:289] validate: Got type <class 'oauth2client.client.OAuth2Credentials'>
INFO 2014-01-15 22:41:45,093 client.py:680] Refreshing access_token
INFO 2014-01-15 22:41:45,841 appengine.py:276] make: Got type <class 'google.appengine.api.datastore_types.Blob'>
INFO 2014-01-15 22:41:45,841 appengine.py:289] validate: Got type <class 'oauth2client.client.OAuth2Credentials'>
INFO 2014-01-15 22:41:45,842 appengine.py:289] validate: Got type <class 'oauth2client.client.OAuth2Credentials'>
INFO 2014-01-15 22:41:45,843 appengine.py:265] get: Got type <class 'model.Credentials'>
INFO 2014-01-15 22:41:45,850 discovery.py:709] URL being requested: https://www.googleapis.com/mirror/v1/contacts/python-quick-start?alt=json
WARNING 2014-01-15 22:41:45,852 urlfetch_stub.py:482] Stripped prohibited headers from URLFetch request: ['content-length']
INFO 2014-01-15 22:41:46,472 main_handler.py:93] Unable to find Python Quick Start contact.
INFO 2014-01-15 22:41:46,492 discovery.py:709] URL being requested: https://www.googleapis.com/mirror/v1/timeline?maxResults=3&alt=json
WARNING 2014-01-15 22:41:46,494 urlfetch_stub.py:482] Stripped prohibited headers from URLFetch request: ['content-length']
INFO 2014-01-15 22:41:47,028 discovery.py:709] URL being requested: https://www.googleapis.com/mirror/v1/subscriptions?alt=json
WARNING 2014-01-15 22:41:47,031 urlfetch_stub.py:482] Stripped prohibited headers from URLFetch request: ['content-length']
INFO 2014-01-15 22:41:47,562 module.py:617] default: "GET / HTTP/1.1" 200 8163
INFO 2014-01-15 22:41:47,664 module.py:617] default: "GET /static/bootstrap/css/bootstrap-responsive.min.css HTTP/1.1" 304 -
INFO 2014-01-15 22:41:47,665 module.py:617] default: "GET /static/bootstrap/css/bootstrap.min.css HTTP/1.1" 304 -
INFO 2014-01-15 22:41:47,666 module.py:617] default: "GET /static/main.css HTTP/1.1" 304 -
INFO 2014-01-15 22:41:47,668 module.py:617] default: "GET /static/images/chipotle-tube-640x360.jpg HTTP/1.1" 304 -
INFO 2014-01-15 22:41:47,672 module.py:617] default: "GET /static/bootstrap/js/bootstrap.min.js HTTP/1.1" 304 -
--Send timeline card to all users (after revoking access)
INFO 2014-01-15 22:42:02,892 appengine.py:276] make: Got type <class 'google.appengine.api.datastore_types.Blob'>
INFO 2014-01-15 22:42:02,893 appengine.py:289] validate: Got type <class 'oauth2client.client.OAuth2Credentials'>
INFO 2014-01-15 22:42:02,893 discovery.py:190] URL being requested: https://www.googleapis.com/discovery/v1/apis/mirror/v1/rest?userIp=127.0.0.1
INFO 2014-01-15 22:42:03,278 appengine.py:276] make: Got type <class 'google.appengine.api.datastore_types.Blob'>
INFO 2014-01-15 22:42:03,279 appengine.py:289] validate: Got type <class 'oauth2client.client.OAuth2Credentials'>
INFO 2014-01-15 22:42:03,279 client.py:680] Refreshing access_token
INFO 2014-01-15 22:42:03,829 appengine.py:276] make: Got type <class 'google.appengine.api.datastore_types.Blob'>
INFO 2014-01-15 22:42:03,829 appengine.py:289] validate: Got type <class 'oauth2client.client.OAuth2Credentials'>
INFO 2014-01-15 22:42:03,830 appengine.py:289] validate: Got type <class 'oauth2client.client.OAuth2Credentials'>
INFO 2014-01-15 22:42:03,830 appengine.py:265] get: Got type <class 'model.Credentials'>
INFO 2014-01-15 22:42:03,834 main_handler.py:275] Inserting timeline item to all users
INFO 2014-01-15 22:42:03,844 appengine.py:276] make: Got type <class 'google.appengine.api.datastore_types.Blob'>
INFO 2014-01-15 22:42:03,844 appengine.py:289] validate: Got type <class 'oauth2client.client.OAuth2Credentials'>
INFO 2014-01-15 22:42:03,846 appengine.py:276] make: Got type <class 'google.appengine.api.datastore_types.Blob'>
INFO 2014-01-15 22:42:03,847 appengine.py:289] validate: Got type <class 'oauth2client.client.OAuth2Credentials'>
INFO 2014-01-15 22:42:03,847 discovery.py:190] URL being requested: https://www.googleapis.com/discovery/v1/apis/mirror/v1/rest?userIp=127.0.0.1
INFO 2014-01-15 22:42:04,270 discovery.py:709] URL being requested: https://www.googleapis.com/mirror/v1/timeline?alt=json
INFO 2014-01-15 22:42:04,271 appengine.py:276] make: Got type <class 'google.appengine.api.datastore_types.Blob'>
INFO 2014-01-15 22:42:04,271 appengine.py:289] validate: Got type <class 'oauth2client.client.OAuth2Credentials'>
INFO 2014-01-15 22:42:04,275 appengine.py:276] make: Got type <class 'google.appengine.api.datastore_types.Blob'>
INFO 2014-01-15 22:42:04,276 appengine.py:289] validate: Got type <class 'oauth2client.client.OAuth2Credentials'>
INFO 2014-01-15 22:42:04,276 discovery.py:190] URL being requested: https://www.googleapis.com/discovery/v1/apis/mirror/v1/rest?userIp=127.0.0.1
INFO 2014-01-15 22:42:04,705 discovery.py:709] URL being requested: https://www.googleapis.com/mirror/v1/timeline?alt=json
WARNING 2014-01-15 22:42:04,705 util.py:125] execute() takes at most 1 positional argument (2 given)
INFO 2014-01-15 22:42:05,531 appengine.py:276] make: Got type <class 'google.appengine.api.datastore_types.Blob'>
INFO 2014-01-15 22:42:05,531 appengine.py:289] validate: Got type <class 'oauth2client.client.OAuth2Credentials'>
INFO 2014-01-15 22:42:05,532 client.py:680] Refreshing access_token
INFO 2014-01-15 22:42:06,031 client.py:699] Failed to retrieve access token: {
"error" : "invalid_grant"
}
INFO 2014-01-15 22:42:06,035 appengine.py:276] make: Got type <class 'google.appengine.api.datastore_types.Blob'>
INFO 2014-01-15 22:42:06,035 appengine.py:289] validate: Got type <class 'oauth2client.client.OAuth2Credentials'>
INFO 2014-01-15 22:42:06,038 appengine.py:289] validate: Got type <class 'oauth2client.client.OAuth2Credentials'>
INFO 2014-01-15 22:42:06,038 appengine.py:265] get: Got type <class 'model.Credentials'>
INFO 2014-01-15 22:42:06,043 appengine.py:276] make: Got type <class 'google.appengine.api.datastore_types.Blob'>
INFO 2014-01-15 22:42:06,043 appengine.py:289] validate: Got type <class 'oauth2client.client.OAuth2Credentials'>
INFO 2014-01-15 22:42:06,056 module.py:617] default: "POST / HTTP/1.1" 302 -
INFO 2014-01-15 22:42:06,066 module.py:617] default: "GET /auth HTTP/1.1" 302 -
Run Code Online (Sandbox Code Playgroud)
两个问题的两个答案,从我们在上面的评论中讨论的内容中收集.
不.批处理中的API请求仍计为整个API请求.
批处理是为了表现.它减少了您必须打开的套接字数量并节省了带宽,特别是如果您使用gzip并向多个用户发送相同的有效负载.
如果您想要更多配额,请填写Glassware提交表格.即使您只需要更多配额来继续开发,也可以使用它.
正如您所发现的,这是Python API客户端库中的一个错误.即使是失败的请求,它也应该调用你的回调.你修补它是这样的:
--- /google-api-python-client-1ab344e0a34d/apiclient/http.py
+++ /google-api-python-client-patched/apiclient/http.py
@@ -50,7 +50,7 @@
from model import JsonModel
from oauth2client import util
from oauth2client.anyjson import simplejson
-
+from oauth2client.client import AccessTokenRefreshError
DEFAULT_CHUNK_SIZE = 512*1024
@@ -1299,10 +1299,13 @@
for request_id in self._order:
resp, content = self._responses[request_id]
if resp['status'] == '401':
- redo_order.append(request_id)
- request = self._requests[request_id]
- self._refresh_and_apply_credentials(request, http)
- redo_requests[request_id] = request
+ try:
+ request = self._requests[request_id]
+ self._refresh_and_apply_credentials(request, http)
+ redo_order.append(request_id)
+ redo_requests[request_id] = request
+ except AccessTokenRefreshError:
+ pass
if redo_requests:
self._execute(http, redo_order, redo_requests)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
382 次 |
| 最近记录: |