@engine.define
def geneId(**params):
print 'geneId is called!!!!!'
if engine.current_user != None: # 只有已登录用户才有权限调用该云函数
objId = params["id"]
queryForGene = leancloud.Query('Gene')
try:
gene = queryForGene.get(objId) # 标记点1
except leancloud.LeanCloudError as e:
print '查询gene出错,id = ', objId
raise e # 标记点2
else:
#查询成功
#有pointer的需要特殊处理,只记录pointer的id
oldStatDict = gene.get(key_Gene_stat)
newStatDict = {}
if oldStatDict is not None:
newStatDict = statDictPointerToIdString(oldStatDict)
geneDataDict = {
key_Gene_name_zh_Hans:gene.get(key_Gene_name_zh_Hans),
key_Gene_dominant:gene.get(key_Gene_dominant),
key_Gene_color:gene.get(key_Gene_color),
key_Gene_shape:gene.get(key_Gene_shape),
}
if len(newStatDict) > 0:
geneDataDict[key_Gene_stat] = newStatDict
jsonString = json.dumps(geneDataDict, separators = (',',':'))
return jsonString
就是以上云函数 geneId
被调用多次。但我仔细看了一遍代码,该云函数只进行了一次查询(标记点1),调用的statDictPointerToIdString
函数里只是对字典里的poiner类型都处理为string(否则用json.dumps会出错),没有查询操作。当出现Error429时,行号是指向的标记点2。
按我的粗浅理解,我的那个调用频率和代码是不会造成线程不够用的,问题有可能是当调用这个云函数时,还有其他大量的云函数或者查询调用请求我没注意到,只是运行到这行因为显式raise了才误以为问题是发生在这里。这样理解对吗?
之前我贴那一大段Error信息时说当有这些时没有Error429,这一点我说错了,因为在那一大段Error中间我也发现了这句 LeanCloudError: LeanCloudError: [429] Too many requests.
您之前说建议我检查云引擎代码,我检查过了(见贴的云函数代码那条回复),没有发现该函数使用大量查询请求。另外我也在客户端检查了,发生问题的时刻可以理解为瞬间进行了以下操作:
- 6次geneId云函数调用
- 1次云函数B调用 云函数B构造与geneId基本一致但更简单。
- 20次使用UnitySDK默认接口通过id查询某个表A的该条数据
- 4次使用UnitySDK默认接口通过id查询某个表B的该条数据
这两条云函数的开始都进行了engine.current_user != None
的判断,除了该current_user
语句外各自只进行了一次通过id查询的操作(例上个回复中代码的标记点A处)。我的判断是问题不出在这些云函数的代码中。
之后我没有什么头绪了,请问该如何定位问题呢? 谢谢!
我的建议是如果没办法快速定位问题的话可以先忽略 429 错误,在抛出 LeanCloudError 的时候做一下异常处理,如果愿意的话可以同时在这里打印一条日志以便日后 debug。
出现超限的工作线程数只能说明有超限,但并不一定能 100% 反映实际情况。因为和工作线程数正相关的指标是 QPS,而 QPS 的数量又和服务端处理请求的时间相关。我们丢弃一个请求所需的时间非常小,这样客户端很快就可以再次发起另一个请求,可能会引起非常大的 QPS。也就是说你在图表里看到的这么猛的超限很可能只会在开发阶段出现。
所以我的建议如前面所说,如果没发现代码有大的问题,就建议先绕过这里。如果这个问题频繁复现,再考虑如何解决。解决方案一般都是优化查询。
我对异常理解的不是很好“在抛出 LeanCloudError 的时候做一下异常处理”我这样做您看是否可以:
在我贴出的那段代码中的以下片段:
try:
gene = queryForGene.get(objId) # 标记点1
except leancloud.LeanCloudError as e:
print '查询gene出错,id = ', objId
raise e # 标记点2
else:
#查询成功
do somthing
将except这段的raise e这行删除。客户端查询失败后有重新发起查询的机制。而在服务器端,我也不清楚except得到该异常后我能做什么处理,所以“catch”后不作任何动作,后面的代码就不执行了,等客户端过些毫秒再重新发起查询。
另外我没明白“我们丢弃一个请求所需的时间非常小,这样客户端很快就可以再次发起另一个请求,可能会引起非常大的 QPS。”这句话中,客户端很快再次发起另一个请求,这是指的例如我在客户端写的只要未成功就重新请求的类,还是unity SDK中有类似的不成功则重试发起请求的机制?
建议判断一下 LeanCloudError
的 code
,根据不同情况分别处理。譬如当 e.code == 429
时打印「并发超限」。
错误代码可以参考 我们的文档2,不过一般来说只要处理几种比较常见的情况就可以了。
更新:
我尝试了将项目里所有的except句中的raise e改为以下,该问题仍然存在。
except leancloud.LeanCloudError as e:
print '查询Species出错,id = ', objId
if e.code == 429:
print '并发超限, code = 429'
else:
raise e
云引擎网页后台中的出错信息:
ERROR web1 17:21:34 Traceback (most recent call last):
ERROR web1 17:21:34 File "/usr/local/lib/python2.7/dist-packages/gevent/pywsgi.py", line 884, in handle_one_response
ERROR web1 17:21:34 self.run_application()
ERROR web1 17:21:34 File "/usr/local/lib/python2.7/dist-packages/geventwebsocket/handler.py", line 88, in run_application
ERROR web1 17:21:34 return super(WebSocketHandler, self).run_application()
ERROR web1 17:21:34 File "/usr/local/lib/python2.7/dist-packages/gevent/pywsgi.py", line 870, in run_application
ERROR web1 17:21:34 self.result = self.application(self.environ, self.start_response)
ERROR web1 17:21:34 File "/usr/local/lib/python2.7/dist-packages/leancloud/engine/__init__.py", line 52, in __call__
ERROR web1 17:21:34 return self.cloud_app(environ, start_response)
ERROR web1 17:21:34 File "/usr/local/lib/python2.7/dist-packages/werkzeug/local.py", line 228, in application
ERROR web1 17:21:34 return ClosingIterator(app(environ, start_response), self.cleanup)
ERROR web1 17:21:34 File "/usr/local/lib/python2.7/dist-packages/leancloud/engine/cors.py", line 51, in __call__
ERROR web1 17:21:34 return self.app(environ, cors_start_response)
ERROR web1 17:21:34 File "/usr/local/lib/python2.7/dist-packages/leancloud/engine/authorization.py", line 43, in __call__
ERROR web1 17:21:34 return self.app(environ, start_response)
ERROR web1 17:21:34 File "/usr/local/lib/python2.7/dist-packages/leancloud/engine/leanengine.py", line 73, in __call__
ERROR web1 17:21:34 self.process_session(environ)
ERROR web1 17:21:34 File "/usr/local/lib/python2.7/dist-packages/leancloud/engine/leanengine.py", line 84, in process_session
ERROR web1 17:21:34 user = leancloud.User.become(session_token)
ERROR web1 17:21:34 File "/usr/local/lib/python2.7/dist-packages/leancloud/user.py", line 59, in become
ERROR web1 17:21:34 response = client.get('/users/me', params={'session_token': session_token})
ERROR web1 17:21:34 File "/usr/local/lib/python2.7/dist-packages/leancloud/client.py", line 90, in new_func
ERROR web1 17:21:34 return func(headers=headers, *args, **kwargs)
ERROR web1 17:21:34 File "/usr/local/lib/python2.7/dist-packages/leancloud/client.py", line 171, in new_func
ERROR web1 17:21:34 response = func(*args, **kwargs)
ERROR web1 17:21:34 File "/usr/local/lib/python2.7/dist-packages/leancloud/client.py", line 163, in new_func
ERROR web1 17:21:34 raise leancloud.LeanCloudError(content.get('code', 1), content.get('error', 'Unknown Error'))
ERROR web1 17:21:34 LeanCloudError: LeanCloudError: [429] Too many requests.
ERROR web1 17:21:34 {'CONTENT_LENGTH': '33',
ERROR web1 17:21:34 'CONTENT_TYPE': 'application/json',
ERROR web1 17:21:34 'GATEWAY_INTERFACE': 'CGI/1.1',
ERROR web1 17:21:34 'HTTP_ACCEPT': '*/*',
ERROR web1 17:21:34 'HTTP_CONNECTION': 'close',
ERROR web1 17:21:34 'HTTP_HOST': 'api.leancloud.cn',
ERROR web1 17:21:34 'HTTP_USER_AGENT': 'UnityPlayer/5.5.0f3 (http://unity3d.com)',
ERROR web1 17:21:34 'HTTP_X_FORWARDED_FOR': '::ffff:10.10.225.216',
ERROR web1 17:21:34 'HTTP_X_FORWARDED_PORT': '80',
ERROR web1 17:21:34 'HTTP_X_FORWARDED_PROTO': 'https',
ERROR web1 17:21:34 'HTTP_X_FORWARDED_PROTOCOL': 'http',
ERROR web1 17:21:34 'HTTP_X_LC_CLIENT_VERSION': 'net-portable-2.0.0.0',
ERROR web1 17:21:34 'HTTP_X_LC_ID': 'GSIRUlB5taiAdvjI1jynXHu7-gzGzoHsz',
ERROR web1 17:21:34 'HTTP_X_LC_INSTALLATION_ID': 'ce91dfe8-3c0d-455f-bae1-354fab2fe3ad',
ERROR web1 17:21:34 'HTTP_X_LC_KEY': 'zPc1Cs5CKAr2i0QGPxtHhOma',
ERROR web1 17:21:34 'HTTP_X_LC_SESSION': '28l6z4jr28u2b8lo84f74nide',
ERROR web1 17:21:34 'HTTP_X_REAL_IP': '125.33.93.118',
ERROR web1 17:21:34 'HTTP_X_UNITY_VERSION': '5.5.0f3',
ERROR web1 17:21:34 'PATH_INFO': '/1.1/functions/geneId',
ERROR web1 17:21:34 'QUERY_STRING': '',
ERROR web1 17:21:34 'REMOTE_ADDR': '10.10.58.223',
ERROR web1 17:21:34 'REMOTE_PORT': '40748',
ERROR web1 17:21:34 'REQUEST_METHOD': 'POST',
ERROR web1 17:21:34 'SCRIPT_NAME': '',
ERROR web1 17:21:34 'SERVER_NAME': '3e196bed2a81',
ERROR web1 17:21:34 'SERVER_PORT': '3000',
ERROR web1 17:21:34 'SERVER_PROTOCOL': 'HTTP/1.1',
ERROR web1 17:21:34 'SERVER_SOFTWARE': 'gevent/1.1 Python/2.7',
ERROR web1 17:21:34 '_app_params': {'id': u'GSIRUlB5taiAdvjI1jynXHu7-gzGzoHsz',
ERROR web1 17:21:34 'key': u'zPc1Cs5CKAr2i0QGPxtHhOma',
ERROR web1 17:21:34 'master_key': None,
ERROR web1 17:21:34 'session_token': u'28l6z4jr28u2b8lo84f74nide'},
ERROR web1 17:21:34 'leanengine.request': <Request 'http://api.leancloud.cn/1.1/functions/geneId' [POST]>,
ERROR web1 17:21:34 'werkzeug.request': <Request 'http://api.leancloud.cn/1.1/functions/geneId' [POST]>,
ERROR web1 17:21:34 'wsgi.errors': <open file '<stderr>', mode 'w' at 0x7f05898e91e0>,
ERROR web1 17:21:34 'wsgi.input': <gevent.pywsgi.Input object at 0x7f0584ea8188>,
ERROR web1 17:21:34 'wsgi.multiprocess': False,
ERROR web1 17:21:34 'wsgi.multithread': False,
ERROR web1 17:21:34 'wsgi.run_once': False,
ERROR web1 17:21:34 'wsgi.url_scheme': 'http',
ERROR web1 17:21:34 'wsgi.version': (1, 0)} failed with LeanCloudError
ERROR web1 17:21:34
3张后台统计数据截图:
app ID: GSIRUlB5taiAdvjI1jynXHu7-gzGzoHsz
麻烦请您帮忙看一下,谢谢!
另外我一个用户在一瞬间发起20多个普通查询和10次左右的云函数调用,这个不算过份吧?
如果这三十多个请求都是并发发送的话,确实会给服务器造成压力,达到工作线程上限,并且对客户端(手机)造成一定性能瓶颈。现代浏览器中,如果对同一个 Host 的 URL 并发请求的话,浏览器也会做并发限制,将并发请求改为串行的。
具体到这个问题,我建议把这三十多个请求,改成串行调用。或者考虑到速度,改为最大两个/三个的并发调用。这样就不再很容易的达到并发上限了。
关于不能捕获所有 429 异常的问题,根据日志发现现在的 SDK 在执行实际的云函数代码之前,还会根据在 SDK 当前登录的用户,获取一次当前用户信息。这个时候抛出的 429 异常并没有被捕获,导致请求挂掉。
目前此问题已经记录在 GitHub: https://github.com/leancloud/python-sdk/issues/2951 ,我们尽快解决,请查看此 issue 来跟踪问题解决状态。