TinyHTTPServer源码解析
TinyHTTPServer源码分析
目录树:
源码目录源码地址:
- 日志功能 log/
- 数据库连接池sql/
- 线程池 include/threadpool.h
- 定时器 timer/:
- 封装的基本的线程同步机制:locker/
- http协议解析: http/
- 运行选项配置 config/
- 头文件 include/
- 资源文件 root/
提供8个配置选项:
- -p 指定监听端口
- -l 0同步写入日志, 1异步写入日志
- -o 1:开启socket linger延迟关闭连接 0:关闭
- -s 8 数据库连接池数量
- -t 线程池内线程的数量
- -c 0:开启日志 1:关闭日志
- -a IO模型 1:reactor 0:proactor
构建
项目依赖:
- Mysql C api apt:
sudo apt install libmysqlclient
- pthread
修改项目main.cc
中的登陆mysql的用户名和密码,以及库名
表结构
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
同步:
直接将产生的日志信息通过C的标准IO写入的日志文件
异步:
用数组封装一个先进先出的队列,然后将产生的每一条日志信息写入到队列,开启一个线程来不断读取队列中的每一条信息
/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(),(原来的发送好像有点问题)
解析流程看看有限状态自动机,
http包的组成:请求行+请求头部字段+空行+body
整个http的解析流程封装在函数process()中;
webbench测试数据
只放在本地的虚拟机里面测试了,不准确
CPU: AMD Ryzen 5 2500U with Radeon Vega Mobile
Memory: 6917MiB
1 |
|
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!