6

我们目前正在构建一个小型且简单的中央 HTTP 服务,该服务将“外部身份”(如 facebook id)映射到“内部 (uu)id”,在我们所有的服务中都是唯一的,以帮助进行分析。

“我们的堆栈”(flask+postgresql)中的第一个原型在一天内完成。但由于我们希望服务(几乎)永远不会失败并自动扩展,因此我们决定使用 Google App Engine。

经过一周的阅读、尝试和基准测试后,出现了这个问题:

在 App Engine(使用 NDB)上,哪些响应时间被视为“正常”?

我们得到的平均响应时间始终在 500 毫秒以上,并且在 90% 中远高于 1 秒

我在下面附上了我们代码的精简版本,希望有人能指出明显的缺陷。我们真的很喜欢自动缩放和分布式存储,但我们无法想象 500 毫秒真的是我们案例的预期性能。基于 sql 的原型响应速度更快(始终如一),使用免费、无缓存的 postgresql(即使使用 ORM)托管在一个 Heroku dyno 上。

我们尝试了以下代码的同步和异步变体,并查看了 appstats 配置文件。总是 RPC 调用(memcache 和数据存储)需要很长时间(50ms-100ms),因为总是有多个调用(例如 mc.get() + ds.get() + ds.set( )写)。我们还尝试尽可能地推迟到任务队列,但没有明显的收获。

import json
import uuid

from google.appengine.ext import ndb

import webapp2
from webapp2_extras.routes import RedirectRoute


def _parse_request(request):
    if request.content_type == 'application/json':
        try:
            body_json = json.loads(request.body)
            provider_name = body_json.get('provider_name', None)
            provider_user_id = body_json.get('provider_user_id', None)
        except ValueError:
            return webapp2.abort(400, detail='invalid json')
    else:
        provider_name = request.params.get('provider_name', None)
        provider_user_id = request.params.get('provider_user_id', None)

    return provider_name, provider_user_id


class Provider(ndb.Model):
    name = ndb.StringProperty(required=True)


class Identity(ndb.Model):
    user = ndb.KeyProperty(kind='GlobalUser')


class GlobalUser(ndb.Model):
    uuid = ndb.StringProperty(required=True)

    @property
    def identities(self):
        return Identity.query(Identity.user==self.key).fetch()


class ResolveHandler(webapp2.RequestHandler):
    @ndb.toplevel
    def post(self):
        provider_name, provider_user_id = _parse_request(self.request)

        if not provider_name or not provider_user_id:
            return self.abort(400, detail='missing provider_name and/or provider_user_id')

        identity = ndb.Key(Provider, provider_name, Identity, provider_user_id).get()

        if identity:
            user_uuid = identity.user.id()
        else:
            user_uuid = uuid.uuid4().hex

            GlobalUser(
                id=user_uuid,
                uuid=user_uuid
            ).put_async()

            Identity(
                parent=ndb.Key(Provider, provider_name),
                id=provider_user_id,
                user=ndb.Key(GlobalUser, user_uuid)
            ).put_async()

        return webapp2.Response(
            status='200 OK',
            content_type='application/json',
            body = json.dumps({
                'provider_name' : provider_name,
                'provider_user_id' : provider_user_id,
                'uuid' : user_uuid
            })
        )

app = webapp2.WSGIApplication([
      RedirectRoute('/v1/resolve', ResolveHandler, 'resolve', strict_slash=True)
], debug=False)

为了完整起见(几乎是默认的)app.yaml

application: GAE_APP_IDENTIFIER
version: 1
runtime: python27
api_version: 1
threadsafe: yes

handlers:
- url: .*
  script: main.app

libraries:
- name: webapp2
  version: 2.5.2
- name: webob
  version: 1.2.3

inbound_services:
- warmup
4

2 回答 2

3

以我的经验,RPC 性能会按数量级波动,对于数据存储获取而言,在 5 毫秒到 100 毫秒之间。我怀疑这与 GAE 数据中心负载有关。有时它会变得更好,有时它会变得更糟。

你的操作看起来很简单。我预计对于 3 个请求,大约需要 20 毫秒,但最多可能需要 300 毫秒。不过,持续平均 500 毫秒听起来非常高。

ndb 在按 ID 获取对象时进行本地缓存。如果您正在访问相同的用户,那应该会启动,并且这些请求应该会更快。

我假设您正在对生产而不是 dev_appserver 进行性能测试。dev_appserver 性能不具代表性。

不确定您测试了多少次迭代,但您可能想尝试更大的数字,看看 500 毫秒是否真的是您的平均值。

当您在简单的 RPC 调用上被阻止时,您无法进行太多优化。

于 2013-02-14T16:15:56.153 回答
1

我看到的第一个明显时刻:您真的需要针对每个请求进行事务处理吗?

我相信,除非您的大多数请求都创建新实体,否则最好在事务之外执行 .get_by_id() 。如果找不到实体,则开始交易,甚至更好地推迟实体的创建。

def request_handler(key, data):
  entity = key.get()
  if entity:
    return 'ok'
  else:
    defer(_deferred_create, key, data)
    return 'ok'

def _deferred_create(key, data):
  @ndb.transactional
  def _tx():
    entity = key.get()
    if not entity:
       entity = CreateEntity(data)
       entity.put()
  _tx()

这应该为面向用户的请求提供更好的响应时间。

我看到的第二个也是唯一的优化是使用 ndb.put_multi() 来最小化 RPC 调用。

PS 不是 100% 确定,但您可以尝试禁用多线程 ( threadsave: no ) 以获得更稳定的响应时间。

于 2013-02-15T22:14:07.193 回答