我使用Google App Engine(Python)作为手机游戏的后端,其中包括社交网络集成(twitter)和全球&相对排行榜。我的应用程序使用两个任务队列,一个用于构建玩家之间的关系,另一个用于在玩家的分数发生变化时更新这些对象。
模型
class RelativeUserScore(ndb.Model):
ID_FORMAT = "%s:%s" # "friend_id:follower_id"
#--- NDB Properties
follower_id = ndb.StringProperty(indexed=True) # the follower
user_id = ndb.StringProperty(indexed=True) # the followed (AKA friend)
points = ndb.IntegerProperty(indexed=True) # user data denormalization
screen_name = ndb.StringProperty(indexed=False) # user data denormalization
profile_image_url = ndb.StringProperty(indexed=False) # user data denormalization
这允许我通过查询请求用户是关注者的对象来构建相对排行榜。
推送任务队列
我基本上要执行两项主要任务:
sync-twitter
任务将从twitter的API中获取好友/关注者,并构建相对用户得分模型。用户注册时会检查朋友,如果他们的推特朋友计数发生变化,则会再次检查。只有在用户注册时才会检查关注者。它在自己的模块中运行F4实例,min_idle_instances设置为20(尽管实例内存使用至少需要F2实例,但我希望尽可能减少这两个设置。)
- name: sync-twitter
target: sync-twitter # target version / module
bucket_size: 100 # default is 5, max is 100?
max_concurrent_requests: 1000 # default is 1000. what is the max?
rate: 100/s # default is 5/s. what is the max?
retry_parameters:
min_backoff_seconds: 30
max_backoff_seconds: 900
update-leaderboard
任务将在玩游戏后更新所有用户的对象(只需大约2分钟即可完成)。它在自己的模块中运行F2实例,min_idle_instances设置为10(如果可能,我希望减少两个设置)。
- name: update-leaderboard
target: update-leaderboard # target version / module
bucket_size: 100 # default is 5, max is 100?
max_concurrent_requests: 1000 # default is 1000. what is the max?
rate: 100/s # default is 5/s. what is the max?
我已经优化了这些任务,使它们异步运行,并显着缩短了运行时间。大多数情况下,任务需要0.5到5秒。我还将两个任务队列放在他们自己的专用模块上,并且自动缩放比例很高(分别使用F4和F2服务器类型)但是,我仍然遇到了一些问题。 / p>
您还可以看到我试图最大化bucket_size和max_concurrent_requests,以便尽可能快地完成这些任务。
问题
DeadlineExceededErrors: The API call taskqueue.BulkAdd() took too long to respond and was cancelled.
update-leaderboard
队列开始落后于大约10K CCU。我非常希望为至少100K CCU做好准备。 (通过CCU我意味着实际用户在玩我们的游戏,而不是并发请求的数量,这在25K用户中只有大约500个前端api请求/秒。 - 我使用locust.io来加载测试)潜在的优化/问题
我的第一个想法可能是前两个问题只处理每个任务类型的单个任务队列。也许这种情况正在发生,因为基础Bigtable在这些调用期间分裂? (参见this article,特别是"稳定性能的队列分片")
因此,可能将每个队列分成10个不同的队列。我认为问题#3也会从这个队列分片中受益。所以... 1.对问题#1和#2的根本原因有任何想法吗?分片实际上有助于消除这些错误吗? 2.如果我进行队列分片,我是否可以将所有队列保留在相同的模块上,并依靠其自动缩放来满足需求?或者我会更好地使用每个模板的模块? 3.任何动态缩放分片的方法吗?
我的下一个想法是尝试减少对update-leaderboard
任务的调用。不是每个完整游戏都会直接转换为排行榜更新的东西。但我需要一些东西,如果用户只玩一个游戏,它保证最终更新对象。有关实施此减少的任何建议吗?
最后,所有模块'自动缩放参数和队列的参数是任意设置的,试图在最大化这些参数时犯错误。有关适当设置这些的建议,以便我不会花费比我需要的更多的资源吗?