手把手教你用SpringBoot+Netty仿写一个简易版微信(含好友、群聊、历史消息)
从零构建高并发IM系统SpringBoot与Netty深度整合实战在即时通讯(IM)领域如何实现高并发、低延迟的消息传递一直是技术挑战的核心。本文将带您从产品设计到代码实现完整构建一个支持好友管理、群组聊天和历史消息查询的IM系统。不同于简单的Demo演示我们将重点关注业务闭环设计和技术架构的有机结合使用SpringBoot处理复杂业务逻辑Netty保障通信效率最终形成一个可扩展的生产级解决方案。1. 架构设计与技术选型1.1 为什么选择SpringBootNetty组合传统WebSocket方案在小型应用中表现尚可但当用户量增长到万级时就会暴露出性能瓶颈。我们的技术栈组合具有以下优势Netty的NIO模型相比传统BIO单机可支持10W长连接SpringBoot的便利性快速搭建业务系统与Netty形成互补协议灵活性可轻松扩展支持自定义二进制协议// Netty服务启动示例 EventLoopGroup bossGroup new NioEventLoopGroup(1); EventLoopGroup workerGroup new NioEventLoopGroup(); try { ServerBootstrap b new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializerSocketChannel() { Override protected void initChannel(SocketChannel ch) { // 添加协议处理链 } }); ChannelFuture f b.bind(8080).sync(); f.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); }1.2 核心功能模块划分模块技术实现QPS指标用户认证Spring Security JWT5000消息传输Netty自定义协议10W关系管理MySQL Redis缓存3000消息存储MongoDB分片集群1W在线状态Redis Pub/Sub实时更新2. 核心功能实现细节2.1 用户关系管理系统好友关系处理需要考虑多种边界情况双向好友关系验证黑名单机制好友分组管理关系变更通知// 好友关系实体设计 Entity public class UserRelation { Id private Long id; private Long userId; private Long friendId; private RelationStatus status; // 枚举NORMAL/BLOCKED/DELETED private LocalDateTime createTime; private String remark; // 建立复合索引 Indexed(unique true) private String relationKey; // userId:friendId组合 }提示使用Redis缓存活跃用户的好友列表可降低数据库压力80%以上2.2 群组聊天实现方案群聊系统的核心挑战在于消息广播效率。我们采用分级推送策略小群(50人以下)直接全员推送中群(50-500人)分批推送消息合并大群(500人以上)仅推送在线用户离线消息异步处理public void handleGroupMessage(GroupMessage message) { // 获取群成员列表带在线状态 ListGroupMember members groupService.getMembers(message.getGroupId()); // 分流处理 if(members.size() 50) { // 小群直接推送 members.forEach(member - { if(member.isOnline()) { sendToUser(member.getUserId(), message); } }); } else { // 大群异步处理 messageQueue.add(message); } // 消息持久化 messageRepository.save(message); }3. 消息系统的高性能设计3.1 消息协议优化采用二进制协议替代JSON节省30%以上带宽----------------------------------------------- | 魔数(4) | 版本(1) | 命令(1) | 长度(4) | 数据体(N) | -----------------------------------------------关键参数说明魔数0xABCDEF用于识别有效数据包命令字1单聊 2群聊 3心跳 4ACK长度数据体字节数防止粘包3.2 历史消息存储策略结合冷热数据分离原则设计存储方案消息类型存储介质保留时间查询方式最近7天Redis集群7天直接获取7-30天MongoDB分片30天索引查询30天以上对象存储(OSS)永久异步导出// 消息分页查询实现 public PageMessage queryHistory(Long userId, Long targetId, int page, int size) { // 先查Redis ListMessage messages redisTemplate.opsForList() .range(buildKey(userId, targetId), page*size, (page1)*size-1); if(messages.size() size) { // Redis不足时查MongoDB messages.addAll(mongoTemplate.find( Query.query(Criteria.where(userId).is(userId) .and(targetId).is(targetId)) .skip(page*size) .limit(size - messages.size()), Message.class)); } return new PageImpl(messages, PageRequest.of(page, size), count); }4. 性能优化关键点4.1 Netty参数调优// 关键参数配置 bootstrap.option(ChannelOption.SO_BACKLOG, 1024) .childOption(ChannelOption.TCP_NODELAY, true) .childOption(ChannelOption.SO_KEEPALIVE, true) .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);优化效果对比参数默认值优化值连接数提升SO_BACKLOG50102420%TCP_NODELAYfalsetrue15%使用内存池分配器否是30%4.2 消息处理流水线设计Netty的ChannelPipeline是性能关键Inbound: ByteBuf - LengthFieldDecoder - ProtocolDecoder - AuthHandler - BusinessHandler Outbound: BusinessHandler - ProtocolEncoder - LengthFieldPrepender - ByteBuf注意编解码器要放在pipeline最外层避免重复编解码5. 生产环境部署方案5.1 集群部署架构----------------- | Nginx (SLB) | ---------------- | -------------------------------- | | | -------------- -------------- -------------- | Netty Node1 | | Netty Node2 | | Netty Node3 | -------------- -------------- -------------- | | | -------------- -------------- -------------- | SpringBoot Svc| | SpringBoot Svc| | SpringBoot Svc| -------------- -------------- -------------- | | | -------------- -------------- -------------- | Redis集群 | | MySQL主从 | | MongoDB分片 | --------------- --------------- ---------------5.2 监控指标配置使用Prometheus采集关键指标# application.yml配置示例 management: endpoints: web: exposure: include: prometheus metrics: tags: application: ${spring.application.name} export: prometheus: enabled: true核心监控项连接数netty_connections_active消息吞吐messages_sent_total处理延迟message_process_latency_ms错误率message_errors_total在项目初期我们就遇到了消息乱序问题后来通过引入序列号ACK机制解决。具体实现是为每条消息添加严格递增的seqId接收方需要按序确认发现缺失时主动请求重传。这个方案虽然增加了少量协议开销但保证了消息的绝对有序性特别适合金融类IM场景。