大家好,我是砸锅。一个摸鱼八年的后端开发。熟悉 Go、Lua。今天和大家一起学习 IM 系统😊
服务扩容
垂直扩展:提升资源服务器和应用服务器的单机处理能力,只能解决短期的资源和服务瓶颈
水平扩展:入口可以在 DNS 服务器中,针对接入服务域名配置多个 VIP;当用户访问接入服务时,DNS 服务器就会通过轮询或者其他策略,来从 A 记录中选择某一个 VIP 供用户连接。用户通过接入服务上线后,接入服务会在中央资源中(比如 Redis)记录当前用户在哪台网关机上线。如果业务层有消息需要推送给这个用户时,通过查询这个中央资源,就能知道当前用户连接在哪台网关机上,然后就可以通过网关机的 API 接口,把消息定向投递推送给用户了
业务层的多台服务器在启动上线时,先在“服务注册中心”进行服务注册,登记当前业务机器支持调用的“服务”;启动后,服务注册中心通过“注册中心主动检测”或者“业务服务器主动上报”的方式,定期对服务的可用性进行健康检查,不可用的业务服务器会从注册中心摘除。长连网关机在需要调用业务层服务时,会先通过服务发现、获取当前要用到的服务所注册的业务服务器地址,然后直连某一台具体的业务服务器进行 RPC 服务调用。这样,通过对业务层进行“服务化”改造,利用服务注册和服务自动发现机制,我们就能够让业务层做到完全的无状态化。不管我们的业务层如何进行扩缩容,接入层也能随时调用到业务层提供的服务,从而实现了业务层的“水平扩展”
当我们的服务需要扩容时,先把服务打包为 Docker 镜像,通过运维系统或者第三方的 Kubernetes 等容器管理服务,来动态分发镜像到需要部署的机器上,并进行镜像的部署和容器启停
系统监控
被动监控:系统层监控、应用层监控、全链路 Trace 监控
系统层监控是指对操作系统维度的一些指标做监控,例如 CPU、内存、IO、负载、带宽、PPS、Swap 等数据信息
应用层监控是指实时对消息收发接口的 QPS、耗时、失败数、消息在线推送到达率等分析
基于 Trace 服务,对消息的收发行为进行全链路监控数据收集。
Trace 表示对一次请求完整调用链的跟踪,每一条链路都使用一个全局唯一的 TraceID 来标识。Span 是指在链路调用中,两个调用和被调用服务的请求 / 响应过程叫做一次 Span。一条 Trace 可以被认为是由多个 Span 组成的一个有向无环图(DAG 图)
Annotation 主要用于用户自定义事件,Annotation 可以携带用户在链路环节中的自定义数据,用来辅助定位问题
群聊设计
通常对于群聊消息的存储,都是采取“读扩散”的方式,一条消息只针对群维度存储一次,群里用户需要查询消息的时候,通过群维度来获取
如果需要实现新加入群的用户看不到加群前的群聊消息,只需要在用户加群的时候记录一个加群消息,把用户加群的时间、用户加群时该群最新的一条消息的 ID 存储起来,然后用户查询消息的时候,根据这些信息来限制查询消息的范围
单个用户如果删除群消息,可以在用户删除的时候把这条被删除消息加入当前用户和群维度的一个关系记录里,当用户查询消息时,我们对群维度的所有消息,以及对这个“用户和群维度”的删除索引进行聚合剔除就可以了
群的未读数更新可以合并成一次更新多个用户未读数,减少请求
对于离线消息的优化,我们只需要存储消息 ID,避免重复的消息内容存储浪费离线 Buffer 资源,还可以参考 TCP 的 Delay ACK 机制,在接收方层面进行批量 ACK,降低服务端的处理并发压力
对于单聊场景中依赖“中央全局的在线状态”,来进行消息下推的架构瓶颈,我们可以在群聊场景中优化成“网关机本地自治维护”的方式,以此解决高并发下推时,依赖全局资源容易出现瓶颈的问题,从而提升群聊消息在线下推的性能和稳定性
对于长时间没有登录或者消息接收频繁的用户来说,当这些用户下线一段时候后再上线,经常会面临大量的离线消息下推的问题。一种比较好的优化方案是:对多条离线消息采取批量打包 + 压缩的方式来进行下推
面对上万条的离线消息,只单纯地采用批量 + 压缩的方式还是不够的,这种情况下,我们还可以采用“推拉结合”的方式来进行优化。用户上线后,对于服务端离线消息的下推,可以只推送用户最近 N 条消息;而对于后续要推送的消息,我们可以让用户通过向上翻页来触发自动拉取,客户端再从服务端来获取当前聊天会话剩下的消息
此文章为3月Day14学习笔记,内容来源于极客时间《即时消息技术剖析与实战》 这门课真的非常好,推荐大家看看