当前位置: 首页 > news >正文

Netty+SpringBoot的分布式宠友IM即时通讯系统,单机百万在线架构实践

一、连接层设计:如何管理百万长连接

IM系统的第一道坎是连接管理。每个客户端维持一个TCP长连接,百万连接意味着百万个Socket。常规BIO模型每个线程处理一个连接,百万线程直接撑爆服务器。

Netty的Reactor模型解决了这个问题。主线程负责Accept,一组Worker线程负责IO读写,业务逻辑交给异步线程池处理。Linux内核参数需要相应调整,ulimit -n开到100万,net.ipv4.ip_local_port_range扩大端口范围,开启tcp_tw_reuse快速回收TIME_WAIT连接。

二、协议设计:JSON就够了

有人迷信Protobuf,说JSON慢。实测下来,在IM场景下JSON序列化开销完全可以接受。JSON便于调试、跨语言友好、开发效率高。这套系统采用纯JSON格式,一条消息的核心结构如下:

{ "cmd": 101, "seq": 12345678, "from": "10001", "to": "10002", "type": 1, "body": {"text": "你好", "at": ["10003"]} }

协议层的一个核心设计是一端口多协议。Netty同一个端口同时解析自定义Socket协议、WebSocket协议和HTTP协议。通过判断前几个字节区分:自定义协议以特定魔数开头,GET/POST走HTTP,特定握手请求走WebSocket升级。

三、消息路由:用户在哪台机器上

分布式部署下,用户A连接网关节点1,用户B连接网关节点2。A发给B的消息需要从节点1路由到节点2。路由表存储在Redis中,key为用户ID,value为节点ID和连接ID。

用户登录时注册路由,心跳时更新最后活跃时间,断开时清理。节点收到消息后查询目标路由:若目标在本机则直接通过Channel写入,若在其他节点则通过内部RPC转发。内部RPC复用Netty长连接池,避免每次调用新建连接。

四、消息同步:读扩散与写扩散的选择

群聊消息的存储有两种策略。写扩散是发一条群消息给每个群成员存一份,读扩散是群消息只存一份,成员拉取时计算自己该看到哪些。

这套系统群消息采用读扩散,朋友圈采用写扩散。群聊消息量大,千人群发一条消息写扩散需要写入一千条记录,磁盘扛不住。读扩散只需要写一条,客户端本地维护已读位置,拉取增量时用时间戳查询即可。

已读回执单独记录到Redis,格式为read_status:{group_id}:{user_id}:{last_msg_id}。发送方可查询哪些成员已读,群聊场景下展示已读人数。

五、离线消息与漫游:三层保障不丢消息

消息可靠性做了三层保障。第一层同步确认,每条消息下发后客户端回复ACK,服务端超时未收到则重推。第二层离线队列,用户离线时消息存入Redis ZSET按时间戳排序,上线后批量拉取。

redis.opsForZSet().add("offline:" + userId, msgStr, msgTime); Set<String> msgs = redis.opsForZSet().rangeByScore("offline:" + userId, 0, now);

第三层漫游存储,所有消息全量写入MySQL分表存储。用户换设备时可拉取最近7天或最近500条历史消息。漫游拉取接口支持分页和时间范围过滤,千万级消息量的查询延迟控制在50毫秒以内。

六、群聊互动:@成员与消息撤回

@成员功能在消息体中用at_uids字段记录被@的用户列表。服务端收到后为每个被@用户单独推送一条系统通知,离线用户通过厂商推送收到@提醒。

消息撤回限制在2分钟内。撤回操作发送一条撤回指令,服务端将原消息标记为revoked=1,所有在线客户端收到指令后替换本地显示。已离线的用户上线时拉取到已标记撤回的消息,直接显示“对方撤回了一条消息”,不展示原始内容。

七、红包功能:Lua脚本保证原子性

红包涉及金额扣减和领取记录两个操作,必须原子执行。用Redis Lua脚本实现,服务端一次性将脚本传给Redis执行,中间不会被其他命令打断。

local remain = redis.call('hget', KEYS[1], 'remainCount') if tonumber(remain) <= 0 then return -1 end if redis.call('sismember', KEYS[1]..':takers', ARGV[1]) == 1 then return -2 end redis.call('hincrby', KEYS[1], 'remainCount', -1) redis.call('sadd', KEYS[1]..':takers', ARGV[1]) return 1

红包金额采用二倍均值法随机分配,每个红包的金额在剩余金额的0.01倍到剩余平均金额的2倍之间浮动。红包状态缓存在Redis,异步写入MySQL做对账,过期未领完的金额原路退回。

八、语音会议:信令与媒体分离

多人语音会议不走IM的消息通道。单独部署SFU服务器负责媒体流转发,IM系统只处理会议信令:发起会议、邀请成员、成员入会离会、静音控制。

信令通过原有WebSocket通道传输,格式如{"cmd":301,"meetingId":"mtg_123","action":"invite","targetUid":"10008"}。接收方收到信令后直接连接SFU服务器建立WebRTC通道。IM服务器不承担任何媒体流量,只处理信令转发,单机负载极低。

九、存储架构:MySQL+Redis的分层策略

Redis存储热数据:在线状态、未读计数、用户会话路由、最近聊天列表。MySQL存储冷数据:消息漫游记录、用户资料、好友关系、红包订单。

消息漫游表按用户ID哈希分16个库,每个库按月份分表。查询历史消息时先计算用户所在分库,再根据时间范围定位到具体月份表。离线消息从Redis拉取后立即清除,不落MySQL,减少不必要的写入压力。

宠友IM官方演示示例https://www.chongyou.info/1/product/im.html

http://www.jsqmd.com/news/811542/

相关文章:

  • ChromaControl:如何用智能技术终结RGB设备控制混乱局面
  • 【Perplexity AI科研提效指南】:IEEE文献检索效率提升300%的5个隐藏技巧
  • 长期使用Taotoken Token Plan套餐在月度账单上体现的成本优势
  • 1.8.2 掌握Scala类与对象 - 单例对象与伴生对象
  • ODRP开发日记-靠近NPC触发交互(一)
  • LangForce方法:强化VLA模型语言依赖,提升分布外泛化能力并保留语言核心功能
  • 非洲车商采购中国二手车的完整流程:从找车到提车七步走
  • Python 爬虫进阶技巧:本地代理配置爬虫全局网络代理
  • 终极ASN.1 Editor指南:三步快速可视化复杂二进制数据
  • 一个人开发超越OiiOii的开源动画AI Agent:完整技术栈与路线图
  • 5.10
  • AI 原生营销矩阵系统:账号与素材分组协同管理技术实现
  • CH582M蓝牙无感配对与TMOS框架下的RS485联动控制
  • 你的SSD在Linux下掉盘、报CRC错误?可能是SATA线或主板接口的锅,手把手教你用smartctl排查链路问题
  • Gemini Pro函数调用(Function Calling)深度解析,7类高频业务场景适配方案(含TypeScript强类型定义模板)
  • 亲测兴化别墅公司,对比复盘分享 - 花开富贵112
  • 如何反查竞品最近30天内新增的差评关键词,并优化Listing卖点?
  • ARM MPAM内存带宽监控机制解析与应用实践
  • X20BM15数字输入模块
  • C++ 条件变量 condition_variable
  • 游戏设计中的心流理论对开发者工作效率的启发——以软件测试从业者为视角
  • 简单学习 --> Cookie 和Session
  • 重复率和AI率都超标怎么一次降?嘎嘎降AI双引擎几分钟双降不打架! - 我要发一区
  • 领星、聚水潭与金蝶云星空三方系统对接技术方案
  • MediaCreationTool.bat:Windows部署自动化脚本封装架构深度解析
  • Midjourney提示词工程终极护城河:基于CLIP文本嵌入空间的向量对齐技术(附Python可视化调试工具)
  • 各方筹码三分天下通达信指标筹码三分法含1主图2副图1选股工具
  • 【Amazon Quick 桌面 AI 助手初体验】把重复造轮子的活交给 Quick 大显身手
  • SSD201-富利威
  • 5分钟永久激活Windows和Office:KMS智能激活终极指南