在这里插入图片描述

架构设计

主要分为连接层和应用层,连接层用来保持长连接,应用层用来进行消息存储,消息分发等
在这里插入图片描述
因为连接层是一个有状态的服务,所以当用户登录的时候,我们需要把用户,以及登录的网关地址存到redis中

消息发送的过程中,我们主要考虑如何保证消息的实时性,有序性,可靠性,幂等性

实时性

实时性的核心是降低延迟,让消息尽可能的触达消息方

使用长连接来发送和接收消息

  1. 为了高性能可以自定义协议
  2. 简单一点的话使用WebSocket就行

连接层和业务层进行分离

设置专门的网关服务来管理用户的长连接,用来进行加机密和心跳检测。网关可以通过mq将消息转发到业务层

有序性

在这里插入图片描述

我们将不同的消息类型,转发到不同的消息队列中,如果队列中的消息串行的去处理,肯定不会有顺序的问题,但是性能是不可接收的,所以要用多线程去消费消息,如果要用多线程消费消息,如何保证消息的有序性呢?

我们可以利用msgSeq保证消息的有序性,那么msgSeq如何赋值呢?

维度 介绍
客户端发送时间 不行,客户端的时间可以进行修改
雪花算法 不行,生成的id是趋势递增的
redis incr 严格递增,实现简单

单聊维度可以利用发送方和接收方生成一个计数器
群聊维度可以利用房间id生成一个计数器

服务端给消息添加一个msgSeq属性,给msgSeq赋值的过程是在一个单线程中,其他的过程是多线程

客户端收到消息后,不直接按照接收时间展示,而是按照消息体中的msgSeq排序后来展示。

如果客户端发现新收到的消息 msgSeq = 5,而本地最大的还是 3(中间丢了 4),客户端会暂时把 5 放入缓存队列中不展示,同时向服务器发起请求“补发第 4 条消息”,或者直接触发一次离线拉取,等 4 到了之后再按顺序一起渲染

可靠性

在这里插入图片描述

以A给B发消息为例,主要的步骤如下

  1. A发消息到server,server回一个server ack,表明服务器收到消息了
  2. server再将消息发送给b,b给server回一个client ack,server再把a这个client ack回给a,表明b收到消息了

a在每次发完消息后会在本地启动一个定时器,在没收到2个ack之前会不断的重试。因为不断的重试,就有可能造成接收方收到两条相同的消息,这个应该怎么处理呢?

幂等性

客户端生成一个msgId,服务端根据msgId去重

服务端在内存检查一下这个消息有没有被处理过,如果被处理过,则只会发给B,不会进行持久化

如果客户端收到2个msgId相同的数据,也只会展示一条

如果重试后还是不成功,页面上显示感叹号,如果用户点击重发的时候,会生成一条新的msgId

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐