2 Star 17 Fork 10

IRVING-L / TinyHttpServer

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
mysql.md 9.50 KB
一键复制 编辑 原始数据 按行查看 历史

MySQL数据库的使用

常见的服务器设计,其结构组成一般都是:I/O处理模块、业务逻辑模块、数据模块。

我的另一个项目:chatserver聊天服务器其设计模式也是这样:使用Muduo处理网络I/O,编写业务模块代码、数据代码和业务代码要能够实现分离。

1

这个项目开发的http服务器支持用户的【登陆】和【注册】业务。为了保存用户的登陆名称和密码,采用MySQL数据库进行落地存储。

MySQL使用介绍

这部分大家自行学习吧。简单的使用掌握就行,对数据库的增删改查命令要熟悉。书籍这边推荐《MySQL必知必会》,几天就能学会MySQL的基础使用
看书的同时,还可以配合牛客网的刷题网站,通过刷题加深记忆

环境配置

为了让这个项目成功编译,你需要提前配置好mysql的库。 本项目需要安装mysql-server以及对应的开发包。ubuntu环境安装mysql-server和mysql开发包,包括mysql头文件和动态库文件,命令如下:

sudo apt-get install mysql-server    = 安装最新版MySQL服务器
sudo apt-get install libmysqlclient-dev = 安装开发包

安装完成后需要设置MySQL的登录用户和密码,按下面步骤修改mysql的root用户密码为123456:

step 1tony@tony-virtual-machine:~$ sudo cat /etc/mysql/debian.cnf
[client]
host     = localhost
user     = debian-sys-maint        ==============  初始的用户名
password = Kk3TbShbFNvjvhpM      =============== 初始的密码
socket   = /var/run/mysqld/mysqld.sock
step 2】用上面初始的用户名和密码,登录mysql server,修改root用户的密码,命令如下:
tony@tony-virtual-machine:~$ mysql -u debian-sys-maint -pKk3TbShbFNvjvhpM       
  
命令解释: -u后面是上面查看的用户名 -p后面紧跟上面查看的密码
mysql> update mysql.user set authentication_string=password('123456') where 
user='root' and host='localhost';
mysql> update mysql.user set plugin="mysql_native_password";
mysql> flush privileges;
Query OK, 0 rows affected (0.01 sec)
mysql> exit
Bye

如果用户名和密码无法修改成功,也可以在CSDN上找到对应的解决方法

为了能够正确运行项目中的代码,还需导入项目要用到的表 导入文件在:thirdparty/httpserver.sql
导入的方法请自行查询,也是很简单的

设计方法

对于网页上用户的登陆和注册业务,从项目业务逻辑上分析的话,这个服务器需要以下的功能支持:

  • 支持POST请求。用户名和密码会通过POST请求被传输到服务器上。所以,httpconnect模块要能够解析出POST请求中携带的用户名和密码
  • 解析得到的用户名和密码在MySQL数据库中进行查验。账号密码正确,服务器回报登陆成功页面;如果错误也需要告知客户端。登陆和注册业务在服务器端很好区别,通过解析URL地址能够获知客户端是在登陆页面,还是在注册页面发送过来的POST请求

代码文件功能剖析

与MySQL数据库相关的代码文件总共有三个:
include/db/database.h
include/db/user.h
include/model/usermodel.h
include/sqlpool.h

1. database.h

这个代码文件中写的类的作用是:操作数据库。负责完成对数据库的连接、更新、查询操作。

连接的数据库名称、主机名等信息,我预先定义在了文件起始位置。如果你的数据库设置和我的不一样的话,请按照你的配置进行修改

————————更新线———————— 2022年6月30日16:22:39 因为我在项目中添加了【数据库连接池】这一功能。于是,连接数据库的任务,便不再由此文件中编写的MySQL类承担。该类的功能为输入一个SQL语句,对数据库进行执行对应的增删改查操作。

该类的对外接口如下所示:

    // 获取数据库连接池中的SQL连接
    bool connect();
    // 将sql连接归还给连接池
    bool freeConnection();
    // 更新操作
    bool update(std::string sql);
    // 查询操作
    MYSQL_RES *query(std::string sql);
    // 返回MYSQL指针
    MYSQLPTR getConnection();

user.h

user类是一个ORM类。ORM全称Object Relational Mapping,即对象关系映射。这个类是将我们数据库中的user表的信息映射到类中。对于数据的操作,我们无需再去编写原生sql,取代代之的是基于面向对象的思想去编写类、对象、调用相应的方法等,ORM会将其转换/映射成原生SQL然后交给database.h中的MYSQL类去执行。

我们在MySQL中定义了一个user表,表的信息为: +----------+----------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +----------+----------+------+-----+---------+-------+ | username | char(50) | YES | | NULL | | | password | char(50) | YES | | NULL | | +----------+----------+------+-----+---------+-------+

所以,对于的ORM类 User的接口设置如下:

    void setName(std::string &name) { _name = name; }
    void setPwd(std::string &pwd) { _pwd = pwd; }
    const std::string getName() const { return _name; }
    const std::string getPwd() const { return _pwd; }

如果后续MySQL表的结构有所变动,只需对应着修改User表的成员变量和接口即可。

usermodel.h

这个类才算是真正在数据库中查询user表的一个类的。虽然我们在前面定义了一个负责对数据库进行增删改查的MySQL类,但是对于SQL语句的编写,获取到查询数据的判断等操作,仍旧是不小的代码编写。为了使得整个程序结构清晰,让人一目了然,我定义了UserModel类把对user表的操作(查找和插入)都封装了起来:

定义了两个方法:

// 在数据库中新增一个用户信息
bool insert(User &usr);
// 在数据库中查找用户信息
User query(std::string &name);

insert方法会往user表中插入一个新的用户信息(账号名称和密码)。这个方法是针对注册业务使用的

query方法在user表中查询用户名为name的信息,返回值是一个User对象。如果没有找到用户名,该User对象的成员信息都是空值。

4. 数据库连接池类

// 数据库配置信息
static std::string server = "127.0.0.1";
static std::string user = "root";
static std::string password = "123456";
static std::string dbname = "webserver";

1. 什么是数据库连接池 数据库连接池属于池化技术的一种,是程序使用MySQL连接之前,提前准备好一定数量的MySQL连接,方便线程/进程使用MySQL连接访问后台的数据库。使用完毕后,及时归还给连接池。

2. 为什么使用连接池? 为了更好的提高Web服务器查询后台MySQL数据的性能。

试想一下,在不使用连接池技术的情况下,Web服务器每当需要查询数据库时,都需要以下三个步骤:

  • 建立SQL连接;
  • 执行SQL操作;
  • 释放SQL连接;

如果服务器上有成千上万的用户都需要服务器访问数据库,那么频繁的建立和释放SQL势必会成为服务器性能的瓶颈。

所以,如果能够提前准备好一定数量的SQL连接,服务器要访问数据库时,直接拿起就用,用完就还给连接池。免去了SQL连接的建立和释放过程,这便大大提高了服务器的性能表现。

3. 连接池具体实现 我使用队列作为承载SQL连接的容器。队列具有先入先出的特性,很适合本项目的需求和应用场景。

    // MySQL连接队列
    std::queue<MYSQLPTR> m_sqlQueue;

但是STL库的容器不是线程安全的,为了保证线程并发访问数据的安全性,对连接队列的访问需要上锁。我这里是使用的信号量。信号量不仅能反应资源能否访问,还能表明资源的数量。

消耗资源

//获取连接
MYSQLPTR SqlPool::getConn(){
    // 上锁
    MYSQLPTR conn = nullptr;
    if(!isClose){
        full.wait();
        mutex.wait();

        conn = m_sqlQueue.front();
        m_sqlQueue.pop();

        mutex.post();
        empty.post();
    }
    return conn;
}

生产资源:

// 归还连接
void SqlPool::freeConn(MYSQLPTR connPtr){
    // 上锁
    if(!isClose){
        empty.wait();
        mutex.wait();

        m_sqlQueue.push(connPtr);

        mutex.post();
        full.post();
    }
    else{
        // 外部调用想归还SQL连接,但是连接池已经是处于关闭状态
        // 直接调用api释放连接。但是这个函数还有时间执行吗?这是个问题。
        mysql_close(connPtr);
    }
}

此外,我还使用了设计模式的单例模式(懒汉模式)。单例模式保证了类在整个程序的运行周期,有且仅有一份。因为我们将MySQL连接是保存在了程序的内存空间内(堆区),如果不采用单例模式的话,MySQL的连接池是一份接着一份的被创建出来,当连接数量达到上限后,最后就会导致MySQL数据库无法被应用访问。

在mysql的终端界面中,可以使用如下命令: show processlist;查看当前的连接数量。

连接池设计思路: img

实战使用

具体代码使用,请查看:src/httprequest.cpp:_getPost()

马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
C++
1
https://gitee.com/ljunsang/tiny-http-server.git
git@gitee.com:ljunsang/tiny-http-server.git
ljunsang
tiny-http-server
TinyHttpServer
master

搜索帮助

344bd9b3 5694891 D2dac590 5694891