从零开始:如何设计一个现代化聊天系统
写在前面: 此博客内容已经同步到我的博客网站,如需要获得更优的阅读体验请前往https://mainjaylai.github.io/Blog/blog/system/chat-system
在当今数字化时代,聊天系统已成为我们日常生活和工作中不可或缺的一部分。从个人交流到团队协作,从客户服务到社交网络,聊天应用程序在各个领域都发挥着重要作用。正因如此,理解并掌握聊天系统的设计原理对于软件工程师来说尤为重要。
该博客将深入探讨聊天系统的设计,涵盖从基础架构到高级功能的各个方面。我们将分析当前市场上流行的聊天应用,研究它们的成功之处,并探讨如何将这些经验应用到我们自己的设计中。
图1呈现了目前市场上最受欢迎的几款聊天应用。这些应用之所以能够脱颖而出,不仅因为它们满足了用户的基本通讯需求,还因为它们在用户体验、功能创新和技术实现上都有独到之处。通过学习这些成功案例,我们可以更好地理解用户需求,把握技术趋势,从而设计出更加优秀的聊天系统。

图 1
在开始设计聊天系统之前,明确系统的具体需求是至关重要的。聊天应用的多样性意味着它们可以服务于广泛的用途和用户群体,因此在设计过程中,我们需要特别注意以下几点:
- 明确目标用户: 是面向普通消费者的即时通讯工具,还是针对企业内部沟通的协作平台?了解目标用户群体有助于确定核心功能和用户界面设计。
- 确定主要使用场景: 是以一对一聊天为主,还是以群组交流为重点?不同的使用场景会影响系统的架构设计和功能优先级。
- 功能需求分析: 除了基本的文字聊天,是否需要支持语音通话、视频会议、文件传输等功能?每增加一项功能都会影响系统的复杂度和资源需求。
- 性能要求: 系统需要支持多少同时在线用户?消息传递的延迟要求是什么?这些因素会直接影响到系统的架构设计和技术选型。
- 安全性和隐私考虑: 是否需要端到端加密?如何保护用户数据?在某些应用场景中,这可能是最关键的需求之一。
- 可扩展性: 系统是否需要能够快速扩展以适应用户增长?这将影响到底层架构的设计。
- 跨平台支持: 是否需要支持多种设备和操作系统?这会影响到客户端的开发策略。
- 集成需求: 是否需要与其他系统或服务集成?例如,与社交媒体平台或企业管理系统的集成。
好的系统设计始于对需求的深入理解。在实际工作中,这个阶段可能需要多次迭代和讨论。通过仔细分析和明确需求,我们可以为接下来的设计过程奠定坚实的基础,最终交付一个既满足用户需求又技术先进的聊天系统。
第一步 - 理解问题并确立设计范围
确定要设计的聊天应用类型至关重要。市场上有各种类型的聊天应用,如专注于一对一聊天的Facebook Messenger、微信和WhatsApp,侧重群聊的办公应用Slack,以及注重大群交互和低延迟语音聊天的游戏聊天应用Discord。
在该博客中,我们专注于设计一个类似Facebook Messenger的聊天应用,重点关注以下功能:
- 低延迟的一对一聊天
- 小型群聊(最多100人)
- 在线状态显示
- 多设备支持。同一账户可以同时登录多个设备。
- 推送通知
确定设计规模也很重要。我们将设计一个支持5000万日活跃用户的系统。这些需求为我们的设计提供了清晰的框架和边界。通过明确这些关键点,我们可以更有针对性地进行系统设计,避免在不必要的功能上浪费时间,同时确保能够满足核心需求和性能指标。
在实际的系统设计中,这个阶段的重要性往往被低估。然而,正是这个阶段的深入讨论和明确定义,为后续的架构设计和技术选型奠定了基础。它不仅有助于我们集中精力于最关键的功能,还能帮助我们预见可能遇到的挑战和瓶颈。例如,知道系统需要支持5000万日活跃用户,我们就可以开始考虑如何设计一个高度可扩展的架构。了解到需要支持多设备登录,我们就需要考虑如何同步不同设备间的消息和状态。而"永久存储聊天历史"这一需求则提醒我们需要设计一个高效的数据存储和检索系统。
通过这个过程,我们不仅明确了设计目标,还初步勾勒出了系统的轮廓。这为接下来的详细设计和技术讨论提供了明确的方向,使我们能够更有效地进行后续的设计工作。
第二步 - 提出高层设计并获得认可
要开发高质量的设计,我们应该对客户端和服务器如何通信有基本的了解。在聊天系统中,客户端可以是移动应用或网页应用。客户端之间不直接通信,而是每个客户端连接到一个支持上述所有功能的聊天服务。让我们专注于基本操作。聊天服务必须支持以下功能:
- 接收来自其他客户端的消息。
- 为每条消息找到正确的接收者并将消息转发给接收者。
- 如果接收者不在线,则在服务器上保留该接收者的消息,直到他们上线。
图2展示了客户端(发送者和接收者)与聊天服务之间的关系。

图 2
当客户端打算开始聊天时,它使用一个或多个网络协议连接聊天服务。对于聊天服务来说,网络协议的选择很重要。
对于大多数客户端/服务器应用程序,请求是由客户端发起的。这对聊天应用的发送方也是如此。在图2中,当发送者通过聊天服务向接收者发送消息时,它使用经过时间考验的HTTP协议,这是最常见的网络协议。在这种情况下,客户端与聊天服务建立HTTP连接并发送消息,通知服务将消息发送给接收者。这里使用keep-alive很有效,因为keep-alive头允许客户端与聊天服务保持持久连接,同时也减少了TCP握手的次数。HTTP在发送方是一个不错的选择,许多流行的聊天应用,如Facebook,最初就使用HTTP发送消息。
然而,接收方的情况稍微复杂一些。由于HTTP是客户端发起的,从服务器发送消息并不简单。多年来,许多技术被用来模拟服务器发起的连接:轮询(polling)、长轮询(long polling)和WebSocket。这些都是系统设计面试中广泛使用的重要技术,让我们逐一研究。
轮询(Polling)
如图3所示,轮询是一种客户端定期询问服务器是否有可用消息的技术。根据轮询频率的不同,轮询可能会消耗大量资源。它可能会消耗宝贵的服务器资源来回答一个大多数时候答案都是"否"的问题。

图 3
- 工作原理:
- 客户端以固定的时间间隔向服务器发送HTTP请求。
- 服务器立即响应,无论是否有新的消息可用。
- 如果有新消息,服务器会在响应中包含这些消息。
- 如果没有新消息,服务器会发送一个空响应。
- 优点:
- 实现简单:客户端和服务器端的逻辑都相对直接。
- 兼容性好:几乎所有的浏览器和服务器都支持这种方式。
- 防火墙友好:使用标准的HTTP请求,不会被防火墙阻挡。
- 缺点:
- 资源浪费:即使没有新消息,也会频繁发送请求,浪费带宽和服务器资源。
- 实时性差:消息的接收存在延迟,取决于轮询间隔。
- 服务器负载高:当有大量客户端同时轮询时,可能会给服务器带来巨大压力。
- 优化策略:
- 自适应轮询间隔:根据消息频率动态调整轮询间隔。
- 批量获取消息:每次轮询获取多条消息,减少请求次数。
- 结合其他技术:例如,可以用轮询来检查是否有新消息,有的话再建立更高效的连接获取消息内容。
在设计聊天系统时,轮询可能会作为一种降级策略或备用方案使用,例如当WebSocket连接失败时。但对于主要的消息传递机制,我们通常会选择更高效的方法,如WebSocket或长轮询。
长轮询(Long Polling)
由于轮询可能效率低下,下一个进展是长轮询(如图4所示)。

图 4
在长轮询中,客户端保持连接打开,直到实际有新消息可用或达到超时阈值。一旦客户端接收到新消息,它立即向服务器发送另一个请求,重新启动这个过程。长轮询有几个缺点:
- 发送者和接收者可能不会连接到同一个聊天服务器。基于HTTP的服务器通常是无状态的。如果您使用轮询(Round Robin)进行负载均衡,接收消息的服务器可能与接收消息的客户端没有长轮询连接。
- 服务器没有好的方法来判断客户端是否已断开连接。
- 它是低效的。如果用户不经常聊天,长轮询仍会在超时后定期建立连接。
- 工作原理:
- 客户端向服务器发送HTTP请求。
- 如果服务器没有可用的新数据,不会立即发送响应,而是保持请求打开。
- 当新数据可用时,服务器立即响应该请求。
- 客户端收到响应后,立即发送新的请求,重新开始这个
相关文章:
从零开始:如何设计一个现代化聊天系统
写在前面: 此博客内容已经同步到我的博客网站,如需要获得更优的阅读体验请前往https://mainjaylai.github.io/Blog/blog/system/chat-system 在当今数字化时代,聊天系统已成为我们日常生活和工作中不可或缺的一部分。从个人交流到团队协作,从客户服务到社交网络,聊天应用…...
香橙派OrangePi AIpro初体验:当小白拿到一块开发板第一时间会做什么?
文章目录 香橙派OrangePi AIpro初体验:当小白拿到一块高性能AI开发板第一时间会做什么前言一、香橙派OrangePi AIpro概述1.简介2.引脚图开箱图片 二、使用体验1.基础操作2.软件工具分析 三、香橙派OrangePi AIpro.测试Demo1.测试Demo1:录音和播音(USB接口…...
【C语言内存函数】
目录 1.memcpy 使用 模拟实现 2.memmove 使用 模拟实现 3.memset 使用 4.memcmp 使用 1.memcpy 使用 void * memcpy ( void * destination, const void * source, size_t num );目的地址 源地址 字节数 destination:指向要复制内…...
Mysql部署MHA高可用
部署前准备: mysql-8.0.27下载地址:https://cdn.mysql.com//Downloads/MySQL-8.0/mysql-8.0.27-1.el7.x86_64.rpm-bundle.tar mha-manager下载地址:https://github.com/yoshinorim/mha4mysql-manager/releases/download/v0.58/mha4mysql-mana…...
【算法学习】射线法判断点在多边形内外(C#)以及确定内外两点连线与边界的交点
1.前言: 在GIS开发中,经常会遇到确定一个坐标点是否在一块区域的内部这一问题。 如果这个问题不是一个单纯的数学问题,例如:在判断DEM、二维图像像素点、3D点云点等含有自身特征信息的这些点是否在一个区域范围内部的时候&#x…...
SQL语句(DML)
DML英文全称是Data Manipulation Language(数据操作语言),用来对数据库中表的数据记录进行增删改等操作 DML-添加数据 insert into employee(id, workno, name, gender, age, idcard) values (1,1,Itcast,男,10,123456789012345678);select *…...
uniapp小程序打开地图导航
uniapp uni.getLocation({type: gcj02, //返回可以用于uni.openLocation的经纬度success: function (res) {const latitude res.latitude;const longitude res.longitude;uni.openLocation({latitude: latitude,longitude: longitude,success: function () {console.log(suc…...
webstorm格式化或保存时 vue3引入的组件被删除了
解决办法 保存时设置 格式化设置...
Java时间转换
一、线程不安全 Date date new Date(); SimpleDateFormat dateFormat new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String prefix dateFormat.format(date);二、线程安全,建议使用 String t1 LocalDateTime.now().format(DateTimeFormatter.ofPattern("y…...
Spring Boot与WebFlux的实战案例
Spring Boot与WebFlux的实战案例 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!今天,我们将探讨如何利用Spring Boot和WebFlux构建响应式应用的实战…...
vue3引入本地静态资源图片
一、单张图片引入 import imgXX from /assets/images/xx.png二、多张图片引入 说明:import.meta.url 是一个 ESM 的原生功能,会暴露当前模块的 URL。将它与原生的 URL 构造器 组合使用 注意:填写自己项目图片存放的路径 /** vite的特殊性…...
git 禁止dev合并到任何其他分支
创建 pre-merge-commit 钩子 导航到 Git 仓库的钩子目录: cd /path/to/your/repo/.git/hooks创建或编辑 pre-merge-commit 钩子: 也可以通过指令创建 nano pre-merge-commit在钩子文件中添加以下代码: #!/bin/sh# 获取当前分支名称 curr…...
第二节:如何使用thymeleaf渲染html(自学Spring boot 3.x的第一天)
大家好,我是网创有方,今天来学习如何使用thymeleaf渲染html。该模板运用不广泛,所以本节内容了解既可。 第一步:创建html文件。 在模板templates目录下创建一个html文件。 编写代码如下: <!DOCTYPE html> <…...
计算机相关术语科普之什么叫网关(Gateway)
网关(Gateway)是一个在计算机网络中起到关键作用的设备或系统,它扮演着网络间连接器或协议转换器的角色。 一、定义与功能 1)定义: 网关是在不同网络之间实现互连的复杂设备,仅用于两个高层协议不同的网…...
B站网页部分API
https://www.bilibili.com/ 数据结构 mid: 用户id name: 用户名 face: 用户头像url noface.jpg为默认头像 sign: 签名level: b站等级 coins: b站硬币粉丝 https://api.bilibili.com/x/relation/fans?vmid{mid}&pn{pn}&ps{limit}&orderdesc&…...
使用Spring Boot和Spring Security保护你的应用
使用Spring Boot和Spring Security保护你的应用 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!今天我们将深入探讨如何利用Spring Boot和Spring Security来保护…...
CVE-2019-12272 Openwrt可视页面LuCi命令注入漏洞复现(完结)
声明 本文所使用的一些源代码等内容已经上传至github,具体地址如下 Vulnerability_POC-EXP/OpenWrt/CVE-2019-12272 at main a2148001284/Vulnerability_POC-EXP GitHub 漏洞简介 参考内容: CVE-2019-12272 OpenWrt图形化管理界面LuCI命令注入分析 |…...
【多线程开发 4】从源码学习LockSupport
从源码学习LockSupport 2024年6月30日 大家好啊,好久没写博客了,今天打算写一下,讲一下JUC里面LockSupport这个类。 这个是一个工具类,实际上也是为了线程通信开发的。它的源码比较短,也只引用了Unsafe一个类。所以…...
gameui C++的代码
gameui C的代码 #include <graphics.h> #include "gameboard.h" const int WIDTH 560; const int HEIGHT 780; const int GRID_SIZE 120; class GameUi { private: public:GameUi(GameBoard& gb) {// 初始化图形窗口initgraph(WIDTH, HEIGHT);// 设置…...
1.什么是js?特点是什么?组成部分?
Js是一种直译式脚本语言,一种动态类型,弱类型,基于原型的高级语言。 直译式:js程序运行过程中直接编译成机器语言。 脚本语言:在程序运行过程中逐行进行解释说明,不需要预编译。 动态类型:js…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...
Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...
2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面
代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http…...
Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...
Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
