如何设计一个聊天系统?

架构设计
主要分为连接层和应用层,连接层用来保持长连接,应用层用来进行消息存储,消息分发等
因为连接层是一个有状态的服务,所以当用户登录的时候,我们需要把用户,以及登录的网关地址存到redis中
消息发送的过程中,我们主要考虑如何保证消息的实时性,有序性,可靠性,幂等性
实时性
实时性的核心是降低延迟,让消息尽可能的触达消息方
使用长连接来发送和接收消息
- 为了高性能可以自定义协议
- 简单一点的话使用WebSocket就行
连接层和业务层进行分离
设置专门的网关服务来管理用户的长连接,用来进行加机密和心跳检测。网关可以通过mq将消息转发到业务层
有序性

我们将不同的消息类型,转发到不同的消息队列中,如果队列中的消息串行的去处理,肯定不会有顺序的问题,但是性能是不可接收的,所以要用多线程去消费消息,如果要用多线程消费消息,如何保证消息的有序性呢?
我们可以利用msgSeq保证消息的有序性,那么msgSeq如何赋值呢?
| 维度 | 介绍 |
|---|---|
| 客户端发送时间 | 不行,客户端的时间可以进行修改 |
| 雪花算法 | 不行,生成的id是趋势递增的 |
| redis incr | 严格递增,实现简单 |
单聊维度可以利用发送方和接收方生成一个计数器
群聊维度可以利用房间id生成一个计数器
服务端给消息添加一个msgSeq属性,给msgSeq赋值的过程是在一个单线程中,其他的过程是多线程
客户端收到消息后,不直接按照接收时间展示,而是按照消息体中的msgSeq排序后来展示。
如果客户端发现新收到的消息 msgSeq = 5,而本地最大的还是 3(中间丢了 4),客户端会暂时把 5 放入缓存队列中不展示,同时向服务器发起请求“补发第 4 条消息”,或者直接触发一次离线拉取,等 4 到了之后再按顺序一起渲染
可靠性

以A给B发消息为例,主要的步骤如下
- A发消息到server,server回一个server ack,表明服务器收到消息了
- server再将消息发送给b,b给server回一个client ack,server再把a这个client ack回给a,表明b收到消息了
a在每次发完消息后会在本地启动一个定时器,在没收到2个ack之前会不断的重试。因为不断的重试,就有可能造成接收方收到两条相同的消息,这个应该怎么处理呢?
幂等性
客户端生成一个msgId,服务端根据msgId去重
服务端在内存检查一下这个消息有没有被处理过,如果被处理过,则只会发给B,不会进行持久化
如果客户端收到2个msgId相同的数据,也只会展示一条
如果重试后还是不成功,页面上显示感叹号,如果用户点击重发的时候,会生成一条新的msgId
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)