集群聊天服务器各个类进行详解
1.dh.h
类定义概要
类名: MySQL
功能: 简化MySQL的连接、查询和更新操作,提供接口给上层应用使用。
成员变量
private:MYSQL *_conn;
_conn
:指向MYSQL
结构体的指针,用于代表数据库连接实例。由mysql_init()
初始化,之后通过mysql_real_connect
建立连接。
构造与析构函数
MySQL()
- 作用: 初始化数据库连接对象,调用
mysql_init(nullptr)
。 - 功能: 为连接准备一个空的
MYSQL
实例。
~MySQL()
- 作用: 释放数据库连接资源。
- 调用:
mysql_close(_conn)
,关闭连接,释放资源。
成员函数(公共接口)
bool connect()
- 用途: 建立实际的数据库连接。
- 实现细节:
- 调用
mysql_real_connect
,连接到配置信息中的数据库(server
、user
、password
、dbname
、端口3306
)。 - 连接成功后,设置字符集为
gbk
(mysql_query(_conn, "set names gbk")
)。 - 连接成功则输出日志“connect mysql success!”。
- 连接失败则输出详细的错误信息,包含错误原因和连接参数。
- 调用
- 返回值:
true
:连接成功false
:连接失败
bool update(string sql)
- 用途: 执行SQL更新操作(INSERT、UPDATE、DELETE等)。
- 实现细节:
- 调用
mysql_query(_conn, sql.c_str())
执行SQL。 - 若失败,输出错误,返回
false
。 - 成功返回
true
。
- 调用
- 示例: 插入新用户或修改数据。
MYSQL_RES *query(string sql)
- 用途: 执行SQL查询语句(SELECT),返回结果集。
- 实现细节:
- 调用
mysql_query(_conn, sql.c_str())
执行查询。 - 若失败,输出错误信息,返回
nullptr
。 - 若成功,调用
mysql_use_result(_conn)
获取结果集。
- 调用
- 返回值:
MYSQL_RES*
:结果集指针,调用者需负责释放。
MYSQL* getConnection()
- 用途: 提供底层连接指针,便于更多的低级操作或调试。
- 返回:
_conn
指针。
其他细节
连接信息存储:
server
(127.0.0.1
)user
(root
)password
(Sf523416&111
)dbname
(chat
)
这些信息是静态变量(
static string
),方便在整个程序中使用。日志输出:
- 使用
muduo/base/Logging.h
提供的日志宏LOG_INFO
和LOG_ERROR
,方便调试和监控。
- 使用
总结
这个 MySQL
类封装了:
- 连接管理: 内部维护一个
MYSQL*
连接句柄。 - 连接方法:
connect()
,连接数据库。 - 数据操作:
update()
执行写操作(增删改)。query()
执行读操作,返回结果集指针。
- 资源管理: 在析构函数中释放连接。
它为上层应用提供了简洁、封装的数据库访问接口,并加入了错误信息输出和日志记录,非常适合在实际的C++服务器项目中使用。
2.FriendModel
类
让我们一起来详细分析这个FriendModel
类,它主要负责维护和处理好友关系相关的操作。这个类封装了操作好友信息的两个核心功能:添加好友关系和查询好友列表。
类定义概述
#ifndef FRIENDMODEL_H
#define FRIENDMODEL_H#include "user.hpp"
#include <vector>
using namespace std;class FriendModel
{
public:// 添加好友关系void insert(int userid, int friendid);// 返回用户好友列表vector<User> query(int userid);
};#endif
成员方法概述
1. void insert(int userid, int friendid)
- 功能: 在好友关系表中插入一条新的好友关系。
2. vector<User> query(int userid)
- 功能: 查询某个用户的所有好友,返回一个
User
对象的向量(好友列表)。
方法详细解读
1. 添加好友关系:void insert(int userid, int friendid)
实现逻辑:
- 构造SQL语句,格式为:
insert into friend values(userid, friendid)
- 创建
MySQL
对象,并连接数据库。 - 如果连接成功,执行
update()
方法插入好友关系。
说明:
这个操作假设
friend
表有两列(例如:userid
和friendid
),代表一对好友关系。
2. 查询好友列表:vector<User> query(int userid)
实现逻辑:
构造SQL语句,使用
内部连接(inner join)
,查询用户的好友信息:select a.id, a.name, a.state from user a inner join friend b on b.friendid = a.id where b.userid = userid
这条语句的作用:
- 从
user
表中选出所有该用户的好友信息。 - 通过
friend
关系表关联用户ID。
- 从
创建一个
vector<User>
存放好友信息。建立数据库连接,执行查询:
若成功,遍历
MYSQL_RES
结果集:利用
mysql_fetch_row()
读取每一行数据。创建一个
User
对象,设置其字段(id
、name
、state
)。将
User
加入结果向量中。
释放
MYSQL_RES
资源。
返回好友列表
vector<User>
。
类成员变量
// 目前该类没有显式成员变量,只有成员方法
备注:
- 这个类设计中没有持久的成员变量,每次操作时都新建
MySQL
对象以连接数据库。这是一种简单实现,适合关系不频繁变动的场景。
其他细节
数据库操作:
- 插入关系时调用
mysql.update(sql)
,没有事务控制,简单插入。 - 查询好友信息时调用
mysql.query(sql)
,处理结果集。
- 插入关系时调用
安全性:
- 使用
sprintf
拼接SQL语句,可能存在SQL注入风险;实际应用中建议用参数化查询。
- 使用
性能:
- 每次操作都新建数据库连接,不复用连接,可能影响效率。
总结
这个FriendModel
类提供了两个主要功能:
添加好友关系:
insert()
- 将两用户的好友关系存入数据库。
查询好友列表:
query()
- 根据用户ID,查找所有好友信息,返回
User
对象的数组(好友列表)。
- 根据用户ID,查找所有好友信息,返回
该设计结构简洁,直接,但在性能优化和安全性方面可以进一步增强(如连接池、参数化查询等)。
同时,它依赖于数据库表结构(friend
表和user
表)合理设计。
3.类比FriendModel
类,还有usermodel, groupmodel,OfflinMegModel
这是数据操作模型层,负责在业务逻辑和数据存储之间传递信息。它知道如何把业务需求(如添加好友)转化为数据库操作,也懂得如何把数据库查询结果转化为业务可用的对象。
4.group.hpp,groupuser.hpp,user.hpp
让我们详细分析这三个类(User
、GroupUser
、Group
)的设计与作用,它们都是典型的 ORM(对象关系映射)类,用于映射数据库中的表结构到 C++ 对象中,方便开发中对数据的操作。
1. User
类(用户基础表的ORM类)
class User
{
public:User(int id = -1, string name = "", string pwd = "", string state = "offline"){this->id = id;this->name = name;this->password = pwd;this->state = state;}void setId(int id) { this->id = id; }void setName(string name) { this->name = name; }void setPwd(string pwd) { this->password = pwd; }void setState(string state) { this->state = state; }int getId() { return this->id; }string getName() { return this->name; }string getPwd() { return this->password; }string getState() { return this->state; }protected:int id;string name;string password;string state;
};
作用与设计:
- 表示“用户”实体,将用户表中的字段(
id
,name
,password
,state
)映射为类成员。 - 提供了构造函数、getter、setter方法,方便操作。
- 典型的ORM类,用于在程序中封装用户信息。
2. GroupUser
类(群组用户,扩展了User
,添加角色信息)
复制代码
class GroupUser : public User
{
public:void setRole(string role) { this->role = role; }string getRole() { return this->role; }private:string role; // 用户在群组中的角色(如管理员、普通成员)
};
作用与设计:
- 继承自
User
,意味着“群组内的用户”具有用户的所有基本属性(如ID、用户名、密码、状态)。 - 增加
role
属性,表示用户在群组中的角色。 - 设计理念:一个群组中的用户不仅仅算作普通用户,还需区分角色,因此在群组关系中,添加了角色字段。
总结:
GroupUser
是一个“关系类”,表示某个用户在某个群组中的身份信息(不仅仅是用户,还包括角色属性)。- 这样设计方便在多对多关系中存储角色信息(如用户属于哪个群,角色怎样)。
3. Group
类(群组类)
class Group
{
public:Group(int id = -1, string name = "", string desc = ""){this->id = id;this->name = name;this->desc = desc;}void setId(int id) { this->id = id; }void setName(string name) { this->name = name; }void setDesc(string desc) { this->desc = desc; }int getId() { return this->id; }string getName() { return this->name; }string getDesc() { return this->desc; }vector<GroupUser> &getUsers() { return this->users; }private:int id; // 群组IDstring name; // 群组名称string desc; // 群组描述vector<GroupUser> users; // 包含的成员列表(每个成员带角色信息)
};
作用与设计:
- 表示“群组”实体,映射群组表。
- 含有ID、名称、描述字段。
users
成员变量用来存储群组的所有成员(GroupUser
对象),实现多对多关系(一个群组有多个用户,一用户可能在多个群组)。- 提供了基本的getter/setter方法。
关系与整体架构理解
User
:代表整个系统的用户信息,映射用户表。GroupUser
:继承用户信息,并附加角色信息,代表某用户在特定群组中的角色(多对多关系中的中间表/关系实体)。Group
:代表群组实体,含其成员(GroupUser
的集合)信息。
这套设计体现了面向对象方式对数据库关系的封装:
- 通过
Group
类管理群组信息和成员列表; - 通过
GroupUser
类描述用户和角色之间的关系; - 通过
User
类描述用户基础信息。
总结:
- 这三类共同组成了一个与“群组聊天”功能相关的对象模型架构。
- 设计充分利用继承(
GroupUser
继承User
)来复用代码,增强扩展性。 - 使得程序能够以对象的方式操作数据库中的用户、群组与成员关系。
5.Redis
类
这份redis.hpp
和redis.cpp
实现了一个封装了Redis发布订阅(Pub/Sub)功能的类,方便在C++项目中使用Redis进行消息通信。下面我会详细介绍这个类中的成员变量和成员函数,帮助你理解它的设计思想和使用方法。
Redis
类成员和方法详解
1. 成员变量
复制代码
private:redisContext *_publish_context; // 负责发布消息的上下文redisContext *_subcribe_context; // 负责订阅消息的上下文function<void(int, string)> _notify_message_handler; // 消息接收时的回调函数
_publish_context
:用来连接和操作Redis的发布(PUBLISH
)命令,用于向频道发送消息。_subcribe_context
:用来连接和操作Redis的订阅(SUBSCRIBE
)命令,用于监听和接收频道的消息。_notify_message_handler
:业务层定义的回调函数,用于通知业务层收到的消息。
2. 构造函数和析构函数
复制代码
Redis(); // 构造函数,初始化成员指针为空
~Redis(); // 析构函数,释放连接
- 构造函数:初始化指针为
nullptr
。 - 析构函数:如果指针不为空,调用
redisFree()
释放资源,避免内存泄漏。
3. 核心功能成员方法
connect()
复制代码
bool connect();
- 连接本地Redis服务器(默认地址为
127.0.0.1:6379
)。 - 分别创建两个连接:一个用于发布,一个用于订阅。
- 连接成功后,启动一个独立线程调用
observer_channel_message()
,不断监听订阅通道中的消息,异步通知业务层。
publish()
复制代码
bool publish(int channel, string message);
- 向指定频道发布消息。
- 使用
redisCommand()
执行PUBLISH
命令。 - 如果成功,返回
true
,否则返回false
。
subscribe()
复制代码
bool subscribe(int channel);
- 订阅指定频道。
- 使用
redisAppendCommand()
和redisBufferWrite()
向Redis服务器发出SUBSCRIBE
命令。 - 注意:订阅操作是非阻塞的,只是发出订阅命令,接收消息由后台线程监听。
unsubscribe()
复制代码
bool unsubscribe(int channel);
- 取消订阅某个频道。
- 使用类似
subscribe()
的方式发出UNSUBSCRIBE
命令。
observer_channel_message()
复制代码
void observer_channel_message();
- 运行在后台线程中,持续调用
redisGetReply()
接收Redis服务器推送的消息。 - 解析收到的消息数组(
reply->element
):reply->element[2]->str
:消息内容reply->element[1]->str
:频道编号
- 调用
_notify_message_handler()
通知业务层收到新消息。
init_notify_handler()
复制代码
void init_notify_handler(function<void(int, string)> fn);
- 初始化消息通知的回调函数。
- 用户在业务层定义处理逻辑后,调用此函数注册。
其他细节说明
- 连接管理:在
connect()
中创建两个redisContext
,方便分别处理发布和订阅的连接,避免竞争和阻塞。 - 多线程:
- 订阅消息的监听在
observer_channel_message()
中运行在单独的线程,保证异步处理。
- 订阅消息的监听在
- 内存管理:
- 使用
freeReplyObject()
释放redisReply
资源。
- 使用
- 命令执行:
- 发布:
redisCommand()
- 订阅/取消订阅:
redisAppendCommand()
+redisBufferWrite()
,避免阻塞调用。
- 发布:
- 错误处理:
- 连接失败和命令失败都输出错误信息,返回
false
。
- 连接失败和命令失败都输出错误信息,返回
总结:
这个Redis
类封装了Redis的Pub/Sub基本操作:
- 连接:
connect()
- 发布消息:
publish()
- 订阅频道:
subscribe()
- 取消订阅:
unsubscribe()
- 异步监听消息:在后台线程中自动调用
observer_channel_message()
,收到消息后通过回调通知业务逻辑。 - 回调机制:用户可以注册自己的消息处理函数。
它实现了异步订阅模型,能很好地应用在类似聊天服务器、实时通知等场景,非常实用。
6.ChatServer类
你的代码基于知名的 Muduo 网络库,实现了一个高效、易维护的聊天服务器主控类。下面按照结构–成员变量–成员方法详细分析:
一、ChatServer 类的结构作用
ChatServer 封装了实际的网络服务器逻辑,负责:
- 初始化服务器、设置参数
- 监听和管理客户端连接
- 管理消息收发和业务处理
- 调度事件循环(事件驱动)
二、成员变量讲解
private:TcpServer _server; // 组合muduo库,实现服务器功能的类对象EventLoop *_loop; // 指向事件循环对象(相当于主线程的事件分发器)
1. TcpServer _server
- Muduo 中封装的 TCP 服务器核心类。
- 能自动监听端口,负责底层 epoll、多线程 I/O、连接管理等“苦力活”。
- ChatServer 通过它实现与客户端的所有网络交互。
2. EventLoop *_loop
- 事件循环指针。
- 实际上是主 Reactor 的事件分发器,用于响应 I/O 事件。
三、成员方法详解
构造函数:ChatServer(EventLoop*, const InetAddress&, const string&)
ChatServer(EventLoop *loop, const InetAddress &listenAddr, const string &nameArg): _server(loop, listenAddr, nameArg), _loop(loop)
{// 注册链接回调_server.setConnectionCallback(std::bind(&ChatServer::onConnection, this, _1));// 注册消息回调_server.setMessageCallback(std::bind(&ChatServer::onMessage, this, _1, _2, _3));// 设置线程数量_server.setThreadNum(4);
}
作用总结与细节分析:
- 初始化
_server
,告诉 muduo 用哪台主机 IP/端口监听(listenAddr
)、取什么名字(nameArg
)、用哪个 event loop。 setConnectionCallback()
:绑定"有新连接/断开"时执行onConnection
方法。setMessageCallback()
:绑定"收到消息"时执行onMessage
方法。setThreadNum(4)
:用 4 个工作线程处理 I/O,提升吞吐量。
void start()
void ChatServer::start()
{_server.start();
}
- 真正地启动服务器(让 Muduo 开始监听并接收客户端连接)。
void onConnection(const TcpConnectionPtr&)
void ChatServer::onConnection(const TcpConnectionPtr &conn)
{// 客户端断开链接if (!conn->connected()){ChatService::instance()->clientCloseException(conn);conn->shutdown();}
}
核心作用:
- 这是连接状态变化时的回调(新连接或断开)。
- 如果连接断开,则做以下处理:
- 通知业务服务层进行账号、数据等“下线”处理(
clientCloseException
)。 - 主动关闭 socket(
conn->shutdown()
)。
- 通知业务服务层进行账号、数据等“下线”处理(
void onMessage(const TcpConnectionPtr&, Buffer *, Timestamp)
void ChatServer::onMessage(const TcpConnectionPtr &conn, Buffer *buffer, Timestamp time)
{string buf = buffer->retrieveAllAsString();try {json js = json::parse(buf);// 检查必须字段if (!js.contains("msgid")) {throw json::exception("Missing 'msgid' field");}auto msgHandler = ChatService::instance()->getHandler(js["msgid"].get<int>());msgHandler(conn, js, time);}catch (const json::exception& e) {LOG_ERROR << "JSON parse error: " << e.what();if (conn->connected()) {json response;response["msgid"] = INVALID_MSG_ACK;response["errno"] = 400;response["errmsg"] = "Invalid JSON format: " + string(e.what());conn->send(response.dump());}}catch (const exception& e) {LOG_ERROR << "Message handling error: " << e.what();}
}
详细流程:
- 拿到完整缓冲区数据(收到的一次消息)。
- 尝试解析为 JSON 对象。
- 检查是否有"msgid"字段(决定了本消息应该怎么处理)。
- 调用 ChatService::instance()->getHandler(msgid) 找到对应业务处理函数,然后把消息传给它执行。
- 如果解析失败/消息结构有误,则:
- 打日志报错
- 给客户端返回错误提示
作用通俗理解:
“服务器收到客户发来的消息,把这消息当成快递包裹拆开看——快递单上写着包裹类型(msgid),然后根据类型分给对应员工(handler)处理。拆快递出错就写个反馈单还回去。”
四、补充说明
成员方法之间的关系
- start 用于启动服务器。
- onConnection/onMessage 并不是主动调用,而是事件驱动,在有连接/消息时被 Muduo 框架自动调用。
- 事件响应机制和reactor思想一致。
和 ChatService 的协作
ChatServer 只管网络部分,具体业务如何处理交由 ChatService,“专人专职”,大大降低耦合。
五、总结
ChatServer 核心成员&方法概览:
名称 | 简要作用 |
---|---|
_server | Muduo 的 TCP 服务器,负责监听和通信。 |
_loop | 名字叫event loop,事件派发中心。 |
start() | 启动服务器。 |
onConnection() | 检测连接/断开,并在断开时交给业务进一步下线处理。 |
onMessage() | 收到客户端数据时解析json,根据业务类型分发给具体handler处理;出问题自动回错。 |
构造函数 | 初始化服务器参数并注册各回调,每一步都让ChatServer做最擅长的事情。 |
这样,不仅代码结构清晰,而且便于扩展和维护,是高性能网络服务推荐的模式!
7.chatservice类
会把这个ChatService
类中的所有成员变量和方法,逐个详细讲解,确保你理解每个部分的作用、设计思想以及它们的工作流程,而且会尽力用通俗易懂的语言,让你像听故事一样理解这个“聊天服务”系统。
一、类的整体架构和角色定位
ChatService
是一个“聊天服务器”的核心业务类(管理者)。它采用单例模式(确保整个程序中只存在唯一的实例),负责处理所有的“用户登录、注册、点对点聊天、群聊、好友关系、群组操作”等业务逻辑。
二、成员变量(这个类内部存的“存储空间”和“工具箱”)
1. 单例实例
static ChatService *instance();
- 作用:提供调用的入口,用于获取或创建唯一的
ChatService
实例(即单例设计模式)。 - 通俗理解:就像超市只有一个“管理员”实例,这个方法帮你拿到它。
2. 消息处理映射表
unordered_map<int, MsgHandler> _msgHandlerMap;
- 作用:把不同“消息ID”映射到对应“处理函数”。
- 比如:
登录
对应login()
函数,注册
对应reg()
函数。 - 通俗理解:就像菜单点菜表,点“牛排”就知道要做牛排一样。
3. 在线用户连接表
unordered_map<int, TcpConnectionPtr> _userConnMap;
- 作用:存放已登录、在线用户的“用户ID”和“对应的TCP连接指针”。
- 用途:当需要向某用户发消息时,先找这个表看用户是否在线,在线了就可以直接推送消息。
- 通俗理解:像“电话簿”,存着每个谁在线、用哪个电话(连接)。
4. 连接互斥锁
mutex _connMutex;
- 作用:在多线程环境下,保证对
_userConnMap
的操作(增删改查)是安全的。 - 为何需要:因为可能有多个连接同时操作这个表,比如一个用户刚登录、登出,或服务器在同时处理多个请求。
5. 数据模型对象(操作数据库)
复制代码
UserModel _userModel;
OfflineMsgModel _offlineMsgModel;
FriendModel _friendModel;
GroupModel _groupModel;
- 作用:封装了数据库操作的封装类(操作用户信息,离线消息,好友关系,群组信息)。
- 例子:
_userModel
:查询或更新用户信息。_offlineMsgModel
:存储或读取离线消息。_friendModel
:管理好友关系(添加好友等)。_groupModel
:管理群聊信息。
6. Redis操作对象
复制代码
Redis _redis;
- 作用:封装了与Redis(一个高速的数据库和消息队列系统)的交互,用于实现“跨进程”、“多服务器”的消息推送和订阅。
三、主要方法(业务逻辑执行的“技能”)
1. 获取单例实例
复制代码
static ChatService *instance();
- 作用:返回全局唯一的
ChatService
对象,确保程序中只有一个“聊天服务”。 - 实现:用“静态局部变量”,保证只创建一次。
2. 构造函数(注册消息-handler)
复制代码
ChatService::ChatService()
- 作用:
- 初始化各种消息ID和对应的请求处理函数的映射关系。
- 链接(连接)Redis,为后续消息订阅做准备。
注册消息响应(通过bind
绑定对应成员函数)
login
注册到LOGIN_MSG
register
注册到REG_MSG
oneChat
注册到ONE_CHAT_MSG
还有好友添加、创建群、加入群、群聊的处理方法。
通俗理解:像打电话时拨通不同号码调用不同小姐姐的服务。
3. 业务重置(重要)
复制代码
void reset();
- 作用:
- 在服务器异常或重启时调用。
- 将所有“已登录的用户”状态重置为“离线”状态(确保状态同步真实情况)。
- 调用场景:系统崩溃后,恢复正常工作状态。
4. 获取特定“消息类型”对应的处理函数
复制代码
MsgHandler getHandler(int msgid);
- 作用:
- 输入
msgid
,返回对应的处理函数(函数指针或lambda)。 - 如果找不到,就返回一个“错误处理器”。
- 输入
- 设计亮点:
- 动态路由:前端发的不同消息会被路由到不同的处理函数去。
5. 各类具体业务的处理方法(详细讲解)
login()登陆流程
- 输入:用户ID、密码。
- 逻辑:-查询用户信息,验证密码。-用户已在线,提示重复登录,否则:-存用户名和连接关系到
_userConnMap
,方便推送。-在Redis订阅频道(方便跨服务通知)。-更新用户状态为在线。-返回用户信息(包括离线消息、好友列表、群组信息)。 - 作用:实现实际“登陆”功能。
reg()注册
- 输入:用户名、密码。
- 逻辑:-校验格式、长度。-存入数据库。-返回成功或失败。
loginout()退出/注销
- 作用:-从活跃连接中删除用户。-在Redis取消订阅某渠道(让其他服务器知道该用户已离线)。-更新数据库用户状态。
clientCloseException()
- 作用:
- 处理客户端异常断开连接(比如突然关闭窗口,没有正常退出)。
- 类似
loginout
,清除连接,更新状态。
oneChat()点对点聊天
- 输入:接收方
toid
,消息内容。 - 机制:-第一步:找
_userConnMap
,看看对方是否在线(直接推)。-第二步:如不在线,则存离线消息。-第三步:如果对方在线但不在自己服务器(假设多节点),就用Redis发布消息,其他节点会收信息。
addFriend()添加好友
- 输入:自己ID和好友ID。
- 操作:存入好友关系表。
createGroup()
- 输入:发起人ID、群组名、描述。
- 操作:-在数据库中创建新群。-将发起人加入该群(权限设为“creator”)。
addGroup()加入群组
- 输入:用户ID、群ID。
- 操作:将用户加入群组。
groupChat()群聊
- 输入:群ID、群成员消息。
- 作用:
- 中转消息:
- 先找成员在不在线(在自己连接map里)。
- 在线了推送。
- 不在线存离线消息(存到数据库中)。
- 中转消息:
6. 处理Redis订阅消息
复制代码
void handleRedisSubscribeMessage(int, string);
- 作用:
- 其他服务器发布同步消息后,调用此函数。
- 找到对应的用户连接,如果在,推送消息。
四、重点总结(通俗版)
ChatService
就像一个“指挥官”,协调所有聊天相关的操作。- 存放“用户和连接关系”,方便主动“发消息给用户”。
- 有事注册“事件处理器”:不同的ID对应不同操作(登录、注册、聊天、群聊)。
- 利用Redis实现多服务消息同步。
- 每个业务都是封装好的函数:登录、注册、点对点聊天等。
- 整体设计高扩展性、线程安全、结构清晰。