TableStore发布Timeline Lib:轻松构建千万级IM和Feed流系统

点击有惊喜


场景

移动互联网时代,微信和微博已经成为这个时代的两大支柱类社交应用。
这两类应用,其中一个是IM产品,一个是Feed流产品,微信的朋友圈也属于Feed流。
如果再细心去发现,会发现基本所有移动App都有Feed流的功能:消息广场、个人关注、通知、新闻聚合和图片分享等等。各种各样的Feed流产品占据了我们生活的方方面面。

现状

IM和Feed流功能已经基本成为所有App标配,如何开发一个IM或者Feed流功能是很多架构师、工程师要面临的问题。虽然是一个常见功能,但仍然是一个巨大的挑战,要考虑的因素非常多,比如:

  1. 存储系统里面如何选型才能支持大量数据存储,而且价格便宜。
  2. 同步系统使用何种架构、推送模型和系统才能保证高并发的同时延迟稳定?

为了解决上述问题,我们之前推出了三篇文章来阐述:

上述三篇文章推出后,用户反响很好,在各个平台的传播很广,为很多用户提供了设计一款IM和Feed流产品的架构思路,但是从这里到完全实现一个可靠的IM、Feed流系统平台还有很长的路,比如:

  1. 如何才能更加简单的弄懂消息存储和同步模型?
  2. 如何将架构模型映射到存储系统和推送系统中?
  3. 如何才能保证对存储系统和推送系统的使用是最佳方式?

Timeline模型

针对上述三个问题,在《现代IM系统中消息推送和存储架构的实现》中引入了一个逻辑模型概念:Timeline模型。

在《现代IM系统中消息推送和存储架构的实现》中基于IM系统提出了Timeline模型,进一步会发现Timeline模型适用场景可以更广泛:

  • 有一方是消息生产者。
  • 有一方是消息消费者。
  • 生产者产生的一条消息可能会被一个或多个消费者消费。
  • 消费者需要聚合来自多个生产者的消息在一个页面展现。

IM和Feed流产品完全匹配上述四个特征,所以Timeline模型可以完全适用于IM和Feed流场景中。

模型适配性

下面我们来看看如何在各个场景中使用Timeline:

IM
  • 单聊就是三个Timeline:

    • 会话Timeline(存储历史消息)
    • 用户A的收件箱(A的同步库Timeline)
    • 用户B的收件箱(B的同步库Timeline)
  • 群聊就是1 + N个Timeline:

    • 会话Timeline(存储历史消息)
    • 用户A的收件箱(A的同步库Timeline)
    • 。。。。。。
    • 用户N的收件箱(N的同步库Timeline)

每个用户只有一个同步库Timeline,就算用户A在10个群里面,那么这个10个群的同步消息都是发送给用户A的这一个同步库Timeline中。

朋友圈
  • 发一条朋友圈状态就是1 + N个Timeline:

    • 自己历史消息Timeline(自己的存储库Timeline)
    • 朋友A的收件箱(A的同步库Timeline)
    • 。。。。。。
    • 朋友N的收件箱(N的同步库Timeline)
微博

如果是微博,粉丝可能会达到上亿级别,这时候会比朋友圈稍微复杂些:

  • 大V发一条微博就是 1 + M个Timeline(M << N,N是粉丝数)。

    • 自己历史消息Timeline(自己的存储库Timeline)
    • 粉丝A的收件箱(A的同步库Timeline)
    • 。。。。。。
    • 粉丝M的收件箱(M的同步库Timeline)
      剩余N - M的粉丝直接读大V自己的存储库Timeline内容即可,那么怎么设置M和N - M,可以参考《如何打造千万级Feed流系统》这篇文章。

从上面分析可以看出来,不管是IM,还是Feed流产品都可以将底层的存储、同步逻辑抽象成一个对多个Timeline进行读写的模型。

问题

有了Timeline概念模型后,从IM/Feed流应用映射到Timeline就比较容易了,但是从Timeline映射到存储、同步系统仍然很复杂,主要体现在:

  • 如何实现Timeline概念模型?
  • 如何将Timeline模型转换成对存储系统、同步系统的读写接口?
  • 如何设计存储系统、同步系统的表结构?
  • 如何选择存储系统、同步系统的读写方式?
  • 如何评估存储系统、同步系统的最大承载能力?
  • 如何实现才能保证性能最佳?
  • 如何才能吸取大型同类型系统架构设计的经验教训、
  • 如何才能避免一些实现、使用上的隐患?

这些问题涉及的内容光,细节多,深度大,坑较多等,整体上很繁杂,这一部分在耗费了大量人力之后,结果可能并不理想。

Timeline LIB

针对上述问题,只要存储系统和推送系统确定后,剩余的工作都是类似的,可以完全将经验封装起来成为一个LIB,将表结构设计,读写方式,隐患等等都解决好,然后供后来者使用,后来者可以不用再关心Timeline到底层存储系统之间的事情了。

所以,我们基于JAVA语言实现了一个TableStore-Timeline LIB,简称Timeline LIB。

目前已经开源在了GitHub上:Timeline@GitHub

Timeline LIB的结构如下:
timeline1

整个Timeline分为两层,上层的Timeline层和下层的Store层。

Timeline层,提供最终的读写接口,用户操作的也是Timeline的接口。

Store层,负责存储系统的交互,目前Timeline LIB中提供了DistributeTimelineStore,基于Table Store,同时实现了分布式的存储和同步。后续会继续实现GlobalTimelineStore等。如果有用户有其他系统需求,比如MySQL,Redis,可以通过实现IStore接口来新增MySQLStore和RedisStore。

也欢迎大家将自己实现的Store通过GitHub的PullRequest共享出来。

有了Timeline LIB之后,如果要实现一个IM或者Feed流,只需要创建两种类型Timeline(存储类,同步类),然后调用Timeline的读写接口即可。

接口

接下来,我们看下Timeline LIB的API。
Timeline LIB中面向最终用户的是Timeline类,用于对每个Timeline做读写操作。

Timeline的接口主要分为三类:

  • 写:

    • store:同步写入
    • storeAsync:异步写入
    • batch:批量写入
  • 读:

    • get:同步读
    • getAsync:异步读
  • 范围读:

    • scan:同步范围读取
定义
   /**
     * Timeline的构造函数。
     * @param timelineID    此Timeline对应的ID。唯一标识一个Timeline,需要全局唯一,如果业务场景中需要多个字段才能唯一标识一个TimelineID,此时可以将多个字段拼接成一个字段。
     * @param store         此Timeline关联的Store,一般为存储Store或同步Store。实现了IStore接口类的对象,目前LIB中默认实现了DistributeTimelineStore,可以使用此store。除此之外,用户还可以自己实现自己的Store类,用于适配其他系统。
     */
    public Timeline(String timelineID, IStore store);

    /**
     * 写入一个消息到此Timeline中。
     * @param message   消息对象,需实现IMessage接口。用户需要通过实现IMessage接口创造符合自己业务场景的消息类,LIB中默认实现了StringMessage类。
     * @return          完整的TimelineEntry,包括消息和顺序ID。
     */
    public TimelineEntry store(IMessage message);
    
    /**
     * 异步写入消息接口。
     * @param message     消息对象,需实现IMessage接口。
     * @param callback    回调函数。
     * @return            Future对象,异步模式下,Future和callback需要二选一。
     */
    public Future<TimelineEntry> storeAsync(IMessage message, TimelineCallback<IMessage> callback);    
    
   /**
     * 批量写入消息接口。
     * 此接口只是把消息加入到本地的一个buffer中,当buffer满或者超时(默认10s,可配置)才会统一写入。
     * 此接口返回时并不一定消息已经写入成功。
     * @param message 消息对象,需实现IMessage接口。
     */
    public void batch(IMessage message) {

   /**
     * 同步读取接口,通过制定一个唯一的顺序ID读取目标TimelineEntry。
     * @param sequenceID    顺序ID。此顺序ID可由store或scan接口获取到。
     * @return              完整的TimelineEntry,包括消息和顺序ID。
     */
    public TimelineEntry get(Long sequenceID);
    
    /**
     * 异步读取接口,通过制定一个唯一的顺序ID读取目标TimelineEntry。
     * @param sequenceID    顺序ID。
     * @param callback      读取结束后的回调函数。
     * @return              Future对象,异步模式下,Future和Callback需要二选一。
     */
    public Future<TimelineEntry> getAsync(Long sequenceID, TimelineCallback<Long> callback);

    /**
     * 顺序读取一段范围内或固定数目的消息,支持逆序,正序。
     * @param parameter     顺序读取的参数,包括方向、from、to和maxCount。
     * @return              TimelineEntry的迭代器,通过迭代器可以遍历到待读取的所有消息。
     */
    public Iterator<TimelineEntry> scan(ScanParameter parameter);

点击有惊喜


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值