1、基础架构:

  • 接入层:连接保持、协议解析、Session 维护(用户和链接的关联关系)、消息推送。

    • 接入层和业务层拆分:消息收发的出入口需要高可用;分开后有助于提升业务开发效率;不需要跟着业务变动频繁发布重启;业务层可以屏蔽协议细节。
  • 业务层:未读数变更、最近联系人等业务场景。
  • 存储层:持久化存储。用于离线消息等。
  • 外部接口:如push等。
    2、特性:
  • 实时性

    • 发:

      • 提供一个http接口,客户端发消息给IM服务器
      • 客户端和IM服务器维持一个TCP长链接,用私有协议封装(如protobuf)。
    • 收:

      • 长链接和push。需要维护一个可靠的长链接:

        • 客户端和服务器精确感知可用性,断线快速重连。
        • 通过这个长链接发送的消息不丢失。
        • TCP协议:Websocket最常见。XMPP(成熟,可扩展,但XML太大),MQTT(推送场景下被广泛使用,但不是很契合IM,不支持群组和离线消息)
      • 轮询:

        • 短轮询:不断的发HTTP请求
        • 长轮询:短轮询的优化,无消息是服务器不立即返回,而是挂起,有新消息或超时后再返回。但是并没有解决大量无效请求,也没有降低QPS(只是入口层降低了,后端还是轮询判断)。
      • 未读数(角标):需要维护一个总未读数,每个会话也需要维护一个未读数。

        • 总未读和每个会话未读,是需要分布式锁/原子操作。
  • 可靠性:不丢消息、不重复消息。

    • 客户端发给服务器丢失:消息加唯一ID,客户端重试+服务器去重。
    • 其他异常:如服务器转给客户端失败,客户端存本地DB失败等,业内一般实现一个业务层的ACK机制。

      • 服务器下发后把ID放入待确认列表,收到客户端ACK后服务器从列表删除。
      • 服务器超时未收到ACK后,会把消息重传,客户端也要进行去重。
      • 特殊情况:服务器下发后未收到ACK时,服务器宕机,客户端重连后并不知道刚才丢了消息,所以客户端上线后拿最新的ID来服务器取大于这个ID的所有消息即可。
      • TCP有ACK为什么业务还要做ACK:

        • 服务器写入TCP缓冲区后,客户端从TCP缓冲区读取后,都可能会失败。而且连接断开时(比如手机crash或没电关机),双方的缓冲区都会直接销毁(即时里面还有未发/未读的数据)。
  • 一致性:顺序保持一致。分布式下,两条消息间肯定会有时间差。

    • 时序基准:全局序号生成器,可以用redis的incr或snowflake算法。但是无法解决同一精度内多个并发消息的排序。
    • 包内整流:多个消息必须要严格顺序时,比如分手+取关,就不能先取关,否则分手发不过去。这时可以以包为单位,packageId+seqId为一条消息,一个包内的消息全部达到后再进行处理,否则丢弃或重试。
  • 安全性:

    • 传输安全:HTTPS/HTTPDNS,明文传输时内容TLS加密。
    • 存储安全:端到端加密,出收发双方,服务器也无法破解。
    • 内容安全:敏感词、图片、语音、外链检测、禁言等。
      3、心跳:除了两端自身问题外,运营商的NAT也会剔除空闲链接以节省资源,这种情况下服务器和客户端均无法感知,只能依赖心跳。
  • TCP keepalive:操作系统实现,默认关闭,三个可配的参数默认值是心跳周期2小时、失败9次、超时75秒。不带数据的心跳包,省带宽,但无法感知如死锁/阻塞等业务异常,也无法动态调整心跳周期。因此只代表网络可用。如WhatApps空闲期10秒一次。
  • 应用层心跳:微博2分半,微信4分半,WhatApps30秒到1分钟。QQ是固定45秒。
    4、推送:
  • 通过中央存储(用户和所在网关的映射关系)进行下推。
  • 推送给所有网关机,有网关机查找链接是否在本机,自行推送。

标签: 架构, 即时消息

添加新评论