云计算百科
云计算领域专业知识百科平台

集群聊天服务器——逻辑梳理

网络聊天服务器项目,该项目分为4个模块:

  • 首先是网络模块:我使用了muduo高性能网络库,解耦合网络与业务之间这两部分代码,可以更加专注与业务的功能开发
  • 其次是服务层模块:我使用了基于C++11的技术比如绑定器和map,绑定器实现了用户发送的消息id类型回调业务功能函数的机制(当网络i/o发送消息请求后,通过解析json消息获取消息id 回调业务功能函数)
  • 然后是数据存储模块:我使用了MySQL数据库存储关键的信息(用户账户、离线消息列表、群组信息、好友列表等)由客户端的功能请求进行增删改查
  • 以上是单机服务器的模块设计,但是单机服务器的并发数量是有限的,所以我采用了集群服务器经行并发能力扩展

        4.最后在多台服务器部署,基于Tcp 协议搭建的C/S 通信,所以我使用了Nginx Tcp负载均衡,保持长连接状态,并且引入Redis 的发布订阅功能实现跨服务器间通信

    1. 客户端:

    1.1. 两个模块:

    1.1.1. 负责用户登录、注册、退出功能

    作为客户端连接服务器成功后的首界面展示:提供以上三种服务

    客户端设计模式:主线线程专注于信息的发送、子线程负责接受信息

    将具体功能的所需参数进行json(key-value键值对存储文本形式)序列化发送给服务器

    个别服务需要服务器返回方法返回值状态信息,(登录成功、信息注册成功)?多线程协调实现

    由主线程发送后,阻塞获取信号量,子线程此时接受服务器返回的json对象,由json中的服务方法枚举id,分类细化具体的功能实现业务逻辑,将服务方法调用的状态写入最后将信号量释放,主函数则可以通过子线程写入的状态进行下一步工作

    登陆成功后进入功能菜单:

    1.1.2. 负责聊天服务器业务功能:添加好友信息、创建群组、加入群组、单人聊天、群组聊天

    将功能函数处理为map表形式<存储函数方法名称,方法绑定器>,循环处理客户端的输入的方法请求,在循环中未使用switch_case而是map+bind回调,适应于程序开发中的开闭原则,对修改关闭,对开发扩展开放;

    该部分函数主要处理用户的功能以及输入的参数,封装为json 对象(服务器协商的通信规则:方法枚举id的标识,与方法请求一致的参数列表)发送给服务器

    auto it = commandHandlerMap.find(command);
    if (it == commandHandlerMap.end())
    //error

    // 调用相应命令的事件处理回调,mainMenu对修改封闭,添加新功能不需要修改该函数
    it->second(clientfd, commandbuf.substr(idx + 1, commandbuf.size() – idx));
    // 调用命令处理方法

     

     

    2. 服务器:

    2.1. 服务器需要处理信息转发、功能性数据的存储、查询、更新服务

    解决基于数据库的功能服务问题:MySQL

    解决高并发场景下数据收发问题:muduo 静态网络库

    解决跨服务器通信问题 :Redis发布订阅问题

    解决负载均衡问题:Nginx_基于Tcp长连接的负载均衡问题

    2.2. 主要基于以上内容将服务器划分出:

    server、service、db、model、redis、public 六个模块

    2.2.1. server——muduo网络:

    基于epoll+多线程的机制实现高并发需求

    在该模块中设置四个工作线程,其中一个处理连接回调,在连接异常的场景下关闭连接,余下三个线程为工作线程,完成将接收的字符基于json-parse反序列化发送给service,基于服务方法id,回调具体的处理函数

    2.2.2. db:

    数据库的接口API ,创建初始化、数据库连接、

    数据更新、数据查询query

    // 初始化数据库连接
    _conn = mysql_init(nullptr);

    // 释放数据库连接资源
    if (_conn != nullptr)
    mysql_close(_conn);

    // 连接数据库
    MYSQL *p = mysql_real_connect(_conn, server.c_str(), user.c_str(),
    password.c_str(), dbname.c_str(), 3306, nullptr, 0);

    // 更新操作
    mysql_query(_conn, sql.c_str())

    // 查询操作
    mysql_query(_conn, sql.c_str())
    return mysql_use_result(_conn);

    2.2.3. model:

    该模块用来实现用户需求与服务器数据库交互增、删、改、查问题

    以举例:

    // 添加好友关系,在好友数据表中添加一条记录
    sprintf(sql, "insert into friend values(%d, %d)", userid, friendid);

    // 返回用户好友列表,联合查询用户表和好友表,将结果插入vector并用返回
    sprintf(sql, "select a.id,a.name,a.state from user a /
    inner join friend b on b.friendid = a.id where b.userid=%d", userid);

    // 删除用户的离线消息,删除离线列表的一条信息
    sprintf(sql, "delete from offlinemessage where userid=%d", userid);

    // 更新用户的状态信息
    sprintf(sql, "update user set state = '%s' where id = %d",/
    user.getState().c_str(), user.getId());

    2.2.4. redis:

    基于Redis发布订阅问题,解决跨服务器通信问题:

    初始化发布上下文、订阅上下文,进行连接、向指定chennel 发布、订阅,结束订阅、断开连接

    2.2.5. service:

    设置方法map集合<方法id,方法handler绑定器>

    设置用户连接map集合<用户id,连接TcpConnectionPtr接口>,将所有方法的枚举值,函数方法绑定器插入map表,连接Redis服务器、MySQL 数据库

    基于具体的方法选择,结合底层存储的用户数据(查询、插入、删除)完成登录、注册、注销用户;

    结合model模块下的函数具体结合用户需求完善功能函数;

    聊天服务:查询用户的连接信息,在本服务器时转发聊天信息,不在本台服务器,检查数据库的用户登陆状态,未登录存储离线信息表,登录则发布到Redis的发布队列

    登录问题:(最复杂)

    从json对象解析得到登录id、密码

    在用户信息表中查询该id的所属对象并比对密码,不一致将封装json返回原因;

    一致时检查登陆状态避免重复登陆,继而更新用户的登陆状态;

    在用户登录map中插入其连接信息;

    订阅Redis对该id 为chennel的消息;

    打印该用户的离线消息;

    将该对象的好友信息封装为vector存储的json序列化文本信息,作为返回的json-response对象key-value 存储的对象之一;群组信息如上;

    最后返回最后的json-response序列化内容。

    2.2.6. public:

    保存服务方法的枚举信息,将每一服务名称与id一一对应

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 集群聊天服务器——逻辑梳理
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!