您可以从GAE数据存储区查询的实体数量是否有限制?

时间:2015-04-28 04:17:10

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

我的GCM端点源自/github.com/GoogleCloudPlatform/gradle-appengine-templates/tree/master/GcmEndpoints/root/src/main中的代码。每个Android客户端设备 向端点注册。可以使用以下代码将消息发送到前10个注册的设备:

@Api(name = "messaging", version = "v1", namespace = @ApiNamespace(ownerDomain = "${endpointOwnerDomain}", ownerName = "${endpointOwnerDomain}", packagePath="${endpointPackagePath}"))
public class MessagingEndpoint {
private static final Logger log = Logger.getLogger(MessagingEndpoint.class.getName());

/** Api Keys can be obtained from the google cloud console */
private static final String API_KEY = System.getProperty("gcm.api.key");

/**
 * Send to the first 10 devices (You can modify this to send to any number of devices or a specific device)
 *
 * @param message The message to send
 */
public void sendMessage(@Named("message") String message) throws IOException {
    if(message == null || message.trim().length() == 0) {
        log.warning("Not sending message because it is empty");
        return;
    }
    // crop longer messages
    if (message.length() > 1000) {
        message = message.substring(0, 1000) + "[...]";
    }
    Sender sender = new Sender(API_KEY);
    Message msg = new Message.Builder().addData("message", message).build();
    List<RegistrationRecord> records = ofy().load().type(RegistrationRecord.class).limit(10).list();
    for(RegistrationRecord record : records) {
        Result result = sender.send(msg, record.getRegId(), 5);
        if (result.getMessageId() != null) {
            log.info("Message sent to " + record.getRegId());
            String canonicalRegId = result.getCanonicalRegistrationId();
            if (canonicalRegId != null) {
                // if the regId changed, we have to update the datastore
                log.info("Registration Id changed for " + record.getRegId() + " updating to " + canonicalRegId);
                record.setRegId(canonicalRegId);
                ofy().save().entity(record).now();
            }
        } else {
            String error = result.getErrorCodeName();
            if (error.equals(Constants.ERROR_NOT_REGISTERED)) {
                log.warning("Registration Id " + record.getRegId() + " no longer registered with GCM, removing from datastore");
                // if the device is no longer registered with Gcm, remove it from the datastore
                ofy().delete().entity(record).now();
            }
            else {
                log.warning("Error when sending message : " + error);
            }
        }
    }
}

}

以上代码发送到前10个注册设备。我想发送给所有注册客户。根据{{​​3}}设置限制(0)完成此操作。但由于内存限制或执行查询所需的时间,我不相信大量注册客户端不会出现问题。 http://objectify-appengine.googlecode.com/svn/branches/allow-parent-filtering/javadoc/com/googlecode/objectify/cmd/Query.html#limit(int)州和#34;游标让你带一个&#34;检查点&#34;在查询结果集中,将检查点存储在其他位置,然后从稍后停止的位置继续。这通常与Task Queue API结合使用,以迭代无法在单个请求的60秒限制内处理的大型数据集&#34;。

请注意有关单个请求的60秒限制的评论。

所以我的问题 - 如果我修改了/github.com/GoogleCloudPlatform/gradle-appengine-templates/tree/master/GcmEndpoints/root/src/main中的示例代码,通过替换limit来请求数据存储区中的所有对象( 10)有限制(0),这会对大量物体失败吗?如果它会失败,大概是多少个对象?

2 个答案:

答案 0 :(得分:2)

即使使用游标,这也是一种糟糕的模式。至少,您只需为单个请求达到60秒的严格限制。而且,由于您正在对RegistrationRecord进行更新,因此您需要进行交易,这会使流程进一步减慢。

这正是任务队列的用途。最好的方法是在两个任务中完成:

  1. 您的api端点排队&#34;向每个人发送消息&#34;并立即返回。
  2. 第一个任务是&#34; mapper&#34;它使用仅密钥查询迭代RegistrationRecord s。对于每个键,排队&#34; reducer&#34;任务为&#34;将X消息发送到此记录&#34;。
  3. reducer任务发送消息并(在事务中)执行记录更新。
  4. 使用Deferred这实际上并不是很多代码。

    第一个任务立即释放您的客户端,并为您提供10米的迭代- (IBAction)addParty:(id)sender { NSArray *required = @[ self.firstname, self.lastname, self.guestphone, self.guestemail, self.guestsize ]; if([[required valueForKeyPath:@"text.@min.length"] intValue] == 0) { [pleasefinish setHidden:NO]; [thankyou setHidden:YES]; } else { [pleasefinish setHidden:YES]; [thankyou setHidden:NO]; } } 密钥,而不是正常请求的60秒限制。如果你有你的分块权限和批处理队列提交,你应该能够每秒生成数千个reducer任务。

    这将毫不费力地扩展到数十万用户,并可能让您获得数百万用户。如果需要更高的比例,可以应用map / reduce方法来并行化映射。然后,这只是一个问题,你想要解决这个问题的实例数。

    我曾经使用这种方法在过去发送数百万次苹果推送通知时效果很好。任务队列是你的朋友,大量使用它。

答案 1 :(得分:0)

如果您尝试检索太多实体,您的查询将会超时。您需要在循环中使用游标。

没有人可以说在超时之前可以检索多少实体 - 这取决于实体的大小,查询的复杂性,最重要的是,循环中还会发生什么。例如,在您的情况下,您可以通过创建任务而不是在循环内部构建和发送消息来显着加快循环(从而在超时之前检索更多实体)。

请注意,默认情况下,查询以20块的形式返回实体 - 如果您拥有大量实体,则需要增加块大小。