2

我对StackOverflow比较陌生,不确定它是否适合提问设计问题。网站给我一个提示“你问的问题看起来很主观,很可能会被关闭”。也许应该询问programmers.stackexchange.com。请告诉我。如何平衡Google App Engine的多个* dynamic *后端实例的负载?

无论如何..我正在进行的项目之一是在线调查引擎。这是我在GAE上的第一个大型商业项目。

我需要你的建议,以便如何收集统计数据并有效地将它们记录在DataStore中而不会破坏我的数据。最初的要求是:

  • 用户完成调查后,客户端发送对[ID(int)+ PercentHit(double)]列表。此列表显示此用户的答案与参考答复者(由ID标识)的预定义答案的匹配程度。我称他们为“目标ID”。
  • 调查的创建者希望查看上个小时,特定时间范围或调查开始时给定ID的总计百分比。
  • 一些调查可能有成千上万的目标/参考答复者。

所以我创建的实体

public class HitsStatsDO implements Serializable 
{ 
    @Id 
    transient private Long id; 
    transient private Long version = (long) 0; 

    transient private Long startDate; 

    @Parent transient private Key parent; // fake parent which contains target id 
    @Transient int targetId; 

    private double avgPercent; 
    private long hitCount; 
} 

但是从每个用户为每个目标写HitsStatsDO将使大量的数据。例如,我对3000个目标进行了调查,一周内有400万人回答,第一天有30万人参与调查。即使我们假设他们均匀地回答了24小时,它也会给我们~1040个写/秒。显然,它达到了Datastore的并发写入限制。

我决定收集一小时的数据,除此之外,这就是为什么avgPercenthitCountHitsStatsDO。 GAE实例是无状态的,因此我必须使用dynamic backend instance

在那里,我有这样的事情:

// Contains stats for one hour 
private class Shard 
{ 
    ReadWriteLock lock = new ReentrantReadWriteLock(); 
    Map<Integer, HitsStatsDO> map = new HashMap<Integer, HitsStatsDO>(); // Key is target ID 

    public void saveToDatastore(); 
    public void updateStats(Long startDate, Map<Integer, Double> hits); 
} 

,并与碎片对当前小时和前一小时(不留在这里长期)

private HashMap<Long, Shard> shards = new HashMap<Long, Shard>(); // Key is HitsStatsDO.startDate 

所以每小时一次我映射将前一小时的碎片转储到数据存储。

另外我还有class LifetimeStats它将Map<Integer, HitsStatsDO>保留在memcached中,其中map-key是目标ID。

同样在我的backend shutdown hook method我将未完成小时的状态转储到数据存储。

只有一个在这里主要的问题 - 我只有一个后端实例 :)它提出了以下关于这一点我想听听你的意见的问题:在不使用后端

  • 我能做到这一点实例?
  • 如果一个实例不够,该怎么办?
  • 如何在多个动态后端实例之间拆分数据?这很难,因为我不知道我有多少人,因为随着负载的增加Google会创建新的。
  • 我知道我可以启动居民后端实例的确切数量。但有多少? 2,5,10?如果我一周没有任何负担,该怎么办?不断运行10个后端实例太昂贵了。
  • 在后端实例死机/重新启动时,如何处理来自客户端的数据?

有一点要注意的是我不能更改客户端。目前,JavaScript嵌入到客户的网页中。我可以通过某种方式更改RPC,但在体系结构上,我无法用Google文档格式替换客户端。

非常感谢你的想法。

回答

0

我的服务开始了,我想分享我是如何实现它的。

因此,不是在一个小时内收集单个后端实例的内存中的数据,而是决定将它收集到多个动态后端实例中,并在每个实例每隔10分钟更新Datastore中当前小时的分片。类Shard保持不变,但saveToDatastore()除外,我现在在事务循环中更新HitsStatsDOs,以确保即使另一个后端实例此刻更改分片,也会更新它。

为了获取HitsStatsDO真快,我决定把目标ID的假父键和时间戳,如果这很难主ID这样

public class HitsStatsDO implements Serializable 
{ 
    @Id 
    transient private Long id; // always equals to "startDate" 
    @Unindexed 
    transient private Long version = (long) 0; 

    transient private Long startDate; 

    @Parent 
    transient private Key targetIdKey; // fake parent which contains target id 

    @Unindexed 
    private double avgPercent; 
    @Unindexed 
    private long hitCount; 

    public Key<HitsStatsDO> createKey() 
    { 
     return new Key<HitsStatsDO>(targetIdKey, HitsStatsDO.class, startDate); 
    } 

    public HitsStatsDO(Long startDate, long targetId) 
    { 
     this.id = this.startDate = startDate; 
     this.targetIdKey = new Key(Long.class, targetId); 
    } 
} 

这个实体仅需2写入存储。写入量永远不会超过每小时([后端实例数量] * 2 * 6),这并不坏。此外,我可以在我的代码中预先创建密钥,并从Datastore进行批量获取。

同样我改变了HitsStatsTotalDO其中包含调查开始的统计数据。它看起来像这样

public class HitsStatsTotalDO implements Serializable 
{ 
    @Id 
    private Long targetId; 
    @Unindexed 
    transient private Long version = (long) 0; 

    @Unindexed 
    private double avgPercent; 
    @Unindexed 
    private long hitCount; 
} 

相同的东西-2写入存储/更新。

服务于3天前上线。迄今为止最大负载为230 QPS。我正在使用动态B1类型的实例。在配置中,我现在设置了最多4个实例,但对于我的乐趣,GAE从未实例化过多个实例。令人惊讶的是,我还没有并发异常。

让我知道如果您有任何问题或想我错过了什么。

谢谢大家的帮助。 StackOverflow是非常棒的社区。

1

开发人员不应该回避将离线资源,谷歌网站和Google数据API与gae集成在一起。

您可以设置一个导致您的调查表单的谷歌网站。

目标受访者会将自己的答案输入到您的表单中,并且Google网站会将其收集到单个Google文档电子表格中。

然后,您可以使用一个离线系统(而不是gae)来定期/每小时访问一次“电子表格”并通过google数据API来下载数据。

Google文档会为您提供输入数据的时间,而您的表单设计应该能够让受访者进行索引。这样,您将只能下载“电子表格”的各个部分。

您需要熟悉OAuth以及Google联合登录/ openid使用者。

您可以探索将受访者的登录与您的表单集成在一起。

事实上,你甚至可能不需要使用gae。

你应该可以使用谷歌网站API来更新你的页面,更新要显示的统计信息,将表格切换到新的电子表格。

然后使用gae仅用于生成用户特定的页面。

或者,如果您对gae有太大的亲和力,您可以使用它来生成调查页面,然后使用数据api将结果存储在Google文档中,但使用您自己的离线资源执行统计计算。

+0

谢谢您的反馈!不幸的是,我无法彻底改变客户。我应该在原帖中提及它。我更新了它。再次感谢! – expert

+1

文档格式不会扩展到一百万个受访者。 –

+0

“文档表单不会扩展到一百万个受访者” - Google说的是什么?像谷歌博客页面不会扩展到一百万读者?请记住,匿名无法击败亚马逊。会有一百万用户关闭一个谷歌页面吗? –

0

您在描述的模型上遇到并发写入限制的唯一原因是因为您正在创建同一父级的所有实例子实体。只有当您需要同一个实体组中的实体出于交易目的时才需要这样做,但在此情况并非如此。删除父级属性并将所有实体存储为顶级属性,并且不再需要担心更新率。

+0

让我来粗略计算成本。所以我想我会有6个写操作每个实体(2为实体+2为索引的targetId + 2为索引的时间戳)。然后调查3K目标和4M用户将给我12'000'000'000新的HitsStatsDO实体。这是72B写($ 0.1/100K)+ 12B小($ 0.01/100K),分别是$ 72K + $ 1200。对于一个受欢迎的调查来说,73.2美元是相当苛刻的价格:( – expert

+0

@ruslan在这种情况下,“3k目标”是什么? –

+0

这些参考答案是与用户的答案进行比较的,例如它可能是关于政治/社交/经济偏好,3000名政治家参与调查,然后公众也接受调查,现在你可以看到哪个候选人更接近大多数人的观点,更简单的例子是太阳镜网上商店,你不知道你想买什么。 10个问题,它给你什么太阳镜是最适合你的。:)类似的东西:) – expert