C++项目:基于muduo网络库的聊天系统

项目描述

        这是一个基于C/S架构实现的聊天系统,可以进行 用户注册,用户登录,用户聊天,群组聊天,添加好友,创建群组,加入群组,用户退出等功能。通过数据模块 业务模块 网络模块对整体的项目框架进行解耦,数据通信格式为Json格式。服务器使用集群式服务,负载均衡器使用Nginx tcp长连接负载均衡算法,服务器之间使用基于发布-订阅模式的redis消息中间件进行通信。

项目需求

1. 客户端新用户注册
2. 客户端用户登录
3. 添加好友和添加群组
4. 好友聊天
5. 群组聊天
6. 离线消息
7. nginx配置tcp负载均衡
8. 集群聊天系统支持客户端跨服务器通信

开发环境

操作系统:Centos7.5

数据库:MySQL

编译器:g++

Json:JSON for Modern C++

源码地址:chatserver: 基于muduo网络库的服务器集群聊天项目

模块划分

网络模块

服务端使用muduo网络库

muduo的网络设计

是one loop per thread,有一个main reactor负载accept连接,然后把连接分发到某个sub
reactor(采用round-robin的方式来选择sub reactor),该连接的所用操作都在那个sub reactor所处的线程中完成。多个连接可能被分派到多个线程中,以充分利用CPU。

reactor模型
1. 事件驱动(event handling)
2. 可以处理一个或多个输入源(one or more inputs)
3. 通过Service Handler同步的将输入事件(Event)采用多路复用分发给相应的Request
Handler(多个)处理

业务模块 


   

 1.登录业务 msgid:id:passwd

通过接收到的id在数据库中查询是否有id  
      没有 直接退出
     若有 连同pwd返回  是否是在线

        在线 报错退出    

        不在线进行密码匹配 匹配成功ok
                     推送离线消息 好友列表 群组列表
                     将数据库状态设置为在线
                     向redis中订阅channal
            

2.注册业务 msgid:name:passwd 

将name passwd直接进行用户表的插入 通过返回的id将id号返回

3.注销业务 msgid

在连接map中删除,redis取消订阅  在user表状态设置为离线

4.聊天  msgid:id:name:toid:message:time

在连接map中查找toid-conn 有 进行消息转发
没有 在数据库中查询toid状态 离线进行离线消息转发
在线 向redis消息队列中发布消息

5.添加好友 msgid:id:friendid

在数据库中查找friendid,没有退出
有,将id--friendid与 friendid---id 写入数据库中

6.创建群组  msgid:id:groupname:groupdesc

在allgroup数据库中插入groupname-groupdesc字段,并且将allgroup表中生成的groupid返回
在groupuser表中插入id--groupid--'creator' 
创建群组成功后,该用户就是该群组的创建者

7.加入群组 msgid:id:groupid

在groupuser表中插入id--groupid--'normal'就可以了

实际上加入群组这只是一个业务,对于实际操作中肯定是先进行查询群组操作,然后加入群组

8.群组聊天 msgid:id:groupid:msg

先使用groupid在groupuser中查出除了id外所有在groupid中的用户
使用vector<int>保存用户id
然后通过_idConnMap中查询本地在线用户是否有id 有就转发消息
没有然后去查询user表中的用户state是否在线,
      在线那就向redis消息队列中发布userid--js 让订阅userid的主机会去获取订阅消息,
      不在线就会进行离线消息存储

9.从redis消息队列中获取订阅的消息

订阅userid的主机会去获取订阅消息,判断此时用户是否在线,
    在线其进行转发,不在线就进行离线消息存储

数据模块

所有的数据表

 用户表 

id--用户账号,自动生成的,唯一主键

name---用户名,名字唯一

password----用户密码

state----用户状态:在线 离线

 好友表

userid---本人账号

friendid-----好友账号

 所有群组表

id--群组号,自动生成,唯一主键

groupname---群组名,不允许重复

groupdesc---群组描述

 群组与用户表

记录加入群组的用户及该群组的表

一个群可以有多个用户,一个用户也可以有多个群,这是一个多对多的关系

groupid---群组号

userid---用户号

grouprole---用户角色

 离线消息表

         每条数据对应的是某个用户的一条离线消息

userid----用户名

message---离线消息

 

服务器集群

单台服务器受限于硬件资源,其性能是有上限的,当单台服务器不能满足应用场景的并发需求量时,就需要考虑部署多个服务器共同处理客户端的并发请求,但是客户端怎么知道去连接具体哪台服务器呢?
此时就需要一台负载均衡器,通过预设的负载算法,指导客户端连接服务器,这里我是用了nginx tcp长连接负载均衡算法(主要能力有限,实现一个负载均衡器目前来说有点难度),这里就是对nginx的配置文件进行配置,客户端只需要连接到nginx均衡器上,通过nginx负载均衡算法给客户端分配服务

 

nginx.conf文件,配置tcp长连接负载均衡

跨服务器通信

由于不同客户端的连接可能在不同的主机上,这就会牵扯到一个问题,在不同服务器上的用户之间如何进行通信呢?也就是说服务器与服务器之间如何进行通信呢?

1.服务器与服务器之间两两在通过tcp连接通信?

上面的设计,让各个ChatServer服务器互相之间直接建立TCP连接进行通信,相当于在服务器网络之间 进行广播。这样的设计使得各个服务器之间耦合度太高,不利于系统扩展,并且会占用系统大量的 socket资源,各服务器之间的带宽压力很大,不能够节省资源给更多的客户端提供服务,因此绝对不是 一个好的设计
集群部署的服务器之间进行通信,最好的方式就是引入中间件消息队列,解耦各个服务器,使整个系统 松耦合,提高服务器的响应能力,节省服务器的带宽资源,如下图所示:

这里引入了消息中间件,基于发布-订阅模式的redis消息队列

项目中的问题

1.添加好友业务

添加完好友后,对方却没有显示我为对方的好友

原因:在进行数据库插入操作时只进行了id--friendid,没有插入friendid---id操作,这应该是双向的

2.离线消息存储

当一个用户有多条离线消息时,登陆成功后只显示一条消息

原因:在设计offlinemessage数据表时对id字段设置为了主键,导致表中不能有重复id,这样使得每个用户只能有一条离线数据。解决也很简单将id字段的主键属性去掉,使得id可以重复。

3.向消息中间件PUBLISH发布消息不成功,导致无法跨服务器通信

原因:对于redis客户端进行publish或者 subscribe 时,一个客户端只能进行某一种操作。因此对于业务层应当使用两个redis对象,一个用于publish,一个用于subscribe

4.多次向中间件SUBSCRIBE订阅消息,出现无响应,客户端也无响应

原因:记C++集群服务器项目消息中间件编程大家遇到的两个Bug_大秦坑王的专栏-CSDN博客

项目缺陷及扩展

业务方面

由于这个项目业务比较简单,中间有很多设计会不符合我们日常聊天的场景和业务,这里就不一一赘述,其实业务方面没有什么,要扩展也很容易,根据需求来自己定制很多业务。

服务器设计方面

1.由于所有的连接都会经过nginx负载均衡,因此对于nginx会有很大的IO负载压力。

2.所有服务器都会提供整套的服务,这对于服务器的性能来说得不到提升。

3.对于请求数据库连接方面,频繁的去数据库申请连接断开连接这也是一种性能的损耗

因此:对于服务器设计方面可以采用分布式集群的方式进行设计
        多台服务器组合而成的一台超级性能的服务器,这些服务器形成一个小集合,部署一整套对外的服务。set模型弥补了单机能力的不足,对业务组合搭配成一个单元。本质上是对服务的一个高内聚的封装。

 对内不同主机执行一种不同的业务,这些主机构成一个超级服务器提供一整套服务,对外通过一台主机对这个超级服务器的抽象作为接口

建立多个这样的服务集群,这些服务集群是通过一个代理服务器 根据哈希算法和最小负载进行分发业务。但是这样也会造成代理服务器的IO负载压力过大,因此可以将这样的压力分配给客户端。

建立一个注册信息服务单元,这里只会注册每个服务器集群的IP及负载压力,客户端主动拉取这些信息,然后根据负载均衡算法以及最小负载算法去进行连接服务器。

参考

腾讯课堂 施磊   基于muduo实现集群服务器项目

基于muduo网络库的集群聊天系统(C++实现)_0 error(s)-CSDN博客

  • 4
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Linux多线程服务端编程是指使用Muduo C网络在Linux操作系统中进行多线程的服务端编程。Muduo C网络是一个基于事件驱动的网络,采用了Reactor模式,并且在底层使用了epoll来实现高效的I/O复用。 使用Muduo C网络进行多线程服务端编程有以下几个步骤: 1. 引入Muduo C网络:首先需要下载并引入Muduo C网络的源代码,然后在编写代码时包含相应的头文件。 2. 创建并初始化EventLoop:首先需要创建一个EventLoop对象,它用于接收和分发事件。通过初始化函数进行初始化,并在主线程中调用它的loop()函数来运行事件循环。 3. 创建TcpServer:然后创建一个TcpServer对象,它负责监听客户端的连接,并管理多个TcpConnection对象。通过设置回调函数,可以在特定事件发生时处理相应的逻辑。 4. 创建多个EventLoopThread:为了提高并发性能,可以创建多个EventLoopThread对象,每个对象负责一个EventLoop,从而实现多线程处理客户端的连接和请求。 5. 处理事件:在回调函数中处理特定事件,例如有新的连接到来时会调用onConnection()函数,可以在该函数中进行一些初始化操作。当有数据到来时会调用onMessage()函数,可以在该函数中处理接收和发送数据的逻辑。 6. 运行服务端:在主线程中调用TcpServer的start()函数来运行服务端,等待客户端的连接和请求。 总的来说,使用Muduo C网络进行Linux多线程服务端编程可以更好地利用多核处理器的性能优势。每个线程负责处理特定事件,通过事件驱动模式实现高效的网络编程。这样可以提高服务器的并发能力,提高系统的整体性能。 ### 回答2: Linux多线程服务端编程是指在Linux平台上使用多线程的方式来编写网络服务器程序。而使用muduo C网络是一种常见的方法,它提供了高效的网络编程接口,可以简化多线程服务器的开发过程。 muduo C网络基于Reactor模式,利用多线程实现了高并发的网络通信。在使用muduo C进行多线程服务端编程时,我们可以按照以下步骤进行: 1. 引入muduo:首先需要导入muduo C网络的头文件,并链接对应的文件,以供程序调用。 2. 创建线程池:利用muduo C中的ThreadPool类创建一个线程池,用于管理和调度处理网络请求的多个线程。 3. 创建TcpServer对象:使用muduo C中的TcpServer类创建一个服务器对象,监听指定的端口,并设置好Acceptor、TcpConnectionCallback等相关回调函数。 4. 定义业务逻辑:根据具体的业务需求,编写处理网络请求的业务逻辑代码,如接收客户端的请求、处理请求、发送响应等。 5. 注册业务逻辑函数:将定义好的业务逻辑函数注册到TcpServer对象中,以便在处理网络请求时调用。 6. 启动服务器:调用TcpServer对象的start函数,启动服务器,开始监听端口并接收客户端请求。 7. 处理网络请求:当有客户端连接到服务器时,muduo C会自动分配一个线程去处理该连接,执行注册的业务逻辑函数来处理网络请求。 8. 释放资源:在程序结束时,需要调用相应的函数来释放使用的资源,如关闭服务器、销毁线程池等。 通过使用muduo C网络,我们可以简化多线程服务端编程的过程,提高服务器的并发处理能力。因为muduo C网络已经实现了底层的网络通信细节,我们只需要专注于编写业务逻辑代码,从而减少开发的工作量。同时,muduo C的多线程模型可以有效地提高服务器的并发性能,满足高并发网络服务的需求。 ### 回答3: Linux多线程服务端编程是指在Linux操作系统上开发多线程的服务器应用程序。使用muduo C网络有助于简化开发过程,提供高效的网络通信能力。 muduo C网络是一个基于Reactor模式的网络,适用于C++语言,由Douglas Schmidt的ACE网络演化而来。它提供了高度并发的网络编程能力,封装了许多底层细节,使得开发者能够更加专注于业务逻辑的实现。 在开发过程中,首先需要创建一个muduo C的EventLoop对象来管理事件循环。然后,可以利用TcpServer类来创建服务器并监听指定的端口。当有新的客户端请求到达时,muduo C会自动调用用户定义的回调函数处理请求。 在处理请求时,可以使用muduo C提供的ThreadPool来创建多个工作线程。这些工作线程将负责处理具体的业务逻辑。通过将工作任务分配给不同的线程,可以充分利用多核服务器的计算资源,提高服务器的处理能力。 在具体的业务逻辑中,可以使用muduo C提供的Buffer类来处理网络数据。Buffer类提供了高效的数据读写操作,可以方便地进行数据解析与封装。 此外,muduo C还提供了TimerQueue类来处理定时任务,可以用于实现定时事件的调度与管理。这对于一些需要定期执行的任务非常有用,如心跳检测、定时备份等。 总之,使用muduo C网络可以简化Linux多线程服务端编程的开发过程,提供高效的并发能力。通过合理地利用多线程和其他的相关组件,可以实现高性能、稳定可靠的网络服务端应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值