GAE中的RPC调用次数高,这是正常的吗?

时间:2011-11-01 16:55:00

标签: google-app-engine google-cloud-datastore

我有几个问题希望有助于巩固我对GAE“背后的风格”的理解。目前在我的应用程序中,我需要检索一组大小为258个实体的数据。我已将indexed = False设置为此实体的Model类的相应属性。我正在使用Nick Johnson的blog描述的序列化/反序列化实体技术。为了示例目的,我将代码设为通用:

def serialize_entities(models):
    if not models:
        return None
    elif isinstance(models, db.Model):
        return db.model_to_protobuf(models).Encode()
    else:
        return [db.model_to_protobuf(x).Encode() for x in models]

def deserialize_entities(data):
    if not data:
        return None
    elif isinstance(data, str):
        return db.model_from_protobuf(entity_pb.EntityProto(data))
    else:
        return [db.model_from_protobuf(entity_pb.EntityProto(x)) for x in data]

class BatchProcessor(object):
    kind = None
    filters = []
    def get_query(self):
        q = self.kind.all()
        for prop, value in self.filters:
            q.filter("%s" % prop, value)
        q.order("__key__")
        return q

    def run(self, batch_size=100):
        q = self.get_query()
        entities = q.fetch(batch_size)
        while entities:
            yield entities
            q = self.get_query()
            q.filter("__key__ >",entities[-1].key())
            entities = q.fetch(batch_size)

class MyKindHandler(webapp2.RequestHandler):
    def fill_cache(self, my_cache):
        entities = []
        if not my_cache:
            # retry in case of failure to retrieve data
            while not my_cache:
                batch_processor = BatchProcessor()
                batch_processor.kind = models.MyKind
                batch = batch_processor.run()
                my_cache = []

                for b in batch:
                    my_cache.extend(b)                    
            # Cache entities for 10 minutes
            memcache.set('MyKindEntityCache', serialize_entities(my_cache), 
                         600)
        for v in my_cache:
            # There is actually around 10 properties for this entity.
            entities.append({'one': v.one, 'two': v.two})

        mykind_json_str = simplejson.dumps({'entities':entities})
        # Don't set expiration - will be refreshed automatically when 
        # entity cache is.
        memcache.set('MyKindJsonCache', mykind_json_str)
        return mykind_json_str

    def get(self):
        my_cache = deserialize_entities(memcache.get('MyKindEntityCache'))
        if not my_cache:
            # Create entity & json cache
            self.fill_cache(my_cache)

        mykind_json_str = memcache.get('MyKindJsonCache')
        if not mykind_json_str:
            mykind_json_str = self.fill_cache(my_cache)
        self.response.write(mykind_json_str)

当我查看at appstats时,它会显示检索此数据的处理程序的ajax调用需要花费大量时间。当必须从数据存储区刷新数据时(实体缓存每10分钟到期)调用该处理程序时,会进行434次RPC调用,否则有2个RPC称为检索每个memcaches。刷新数据的appstats是:

real=10295ms cpu=13544ms api=5956ms overhead=137ms (434 RPCs)
datastore_v3.Get       428
memcache.Get             2
memcache.Set             2
datastore_v3.RunQuery    2

我想我理解每个调用在大多数情况下都在做什么,但与datastore_v3.RunQuery相关联的datastore_v3.Get调用会让我感到困惑,如果它们代表了我认为他们所做的事情。它们是否意味着那些是对数据存储区的单独.get()调用?我认为当你调用.fetch(batch_size)时,你会收到一个带有1个调用的“批处理”...不仅如此,为什么这些调用的数量比数据存储区中的实体数量多150多个给定的种类?

作为旁注,我在学习查询游标之前编写了BatchProcessor类,并尝试将它们交换出来以查看性能是否变得更好,但它不仅保持相同,而且由于存储的原因而增加了RPC的数量光标在memcache中。

对我可能会忽略的事情的任何见解都非常感谢!


修改 - 来自appstats的更多详细信息:

在对此进行测试并检查appstats时,我发现在创建json响应字符串时,我在每个MyKind上调用.parent()会大大增加RPC,同时每个MyKind有2个ReferenceProperties可变地拥有或者没有价值。我使用此.parent()调用,以便在响应中使用MyKind数据返回公司名称,如果ReferenceProperties具有值,我也会得到它们的名称。我没有意识到调用.parent()。name,.refprop1.name和.refprop2.name会从数据存储区为每个数据存储执行单独的datastore_V3.Get。我认为父和引用属性的数据是与MyKind对象的原始查询一起返回的。有没有办法让这种情况发生,或者我是否需要在MyKind中创建3个其他属性才能有效访问这些名称属性?

0 个答案:

没有答案