TinyHTTPServer源码解析

TinyHTTPServer源码分析

目录树:

源码目录源码地址:

  1. 日志功能 log/
  2. 数据库连接池sql/
  3. 线程池 include/threadpool.h
  4. 定时器 timer/:
  5. 封装的基本的线程同步机制:locker/
  6. http协议解析: http/
  7. 运行选项配置 config/
  8. 头文件 include/
  9. 资源文件 root/

提供8个配置选项:

  • -p 指定监听端口
  • -l 0同步写入日志, 1异步写入日志
  • -o 1:开启socket linger延迟关闭连接 0:关闭
  • -s 8 数据库连接池数量
  • -t 线程池内线程的数量
  • -c 0:开启日志 1:关闭日志
  • -a IO模型 1:reactor 0:proactor

构建

项目依赖:

  1. Mysql C api apt:sudo apt install libmysqlclient
  2. pthread

修改项目main.cc中的登陆mysql的用户名和密码,以及库名

表结构

1
2
3
4
5
6
7
8
9
10
11
12
13
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`username` char(50) DEFAULT NULL,
`passwd` char(50) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

SET FOREIGN_KEY_CHECKS = 1;

编译:(重写了makefile,0 warning)下载后进入项目根目录执行make

执行: ./server -p 8080

清除:make clean

如果有错误注意当前文件夹的权限问题,gdb看看可能是创建日志文件权限不够


大概工作流程:

Config类解析配置,个个模块类最后都整合进WebServer类,方便进程运行时调用

server整体的工作流程: config解析命令行选项->启动日志->初始化sql连接池->初始化线程池->监听->处理http请求->返回

模块功能解析

/locker

将信号量sem_t封装成类sem,互斥量pthread_mutex_t封装成locker,配合条件变量pthread_cond_t ->cond使用,并将post wait lock unlock wait signal 等基本方法封装到类中

/log

  1. 同步:

    直接将产生的日志信息通过C的标准IO写入的日志文件

  2. 异步:

    用数组封装一个先进先出的队列,然后将产生的每一条日志信息写入到队列,开启一个线程来不断读取队列中的每一条信息

/config

​ 使用getopt()函数来解析命令行选项

include/threadpool.h

​ 用到了locker(线程同步)和sql(某页面使用了注册和登陆功能需要查询数据库),

​ 线程池由 请求队列+多个线程 组成

主线程接受到读写事件发生的通知后:

reactor模式: 将所有的工作都交给工作的线程来处理: IO + http解析(业务逻辑)

​ 过程: 调整定时器在lst的位置和加入到线程池的任务队列

proactor模式: 在主线程中处理IO(read/write):然后将工作(http解析,业务)交给线程的处理

​ 过程:主线程在处理完read(注意write不需要加入到线程池的任务队列中,因为在read完成后加入到任务队列已经完成了http的解析,所以返回给客户端就行),交给线程来处理http解析,然后修改监听事件类型,写返回给客户端

​ 多个线程会根据信号量的值来竞争处理任务队列的任务(http class也封装了IO操作)

​ worker函数声明为static就不会有隐含的this指针作为参数,和void*参数不匹配

/sql

​ init()中调用mysql_real_connect()获取*MYSQL**连接,生成多个可用的sql连接

/timer

​ client_data存client的IP+port以及util_timer的指针,util_timer也指向对应连接的client_data,所有的连接维护在sort_timer_lst链表中,(根据expire超时时间)

​ 通过sockpair和信号处理函数将信号转化为IO事件,

​ cb_func回调函数用来清除超时的连接

/http

​ 修改了原项目的http_conn::write(),(原来的发送好像有点问题)

​ 解析流程看看有限状态自动机,

1

2

​ http包的组成:请求行+请求头部字段+空行+body

​ 整个http的解析流程封装在函数process()中;


webbench测试数据

  • 只放在本地的虚拟机里面测试了,不准确

    CPU: AMD Ryzen 5 2500U with Radeon Vega Mobile
    Memory: 6917MiB

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
webbench -c 8 -t 60:
1.LT + LT:-m 0 -t 8 -s 8:
176432 pages/min 329339 bytes/sec
188502 pages/min 351870 bytes/sec
189271 pages/min 353307 bytes/sec
190430 pages/min 355469 bytes/sec
191499 pages/min 357464 bytes/sec
191771 pages/min 357974 bytes/sec
189068 pages/min 352926 bytes/sec

2.LT + ET:-m 1 -t 8 -s 8:
180609 pages/min 337136 bytes/sec
188382 pages/min 351646 bytes/sec
202035 pages/min 377133 bytes/sec
202198 pages/min 377436 bytes/sec
193193 pages/min 360460 bytes/sec

3.ET + LT:-m 2 -t 8 -s 8:
217595 pages/min 406177 bytes/sec
214466 pages/min 400336 bytes/sec

4.ET + ET:-m 3 -t 8 -s 8:
213698 pages/min 398904 bytes/sec
207289 pages/min 386939 bytes/sec

--------------------------------------------------
webbench -c 32 -t 10:
1.LT + LT:-m 0 -t 32 -s 32:
181638 pages/min 339057 bytes/sec
203268 pages/min 379444 bytes/sec
197808 pages/min 369252 bytes/sec

2.LT + ET:-m 1 -t 32 -s 32:
176514 pages/min 329481 bytes/sec
190170 pages/min 354984 bytes/sec
182838 pages/min 341308 bytes/sec

3.ET + LT:-m 2 -t 32 -s 32:
197916 pages/min 369443 bytes/sec
210204 pages/min 392392 bytes/sec
212076 pages/min 395996 bytes/sec

4.ET + ET:-m 3 -t 32 -s 32:
204384 pages/min 381516
188784 pages/min 352396
208128 388505

原项目地址