Google app engine 如何平衡Google App Engine的多个*动态*后端实例的负载?
我对StackOverflow比较陌生,不确定它是否适合提出设计问题。该网站给了我一个提示:“你所问的问题似乎是主观的,很可能会被关闭。”。也许应该继续问下去。请让我知道 无论如何。。我正在从事的项目之一是在线调查引擎。这是我在电视上的第一个大型商业项目 我需要你的建议,如何收集统计数据,并有效地记录在数据存储中,而不会让我破产。初步要求如下:Google app engine 如何平衡Google App Engine的多个*动态*后端实例的负载?,google-app-engine,architecture,scalability,backend,software-design,Google App Engine,Architecture,Scalability,Backend,Software Design,我对StackOverflow比较陌生,不确定它是否适合提出设计问题。该网站给了我一个提示:“你所问的问题似乎是主观的,很可能会被关闭。”。也许应该继续问下去。请让我知道 无论如何。。我正在从事的项目之一是在线调查引擎。这是我在电视上的第一个大型商业项目 我需要你的建议,如何收集统计数据,并有效地记录在数据存储中,而不会让我破产。初步要求如下: 用户完成调查后,客户端发送配对列表[ID(int)+命中百分比(double)]。此列表显示此用户的答案与参考答案(由ID标识)的预定义答案的匹配程度
- 用户完成调查后,客户端发送配对列表[ID(int)+命中百分比(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次。显然,它达到了数据存储的并发写入限制
我决定收集一小时的数据并保存,这就是为什么HitsStatsDO
中有avgPercent
和hitCount
。GAE实例是无状态的,所以我必须使用
这里我有这样的东西:
// 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);
}
//包含一小时的统计信息
私有类碎片
{
ReadWriteLock lock=new ReentrantReadWriteLock();
Map Map=new HashMap();//键是目标ID
public void saveToDatastore();
公共无效更新状态(长起始日期、地图点击次数);
}
并使用当前小时和前一小时的碎片进行映射(不会在这里停留太久)
private HashMap shards=new HashMap();//钥匙是HitsStatsDO.startDate
所以每小时我会将前一个小时的碎片转储到数据存储中一次
另外,我还有类生命状态
,它将映射
保存在memcached中,其中映射键是目标ID
在我的数据库中,我也会将未完成小时的统计数据转储到数据存储中
这里只有一个主要问题-我只有一个后端实例:)它提出了以下问题,我想听听您的意见:
- 我可以在不使用后端实例的情况下执行此操作吗
- 如果一个实例还不够呢
- 如何在多个动态后端实例之间拆分数据?这很难,因为我不知道我有多少,因为随着负载的增加,谷歌会创建一个新的
- 我知道我可以启动确切数量的常驻后端实例。但是有多少?2, 5, 10 ? 如果我一个星期都没有工作怎么办。持续运行10个后端实例太贵了
- 当后端实例死机/重新启动时,如何处理来自客户端的数据
非常感谢您的想法。开发者不应回避将离线资源、谷歌网站和谷歌数据api与gae集成 你可以建立一个谷歌网站,引导你的调查表格 目标受访者会将他们的答案输入到你的表格中,谷歌网站会在一个谷歌文档电子表格中收集他们的答案 然后使用离线系统(不是gae),通过谷歌数据api定期/每小时访问“电子表格”以下载数据 谷歌文档将为您提供数据输入的时间,而您的表单设计应允许受访者编制索引。这样,您将能够只下载“电子表格”的部分 您需要熟悉OAuth,也许还需要熟悉google federated login/openid consumer 您可以探索将受访者的登录名与您的表单集成 事实上,您甚至不必使用gae 您应该能够使用google sites api更新页面,更新要显示的统计信息,将表单切换到新的电子表格 然后仅使用gae生成特定于用户的页面
或者,如果您的afinity对于gae来说太棒了,您可以使用它生成调查页面,然后使用数据api将结果存储在google文档中,但是,请使用您自己的脱机资源来执行统计计算。您所描述的模型上遇到并发写入限制的唯一原因是,您正在将所有实例作为同一父实体的子实体。仅当出于事务目的需要同一实体组中的实体时,才有必要这样做,但此处的情况并非如此。删除父属性并将所有实体存储为顶级实体,您就不必再担心更新率。我的服务上线了,我想分享一下我是如何实现它的 所以,我决定在多个动态后端实例中收集数据,并每隔10分钟从每个实例更新数据存储中当前一小时的碎片,而不是在单个后端实例的内存中收集一小时的数据。类
Shard
保持不变,除了saveToDatastore()
之外,我现在在事务循环中更新HitsStatsDOs
,以确保即使另一个后端实例此时更改了Shard,它也会被更新
为了快速获取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);
}
}
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次写入存储/更新
服务
public class HitsStatsTotalDO implements Serializable
{
@Id
private Long targetId;
@Unindexed
transient private Long version = (long) 0;
@Unindexed
private double avgPercent;
@Unindexed
private long hitCount;
}