点 击 关 注 上 方"两猿社"
设 为"置 顶 或 星 标",干 货 第 一 时 间 送 达。
互 联 网 猿 | 两 猿 社
TineyWebServer
Linux下C++轻量级Web服务器,助力初学者快速实践网络编程,搭建属于自己的服务器.
使用线程池 + epoll(ET和LT均实现) + 模拟Proactor模式并发模型
使用状态机解析HTTP请求报文,支持解析GET和POST请求
通过访问服务器数据库实现web端用户注册、登录功能,可以请求服务器图片和视频文件
实现同步/异步日志系统,记录服务器运行状态
经Webbench压力测试可以实现上万的并发连接数据交换
框架
项目框架主要分为I/O处理单元、逻辑处理单元和存储单元三个模块
I/O处理单元和逻辑处理单元对应半同步/半反应堆线程池
逻辑处理单元和存储单元对应数据库连接池和日志系统
半同步/半反应堆线程池将web端和服务器端建立通信
实现http请求报文的处理与响应
定时器完成非活动连接的处理
数据库连接池避免频繁访问数据库,实现登录和校验功能
日志系统实现同步和异步两种方式记录服务器运行状态
工作流程
以一个请求到来具体的处理过程介绍项目工作流程,具体包括web端和服务器建立连接,访问服务器数据库完成登录和注册,并通过定时器完成非活动连接的处理,最后服务器运行状态通过日志系统进行记录。
web端和服务器端建立连接
采用epoll的边缘触发模式同时监听多个文件描述符,采用同步I/O模拟proactor模式处理事件,主线程负责监听客户端是否发起请求
当web端发起http请求时,主线程接收请求报文,然后将任务插入请求队列,由工作线程通过竞争从请求队列中获取任务
通过http类中的主从状态机对请求报文进行分析,根据请求报文对客户端进行http响应,然后由主线程给客户端发送响应报文。
连接数据库
单例模式创建数据库连接池,避免频繁建立连接,用于后续web端登录和注册校验访问服务器数据库
实现web端的登录和注册
web访问的欢迎界面为GET请求,登录和注册界面是POST请求。
欢迎界面有新用户(0)和已有账号(1)两个选项,若选择新用户,会跳转注册(3)界面,注册成功或选择已有账号,跳转登录(2)界面,注册或登录失败会提示失败,成功和失败为0,1
同步/异步日志系统,记录服务器运行状态
同步的方式下,工作线程直接写入日志文件
异步会另外创建一个写线程,工作线程将要写的内容push进请求队列,通过写线程写入文件
日志文件支持按日期分类,和超过最大行数自动创建新文件
非活动连接的处理
由于非活跃连接占用了连接资源,严重影响服务器的性能,通过实现一个服务器定时器,处理这种非活跃连接,释放连接资源。
利用alarm函数周期性地触发SIGALRM信号,该信号的信号处理函数利用管道通知主循环执行定时器链表上的定时任务.
Demo
注册演示
登录演示
请求图片文件演示(6M)
请求视频文件演示(39M)
压力测试
Webbench对服务器进行压力测试,在ET非阻塞和LT阻塞模式下均可实现上万的并发连接.
ET非阻塞
LT阻塞
并发连接总数:10500
访问服务器时间:5s
所有访问均成功
注意: 使用本项目的webbench进行压测时,若报错显示webbench命令找不到,将可执行文件webbench删除后,重新编译即可。
基础测试
服务器测试环境
Ubuntu版本16.04
MySQL版本5.7.29
测试前确认已安装MySQL数据库
1//建立yourdb库
2create database yourdb set utf8;
3
4//创建user表
5USE yourdb;
6CREATE TABLE user( 7 username char(50) NULL, 8 passwd char(50) NULL 9)ENGINE=InnoDB;
10
11//添加数据
12INSERT INTO user(username, passwd) VALUES('name', 'passwd');修改main.c中的数据库初始化信息
1//root root为服务器数据库的登录名和密码
2connection_pool *connPool=connection_pool::GetInstance("localhost","root","root","yourdb",3306,5);修改http_conn.cpp中的root路径
1const char* doc_root="/home/qgy/TinyWebServer/root";
生成server
1make server
启动server
1./server port
浏览器端
1ip:port
个性化测试
个性化测试分为三种情况,分别是校验方式、I/O复用方式、日志写入方式。
校验方式
选择任一校验方式,代码中使用同步校验,可以修改为CGI.
同步线程数据库校验
关闭main.c中CGISQLPOOL,打开SYNSQL
123 #define SYNSQL //同步数据库校验
224 //#define CGISQLPOOL //CGI数据库校验关闭http_conn.cpp中两种CGI,打开SYNSQL
17 //同步校验
28 #define SYNSQL
3
410 //CGI多进程使用链接池
511 //#define CGISQLPOOL
6
713 //CGI多进程不用连接池
814 //#define CGISQL
CGI多进程数据库校验,不使用连接池
关闭main.c中SYNSQL和CGISQLPOOL
123 //#define SYNSQL //同步数据库校验
224 //#define CGISQLPOOL //CGI数据库校验关闭http_conn.cpp中SYNSQL和CGISQLPOOL,打开CGISQL
17 //同步校验
28 //#define SYNSQL
3
410 //CGI多进程使用链接池
511 //#define CGISQLPOOL
6
713 //CGI多进程不用连接池
814 #define CGISQL关闭sign.cpp中的CGISQLPOOL,打开CGISQL
112 #define CGISQL //不使用连接池
213 //#define CGISQLPOOL //使用连接池修改sign.cpp中的数据库初始化信息
1//root root为服务器数据库的登录名和密码
2connection_pool *connPool=connection_pool::GetInstance("localhost","root","root","yourdb",3306,5);生成CGISQL.cgi
1make CGISQL.cgi
CGI多进程数据库校验,使用连接池
关闭main.c中SYNSQL,打开CGISQLPOOL
123 //#define SYNSQL //同步数据库校验
224 #define CGISQLPOOL //CGI数据库校验关闭http_conn.cpp中SYNSQL和CGISQL,打开CGISQLPOOL
17 //同步校验
28 //#define SYNSQL
3
410 //CGI多进程使用链接池
511 #define CGISQLPOOL
6
713 //CGI多进程不用连接池
814 //#define CGISQL关闭sign.cpp中的CGISQL,打开CGISQLPOOL
112 //#define CGISQL //不使用连接池
213 #define CGISQLPOOL //使用连接池生成CGISQL.cgi
1make CGISQL.cgi
I/O复用方式
选择任一I/O复用方式,代码中使用LT阻塞,可以修改为ET非阻塞.
LT阻塞
关闭main.c中ET,打开LT
128 //#define ET //边缘触发非阻塞
229 #define LT //水平触发阻塞关闭http_conn.cpp中ET,打开LT
116 //#define ET //边缘触发非阻塞
217 #define LT //水平触发阻塞
ET非阻塞
关闭main.c中LT,打开ET
128 #define ET //边缘触发非阻塞
229 //#define LT //水平触发阻塞关闭http_conn.cpp中LT,打开ET
116 #define ET //边缘触发非阻塞
217 //#define LT //水平触发阻塞
日志写入方式
选择任一日志方式,代码中使用同步日志,可以修改为异步写入.
同步写入日志
关闭main.c中ASYNLOG,打开同步写入SYNLOG
125 #define SYNLOG //同步写日志
226 //#define ASYNLOG /异步写日志
异步写入日志
关闭main.c中SYNLOG,打开异步写入ASYNLOG
125 //#define SYNLOG //同步写日志
226 #define ASYNLOG /异步写日志
选择数据库访问、I/O复用方式或日志写入方式后,按照前述生成server,启动server,即可进行测试.
致谢
Linux高性能服务器编程,游双著.
如果本文对你有帮助,阅读原文
star一下服务器项目,我们需要你的星星^_^.
完。