需求
- 玩家发送消息后,需要广播给当前频道所有在线玩家。并且玩家可以查看。聊天历史记录,记录的上限可以配置。
- 需要支持扩展频道。
- 支持禁言功能,禁言后玩家的发言清除,其他玩家也不可看到。
- 敏感词检测功能,敏感词库需要支持定制化需求。
- 特殊表情的支持
需求分析
- 玩家发送信息到自己的lobby服务后,如何同步消息给其他人。消息是否需要在一个专门的地方存储,方便其他人查询聊天记录。如何估算消息存储的容量上限。
- 敏感词采用什么模式检测,需要提供检测到敏感词后的定制化操作,例如使用*替换敏感词,或者拒绝发送。
- 全词匹配:只能在词语独立出现时匹配,如果作为某个词语的子串,那么就不会匹配。
- 包含匹配:只要敏感词作为子字符串出现即命中。例如,敏感词为"赌博",文本中出现"网上赌博平台"也会被识别。
- 强过滤匹配:检测文本中汉字、字母、数字、特殊字符等组合后是否包含敏感词,能应对更灵活的字符替换或干扰,如"赌$博"、"赌 博"等变体
- 表情数据如何在消息中表达。
- 如何管理不同频道的消息,拓展新的频道需要简单,尽量避免实现复杂,后续维护人员不方便接入。
自己的实现方案
-
由于聊天是一个独立服务,且要支持不同服务器玩家之间聊天,那么最好单独开一个服务管理聊天数据。里面有一个聊天管理器,他需要支持存储不同频道聊天数据,该服务最好支持动态扩展,避免单个服务压力过大导致服务异常。
-
特殊字符或者表情包可跟客户端约定某种特殊结构的字符串,或者用二进制数据表示。
-
敏感词需要支持不同策略的组合使用。字符串匹配有关的算法应该是Trie树。这一块我不熟悉,需要深入学习。
-
禁言功能需要支持客服后台就能执行,并且输入的参数越简单越好,还能够知道执行结果。禁言后需要清除聊天数据,并且广播给其他在线玩家。
方案值得细化的点
一、架构设计:从"一个服务"到"一组服务"
您提到"单独开一个服务"和"动态扩展"非常正确。下一步是细化它:
集群与无状态:"聊天管理器"本身应设计为无状态的多个实例,前面通过负载均衡(如Nginx, Kubernetes Service)分发连接。真正的状态(频道消息、在线列表)需要下沉到共享存储(如Redis集群)或通过一致性哈希在节点间分布。
连接与业务分离:考虑将网关/连接层与业务逻辑层分离。网关专门管理海量TCP/WebSocket连接、心跳、拆包粘包;业务服务专注处理消息逻辑。这更利于水平扩展。
服务发现:当服务可以动态扩展时,新的"聊天管理器"实例如何被网关或客户端知晓?需要引入服务发现组件(如Consul, Nacos, etcd)。
二、数据流与协议:明确"消息如何流动"
您提到了数据表示,还需定义清晰的数据流:
通信协议:选择应用层协议。WebSocket(全双工,实时)是首选,gRPC(基于HTTP/2,高效)也是好选择。需定义一套自己的消息信封(Envelope)协议,包含消息类型、发送者、目标频道、序列号、消息体等。
消息体结构:您提到的"特殊结构字符串或二进制"是可行的。业界常用Protocol Buffers或FlatBuffers来序列化结构化消息体(包含文本、表情类型、资源ID、位置等),高效且跨平台。
完整的发送-广播流程:
上行:客户端 -> 网关 -> 业务服务 -> (1.敏感词过滤 -> 2.持久化存储 -> 3.查询在线玩家 -> 4.下发)
下行:业务服务 -> 网关 -> 目标客户端群。
其中步骤1和3是性能关键。
三、核心功能实现的深化
敏感词系统:
优化:使用双数组Trie(DAT)或AC自动机进行多模式匹配,效率极高。词库需要热更新,通常将编译好的Trie结构放在内存,并通过管理后台触发更新。
策略组合:您想得很好。需设计一个过滤器链,例如:先"强过滤"(替换为***),再"全词匹配"(记录日志),不同频道可以应用不同链。
禁言功能:
"清除聊天数据"需明确:通常指阻止未来消息的发送和广播,而非物理删除历史记录。历史记录一般保留用于审计。
状态存储与同步:禁言状态(用户ID,截止时间)应存储在集中式缓存(如Redis)并设置过期时间。业务服务在处理每条消息前,先查缓存校验。
广播通知:被禁言/解禁时,系统消息需要可靠地广播给相关频道。这里涉及系统消息的类型设计。
方案遗漏的点
消息的可靠性与一致性:
-
消息必达:如何保证消息不丢?尤其是服务重启时。需引入消息ID、客户端ACK、服务端重传机制(如QQ的滑动窗口)。
-
时序与乱序:如何保证同一频道消息的全局顺序?需要生成全局递增的消息序列号(如使用Redis原子操作或Snowflake算法)。
海量消息与存储:
-
冷热分离:最近7天的聊天记录(热数据)放Redis或MongoDB方便快速拉取;更早的(冷数据)归档到对象存储(如S3)或廉价文件系统。
-
分片策略:历史记录表必须按频道ID或时间进行分库分表。
非功能性需求(NFRS):
-
性能:单机支持多少连接?多少条并发消息?压测目标是多少?
-
监控:需要监控哪些指标?(在线人数、消息吞吐、敏感词触发率、服务节点负载)。如何收集日志并追踪一条消息的全链路?(分布式追踪,如OpenTelemetry)
-
安全:防止刷屏(频率限制)、防止超大消息、消息内容加密(TLS必须)、防重放攻击。