千家信息网

如何设计QQ好友系统

发表于:2026-11-04 作者:千家信息网编辑
千家信息网最后更新 2026年11月04日,本文以用QQ好友系统为例,为大家分析QQ好友系统设计的原理以及设计步骤。阅读完整文相信大家对设计QQ好友系统有了一定的认识。什么是好友系统?简单的说,好友系统是维护用户好友关系的系统。我们最熟悉的好友
千家信息网最后更新 2026年11月04日如何设计QQ好友系统

本文以用QQ好友系统为例,为大家分析QQ好友系统设计的原理以及设计步骤。阅读完整文相信大家对设计QQ好友系统有了一定的认识。

什么是好友系统?

简单的说,好友系统是维护用户好友关系的系统。我们最熟悉的好友系统案例当属QQ,实际上QQ是一款即时通讯工具,凭着好友系统沉淀了海量的好友关系链,从而铸就了一个坚不可摧的商业帝国。好友系统的重要性可见一斑。

熟悉互联网产品的人都知道,当产品有了一定的用户量,往往会开发一个好友系统。其主要目的是增加用户粘性(有了好友就会常来)或者增加社区活跃度(有了好友就会多交流)。

而我的后台开发生涯就是从这样一个系统开始的。

那时候,好友系统对于我们团队大部分人来说,都是一个全新的事物,因为我们大部分人都是应届生。整个系统的架构自然不是我们一群黄毛小孩所能创造。当年的架构图已经找不到了,但是凭着一点记忆和多年来的经验积累,还是可以把当年的架构勾勒出来。

如图,好友系统的架构是常见的3层结构,包括接入层、逻辑层和数据层。

我们先从数据层讲起。

因为我们对QQ太熟悉了,我们可以很容易地列出好友系统的数据主要包括用户资料、好友关系链、消息(聊天消息和系统消息)、在线状态等。

互联网产品往往要面对海量的请求并发,传统的关系型数据库比较难满足读写需求。在存储中,一般是读多写少的数据才会使用MySQL等关系型数据库,而且往往还需要增加缓存来保证性能;NoSQL(Not Only SQL)应该是目前的主流。

对于好友系统,用户资料和好友关系链都使用了kv存储,而消息使用公司自研的tlist(可以用redis的list替代),在线状态下面再介绍。

接着是逻辑层。

在这个系统中复杂度最高的应该是消息服务(而这个服务我并没有参与开发[捂脸])。

消息服务中,消息按类型分为聊天消息和系统消息(系统消息包括加好友消息、全局tips推送等),按状态分为在线消息和离线消息。在实现中,维护3种list:聊天消息、系统消息和离线消息。聊天消息是两个用户共享的,系统消息和离线消息每个用户独占。当用户在线时,聊天消息和系统消息是直接发送的;如果用户离线,就把消息往离线消息list存入一份,等用户再次登录时拉取。

这样看来,消息服务并不复杂?其实不然,系统设计中常规的流程设计往往是比较简单的,但是对于互联网产品,异常情况才是常态,当把各种异常情况都考虑进来时,系统就会非常复杂。

这个例子中,消息发送丢包是一种异常情况,怎么保证在丢包情况下,还能正常运行就是一个不小的问题。

常见的解决方法是收包方回复确认包,发送方如果没收到确认包就重发。但是确认包又可能丢包,那又可以给确认包增加一个确认包,这是一个永无止境的确认。

解决方法可以参考TCP的重传机制。那问题来了,我们为什么不用TCP呢?因为TCP还是比较慢的,聊天消息的可靠性没有交易数据要求那么高,丢几条消息并不会造成严重后果,但是如果用户每次发送消息后都要等很久才能被收到,那体验是很差的。

一个比较折中的方案是,收包方回复确认包,如果发送方在一定时间内没有收到确认就重发;如果收包方收到两个相同的包(自定义seq一样),去重即可。

一个面试题引发的讨论:

面试时我常常会问候选人一个问题:在分布式系统中怎样实现一个用户同时只能有一个终端在线(用户在两个地方先后登录账号,后一次登录可以把前一次登录踢下线)?这是互联网产品中非常基础的一个功能,考察的是候选人基本的架构设计能力。

设计要先从接入服务器(下称接口机)说起。接口机是好友系统对外的窗口,主要功能是维护用户连接、登录鉴权、加解密数据和向后端服务透传数据等。用户连接好友系统,首先是连接到接口机,鉴权成功后,接口机会在内存中维护用户session,后续的操作都是基于session进行。

如图所示,用户如果尝试登录两次,接口机通过session就可以将第一次的登录踢下线,从而保证只有一个终端在线。

问题解决了吗?

没有。因为实际系统肯定不会只有一台接口机,在多台接口的情况下,上面的方法就不可行了。因为每个接口机只能维护部分用户的session,所以如果用户先后连接到不同的接口机,就会造成用户多处登录的问题。

自然可以想到,解决的方法就是要维护一个用户状态的全局视图。在我们的好友系统中,称为在线状态服务。

在线状态服务,顾名思义就是维护用户的在线状态(登录时间、接口机IP等)的服务。用户登录和退出会通过接口机触发这里的状态变更。因为登录包和退出包都可能丢包,所以心跳包也用作在线状态维护(收到一次心跳标记为在线,收不到n次心跳标记为离线)。

一种常用的方法是,采用bitmap存储在线状态,具体是指在内存中分配一块空间,32位机器上的自然数一共有4294967296个,如果用一个bit来表示一个用户ID(例如QQ号),1代表在线,0代表离线,那么把全部自然数存储在内存只要4294967296 / (8 1024 1024) = 512MB(8bit = 1Byte)。当然,实现中也可以根据需要给每个用户分配更多的bit。

于是,踢下线功能如图所示。

用户登录的时候,接口机首先查找本机上是否有session,如果有则更新session,接着给在线状态服务发送登录包,在线状态服务检查用户是否已经在线,如果在线则更新状态信息,并向上次登录的接口机IP发送踢下线包;接口机在收到踢下线包时会检查包中的用户ID是否存在session,如果存在则给客户端发送踢下线包并删除session。

在实际中,踢下线功能还有很多细节问题需要注意。

又回到用户先后登录同一台接口机的情况:


图中踢下线流程是正确的,但是如果步骤10和13调换了顺序(在UDP传输中是常见的)会发生什么?大家可以自己推演一下,后到的踢下线包会把第二次登录的A'踢下线了。这不是我们期望的。怎么办呢?

解决方法分几个细节,①接口机在收到13号登录成功包时,先将session A替换成session A',然后给客户端A发生踢下线包(避免多处存活导致互相踢下线);②踢下线包中必须包含除用户ID外的其他标识信息,session的唯一标识应该是ID+XXX的形式(我最开始采用的是ID+LoginTime),XXX是为了区分某次的登录;③接口机在收到踢下线包的时候只要判断ID+XXX是否吻合来决定是否给客户端发踢下线包。

现实情况,问题总是千奇百怪的,好在办法总比问题多。

比如我在项目中遇到过接口机和在线状态服务时间漂移(差几秒)的情况。这样踢下线的唯一标识就不能是用户ID+LoginTime的形式了。可以为每次的登录生成一个唯一的UUID解决。类似的问题还有很多,不再赘述。

总结一下,本篇主要介绍了好友系统的整体架构和部分模块的实现方式。分布式系统中各个模块的实现其实并不难,难点主要在于应对复杂网络环境带来的问题(如丢包、时延等)和服务器异常带来的问题(如为了应对服务器宕机会增加服务器冗余度,进而又会引发其它问题)。

看完这篇文章,你们学会设计QQ好友系统了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注行业资讯频道,感谢各位的阅读。

系统 用户 消息 好友 在线 下线 口机 服务 登录 状态 问题 数据 设计 情况 方法 架构 产品 复杂 互联网 功能 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 深圳市人人互联网科技有限公司 涨乐互联网科技 圆梦互联网科技 互联网品科技 广西互联网科技公司 兼蜜互联网科技 北京大学互联网科技怎么样 恒银科技与互联网金融 互联网科技实习生岗位职责 金融科技与互联网金融相同和不同 互联网科技与管理 广州市恰铎互联网科技有限公司 互联网公司高科技产品 云南省自由互联网科技有限公司 领航科技大厦互联网公司 互联网科技快讯热点 互联网科技年终奖 广州梦谷互联网科技有限公司领导 唐山双旺合互联网科技产业园 互联网 科技节活动 微视频大赛 恒生科技互联网指数分析 互联网校园科技培训 宝灵互联网科技有限公司 花神(南京)互联网科技有限公司 互联网新科技动漫ppt素材 长沙茂晟互联网科技有限公司 互联网科技实习生岗位 神州科技互联网有限公司 银川新华互联网科技学校原画 杭州科技互联网公司有哪些
0