在最近的学习中,实现了基于muduo网络库的集群聊天服务器,在此做一个剖析以及相关内容的梳理介绍,希望可以帮助到大家。
这一篇,先来简单介绍一下这个项目。
源码地址
项目技术特点
- 使用C++开发并基于 muduo 网络库开发网络核心模块,实现高效通信
- 封装 MySQL 接口,将重要的用户数据储存到磁盘中,实现数据持久化
- 使用第三方 JSON 库实现通信数据的序列化和反序列化
- 使用 Nginx 的 TCP 负载均衡功能,将客户端请求以轮询的方式派发到多个服务器上,提高了并发处理能力
- 基于发布-订阅的服务器中间件redis消息队列,解决客户端之间跨服务器通信难题
- 基于 CMake 构建项目
项目需求
- 客户端新用户注册,用户登录
- 添加好友和添加群组
- 好友聊天以及群组聊天
- 存储以及显示离线消息
- 利用nginx配置tcp负载均衡,提高并发量
- 集群聊天系统支持客户端跨服务器通信
项目开发环境
- Linux环境 CentOS7
- 安装json第三方开发库 Linux下安装JSON的步骤以及JSON是什么
- 安装boost以及muduo网络库开发环境,注意,gcc/g++编译器必须保证在5.4 - 7.8版本之间,并且因为muduo是libmuduo_xx.a静态链接库,链接阶段直接合并到可执行文件中的,如果原先安装编译过muduo库,升级完gcc,需要把muduo重新源码编译安装一下,建议在安装之前就升级g++版本 有关安装步骤请看Linux平台下muduo网络库源码编译安装与测试,包含boost库的安装与测试!!!!
- 安装redis环境 Linux下安装redis并配置开机自启保姆级教程-----附带每一步截图
- 安装mysql数据库 Linux安装MySQL(超详细)
- 安装nginx Linux系统下安装配置nginx—附带每一步截图
- 安装CMake环境Linux下安装CMake的方法
开发环境
vscode在linux环境下直接开发 VSCode - 使用VSCode远程连接到Linux并实现免密码登录
MySQL数据库表格设计
关于表的配置,以user为例Mysql基础—创建数据库以及表,附带截图。
业务模块简介
注册模块
从网络模块接收数据,根据 msgid 定位到注册模块。从传递过来的 json 对象中获取用户 ID 和用户密码。并以此生成 User 对象,调用 model 层方法将新生成的 User 插入到数据库中。
登录模块
从 json 对象中获取用户ID和密码,并在数据库中查询对比获取用户信息是否匹配。如果用户已经登录过或者账号密码错误,返回错误信息。登录成功后更改用户表中用户状态,并显示该用户的好友列表群组列表以及收到的离线消息。
客户端异常退出模块
客户端异常退出了,从服务端记录用户连接的表中找到该用户,如果它断连了就从此表中删除,并设置其状态为 offline。
服务端异常退出模块
服务端异常退出(捕捉到CTRL + C ),将所有在线的客户的状态都设置为 offline。使用了 Linux 的信号处理函数,向信号注册回调函数,然后在函数内将所有用户置为下线状态。
一对一聊天模块
通过传递的 json 查找对话用户 ID:
用户处于同一台服务器并且处于登录状态:直接向该用户发送信息
用户不在同一台服务器,在redis查找,处于登录状态:向该用户发送信息
用户处于离线状态:存储离线消息
添加好友模块
从 json 对象中获取添加登录用户 ID 和其想添加的好友的 ID,调用 model 层代码在 friend 表中插入好友信息。
群组模块
创建群组需要描述群组名称,群组的描述,然后调用 model 层方法在数据库中记录新群组信息。加入群组的用户需要给出用户 ID 和想要加入群组的 ID,并显示显示该用户是群组的普通成员还是创建者。
群组聊天
给出群组 ID 和聊天信息,
群内成员用户处于同一台服务器并且处于登录状态:直接向该用户发送信息
群内成员用户不在同一台服务器,在redis查找,处于登录状态:向该用户发送信息
群内成员用户处于离线状态:存储离线消息
Nginx负载均衡模块
假设一台机器支持两万的并发量,现在我们需要保证八万的并发量。但是单台机器的性能是有限的,即使我们升级服务器的配置,(提高 CPU 执行频率,加大内存等提高机器的物理性能),也无法达到并发量。所以需要增加服务器的数量,将用户请求分发到不同的服务器上分担压力,这就是负载均衡。就需要有一个第三方组件充当负载均衡器,由它负责将不同的请求分发到不同的服务器上。而本项目,我们选择 Nginx 的负载均衡功能。
相关配置,会在Nginx模块中给出。
负载均衡模块需要实现三个功能
- 把client的请求按照轮询算法分发到服务器ChatServer上
- 能够和ChantServer保持心跳机制,及时检测ChatServer故障,确保不会分发到故障机上
- 及时发现新添加的ChatServer设备,方便扩展服务器数量,不需要停止服务器
redis发布-订阅功能解决跨服务器通信问题
使用集群服务器,有多个服务器维护自己的用户列表。ChatServer1与ChatServer2的用户聊天,ChatServer1在自己服务器的用户表中找不到,但是可能用户在线,所以我们需要保证跨服务器间的通信!但是如果让后端的服务器之间互相连接,让各个ChatServer服务器互相之间直接建立TCP连接进行通信,相当于在服务器网络之间进行广播。ChatServer维护了一个连接的用户表,每次向别的用户发消息都会从用户表中查看对端用户是否在线。然后再判断是直接发送,还是转为离线消息。这样的设计使得各个服务器之间耦合度太高,不利于系统扩展,并且会占用系统大量的socket资源,各服务器之间的带宽压力很大,不能够节省资源给更多的客户端提供服务,并且存在一个服务器瘫痪其余都崩溃的情况,不采用。
所以引入中间件消息队列,解耦各个服务器,使整个系统松耦合,提高服务器的响应能力,节省服务器的带宽资源,在集群分布式环境中,经常使用的中间件消息队列有ActiveMQ、RabbitMQ、Kafka等,都是应用场景广泛并且性能很好的消息队列,供集群服务器之间,分布式服务之间进行消息通信。但是本项目业务类型并不是非常复杂,对并发请求量也没有太高的要求,因此我们的中间件消息队列选型的是-基于发布-订阅模式的redis。