设计遐想---基于Google App Engine的IM
聊天模式无非点对点聊天和群组聊天.下面一一来分析.
消息格式:[from, to, body, timestamp, isread].(根据from来识别是个人还是group消息)
消息读取方式
标记模式
置位模式
是否要存储:
不需要
需要
[Group Chatting]:
标记模式
每个用户拥有一个timestamp类型的消息游标.(sequence number不利于sharding).
option: 消息携带一个counter, 每被读取一次引用计数就减一.引用计数到0时.消息会删除.
优点: 节省存储空间.
缺点: 实现稍微复杂.
Non-Persistent
只需要有一个Expirer, 定期清理超期信息.
Persistent
只需要关闭Expirer.
置位模式
需要复制Group Size个副本.获取最近的消息列表: 根据timestamp差值(timespan)和pageSize来获取最近消息列表.
优点: 实现简单
缺点: 浪费存储空间
Non-Persistent
用户读取后即删除.
Persistent
读取后设置isread为true
权衡
信息量小
需要持久化
可以直接操作db.
不需要持久化
可以直接memcache来实现.
信息量大
需要持久化
考虑write-back机制(timeout + 阀值触发).
不需要持久化
mem可能不足,仍然考虑write-back机制(timeout + 阀值触发).只不过读取后会删除.
在GAE平台上如何考虑
1. GAE memcache为简单key/value型,而消息存取最好是集合操作,也就是需要一个结构化的memcache(比如mongoDB).
2. GAE datastore为Master/Slave模型, 读快写慢,而IM的特征是读写平均. 但是基于GAE的可扩展性, 对于单笔操作而言开销不会随负载增长.
综上考虑, 消息每次直接经过DB.
置位还是标记?
优先选择置位模型.
Group信息在memcache中进行缓存.在DataStore中以StringListProperty的方式保存用户列表.
收发消息Solution 1:
1. 消息直接存入datastore.
2. 先取peer消息.
3. 取Group消息:
1). 获取用户的所有group(用户所在组的信息也存在于memcache).[complex, fucking thing]
2). 以groupid为key,去datastore检索消息.[loop]
收发消息Solution 2:
在置位模型下,消息可以保存一个StringListProperty类型的receivers字段.
1. 用户发送消息,根据group id得到用户列表,即为receivers字段取值.存入datastore.
2. 取消息. 对比用户id和receivers来还有时间戳来取消息.
收发消息solution的权衡:
如果群组数量很多,方案1的效率就很差.
如果单个群组的成员很多, 方案2的效率就很差, receivers列表的存储开销就比较明显了.