Skip to content

Commit

Permalink
✨ && 🐛 add reactor && fix bug
Browse files Browse the repository at this point in the history
  • Loading branch information
qinguoyi committed May 11, 2020
1 parent b1ebe82 commit 7a16b8f
Show file tree
Hide file tree
Showing 12 changed files with 217 additions and 62 deletions.
29 changes: 21 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ TinyWebServer
===============
Linux下C++轻量级Web服务器,助力初学者快速实践网络编程,搭建属于自己的服务器.

* 使用**线程池 + epoll(ET和LT均实现) + 模拟Proactor模式**的并发模型
* 使用**线程池 + epoll(ET和LT均实现) + 反应堆(Reactor和Proactor均实现)**的并发模型
* 使用**状态机**解析HTTP请求报文,支持解析**GET和POST**请求
* 访问服务器数据库实现web端用户**注册、登录**功能,可以请求服务器**图片和视频文件**
* 实现**同步/异步日志系统**,记录服务器运行状态
Expand Down Expand Up @@ -59,13 +59,21 @@ Demo演示
-------------
在关闭日志后,使用Webbench对服务器进行压力测试,在ET非阻塞和LT阻塞模式下均可实现上万的并发连接.

> * ET非阻塞,57838 QPS
> * Proactor,LT阻塞,84016 QPS
<div align=center><img src="http://ww1.sinaimg.cn/large/005TJ2c7ly1geme8se1a0j30f806qjun.jpg" height="201"/> </div>
<div align=center><img src="http://ww1.sinaimg.cn/large/005TJ2c7ly1geotkkw531j30f906tn0f.jpg" height="201"/> </div>

> * LT阻塞,64525 QPS
> * Proactor,ET非阻塞,83419 QPS
<div align=center><img src="http://ww1.sinaimg.cn/large/005TJ2c7ly1geme7wxpw3j30f906uadg.jpg" height="201"/> </div>
<div align=center><img src="http://ww1.sinaimg.cn/large/005TJ2c7ly1geotkozc4nj30f806p0vu.jpg" height="201"/> </div>

> * Reactor,LT阻塞,60218 QPS
<div align=center><img src="http://ww1.sinaimg.cn/large/005TJ2c7ly1geotkad1v2j30f906rad8.jpg" height="201"/> </div>

> * Reactor,ET非阻塞,58138 QPS
<div align=center><img src="http://ww1.sinaimg.cn/large/005TJ2c7ly1geotk12vwxj30fa06t0w3.jpg" height="201"/> </div>

> * 并发连接总数:10500
> * 访问服务器时间:5s
Expand All @@ -89,6 +97,7 @@ Demo演示
- [x] main函数封装重构
- [x] 新增命令行日志开关,关闭日志后更新压力测试结果
- [x] 改进编译方式,只配置一次SQL信息即可
- [x] 新增Reactor模式,并完成压力测试

目前有两个版本,版本间的代码结构有较大改动,文档和代码运行方法也不一致. 重构版本更简洁,原始版本(raw_version)更大保留游双代码的原汁原味,从原始版本更容易入手.

Expand Down Expand Up @@ -159,7 +168,7 @@ Demo演示
------

```C++
./server [-p port] [-v SQLVerify] [-l LOGWrite] [-m TRIGMode] [-o OPT_LINGER] [-s sql_num] [-t thread_num] [-c close_log]
./server [-p port] [-v SQLVerify] [-l LOGWrite] [-m TRIGMode] [-o OPT_LINGER] [-s sql_num] [-t thread_num] [-c close_log] [-a actor_model]
```

温馨提示:以上参数不是非必须,不用全部使用,根据个人情况搭配选用即可.
Expand All @@ -186,11 +195,14 @@ Demo演示
* -c,关闭日志,默认打开
* 0,打开日志
* 1,关闭日志
* -a,选择反应堆模型,默认Proactor
* 0,Proactor模型
* 1,Reactor模型

测试示例命令与含义

```C++
./server -p 9007 -v 1 -l 1 -m 0 -o 1 -s 10 -t 10 -c 1
./server -p 9007 -v 1 -l 1 -m 0 -o 1 -s 10 -t 10 -c 1 -a 1
```

- [x] 端口9007
Expand All @@ -201,6 +213,7 @@ Demo演示
- [x] 数据库连接池内有10条连接
- [x] 线程池内有10条线程
- [x] 关闭日志
- [x] Reactor反应堆模型

庖丁解牛
------------
Expand All @@ -222,4 +235,4 @@ Demo演示
------------
Linux高性能服务器编程,游双著.

感谢以下朋友的PR和帮助: [RownH](https://github.com/RownH)[ZWiley](https://github.com/ZWiley)[zjuHong](https://github.com/zjuHong)[mamil](https://github.com/mamil)[byfate](https://github.com/byfate).
感谢以下朋友的PR和帮助: [RownH](https://github.com/RownH)[ZWiley](https://github.com/ZWiley)[zjuHong](https://github.com/zjuHong)[mamil](https://github.com/mamil)[byfate](https://github.com/byfate)[MaJun827]https://github.com/MaJun827).
10 changes: 9 additions & 1 deletion config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@ Config::Config(){

//关闭日志,默认不关闭
close_log = 0;

//并发模型,默认是proactor
actor_model = 0;
}

void Config::parse_arg(int argc, char*argv[]){
int opt;
const char *str = "p:v:l:m:o:s:t:c:";
const char *str = "p:v:l:m:o:s:t:c:a:";
while ((opt = getopt(argc, argv, str)) != -1)
{
switch (opt)
Expand Down Expand Up @@ -73,6 +76,11 @@ void Config::parse_arg(int argc, char*argv[]){
close_log = atoi(optarg);
break;
}
case 'a':
{
actor_model = atoi(optarg);
break;
}
default:
break;
}
Expand Down
3 changes: 3 additions & 0 deletions config.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ class Config

//是否关闭日志
int close_log;

//并发模型选择
int actor_model;
};

#endif
24 changes: 14 additions & 10 deletions http/http_conn.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#include "http_conn.h"
#include "../log/log.h"

#include <mysql/mysql.h>
#include <fstream>

Expand All @@ -15,8 +15,9 @@ const char *error_500_title = "Internal Error";
const char *error_500_form = "There was an unusual problem serving the request file.\n";

locker m_lock;
map<string, string> users;

void http_conn::initmysql_result(connection_pool *connPool, map<string, string> &users)
void http_conn::initmysql_result(connection_pool *connPool)
{
//先从连接池中取一个连接
MYSQL *mysql = NULL;
Expand Down Expand Up @@ -46,7 +47,7 @@ void http_conn::initmysql_result(connection_pool *connPool, map<string, string>
}
}

void http_conn::initresultFile(connection_pool *connPool, map<string, string> &users)
void http_conn::initresultFile(connection_pool *connPool)
{
ofstream out("./CGImysql/id_passwd.txt");
//先从连接池中取一个连接
Expand Down Expand Up @@ -135,14 +136,15 @@ void http_conn::close_conn(bool real_close)
{
if (real_close && (m_sockfd != -1))
{
printf("close %d\n", m_sockfd);
removefd(m_epollfd, m_sockfd);
m_sockfd = -1;
m_user_count--;
}
}

//初始化连接,外部调用初始化套接字地址
void http_conn::init(int sockfd, const sockaddr_in &addr, char *root, map<string, string> &users, int SQLVerify, int TRIGMode,
void http_conn::init(int sockfd, const sockaddr_in &addr, char *root, int SQLVerify, int TRIGMode,
int close_log, string user, string passwd, string sqlname)
{
m_sockfd = sockfd;
Expand All @@ -153,7 +155,6 @@ void http_conn::init(int sockfd, const sockaddr_in &addr, char *root, map<string

//当浏览器出现连接重置时,可能是网站根目录出错或http响应格式出错或者访问的文件中内容完全为空
doc_root = root;
m_users = users;
m_SQLVerify = SQLVerify;
m_TRIGMode = TRIGMode;
m_close_log = close_log;
Expand Down Expand Up @@ -184,6 +185,9 @@ void http_conn::init()
m_read_idx = 0;
m_write_idx = 0;
cgi = 0;
m_state = 0;
timer_flag = 0;
improv = 0;

memset(m_read_buf, '\0', READ_BUFFER_SIZE);
memset(m_write_buf, '\0', WRITE_BUFFER_SIZE);
Expand Down Expand Up @@ -447,11 +451,11 @@ http_conn::HTTP_CODE http_conn::do_request()
strcat(sql_insert, password);
strcat(sql_insert, "')");

if (m_users.find(name) == m_users.end())
if (users.find(name) == users.end())
{
m_lock.lock();
int res = mysql_query(mysql, sql_insert);
m_users.insert(pair<string, string>(name, password));
users.insert(pair<string, string>(name, password));
m_lock.unlock();

if (!res)
Expand All @@ -466,7 +470,7 @@ http_conn::HTTP_CODE http_conn::do_request()
//若浏览器端输入的用户名和密码在表中可以查找到,返回1,否则返回0
else if (*(p + 1) == '2')
{
if (m_users.find(name) != m_users.end() && m_users[name] == password)
if (users.find(name) != users.end() && users[name] == password)
strcpy(m_url, "/welcome.html");
else
strcpy(m_url, "/logError.html");
Expand All @@ -487,11 +491,11 @@ http_conn::HTTP_CODE http_conn::do_request()
strcat(sql_insert, password);
strcat(sql_insert, "')");

if (m_users.find(name) == m_users.end())
if (users.find(name) == users.end())
{
m_lock.lock();
int res = mysql_query(mysql, sql_insert);
m_users.insert(pair<string, string>(name, password));
users.insert(pair<string, string>(name, password));
m_lock.unlock();

if (!res)
Expand Down
13 changes: 10 additions & 3 deletions http/http_conn.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@
#include <sys/wait.h>
#include <sys/uio.h>
#include <map>

#include "../lock/locker.h"
#include "../CGImysql/sql_connection_pool.h"
#include "../timer/lst_timer.h"
#include "../log/log.h"

class http_conn
{
Expand Down Expand Up @@ -70,7 +73,7 @@ class http_conn
~http_conn() {}

public:
void init(int sockfd, const sockaddr_in &addr, char *, map<string, string> &, int, int, int, string user, string passwd, string sqlname);
void init(int sockfd, const sockaddr_in &addr, char *, int, int, int, string user, string passwd, string sqlname);
void close_conn(bool real_close = true);
void process();
bool read_once();
Expand All @@ -79,8 +82,11 @@ class http_conn
{
return &m_address;
}
void initmysql_result(connection_pool *connPool, map<string, string> &);
void initresultFile(connection_pool *connPool, map<string, string> &);
void initmysql_result(connection_pool *connPool);
void initresultFile(connection_pool *connPool);
int timer_flag;
int improv;


private:
void init();
Expand All @@ -106,6 +112,7 @@ class http_conn
static int m_epollfd;
static int m_user_count;
MYSQL *mysql;
int m_state; //读为0, 写为1

private:
int m_sockfd;
Expand Down
9 changes: 5 additions & 4 deletions main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ int main(int argc, char *argv[])
//需要修改的数据库信息,登录名,密码,库名
string user = "root";
string passwd = "root";
string databasename = "yourdb";
string databasename = "qgydb";

//命令行解析
Config config;
Expand All @@ -15,14 +15,15 @@ int main(int argc, char *argv[])

//初始化
server.init(config.PORT, user, passwd, databasename, config.LOGWrite, config.SQLVerify,
config.OPT_LINGER, config.TRIGMode, config.sql_num, config.thread_num, config.close_log);
config.OPT_LINGER, config.TRIGMode, config.sql_num, config.thread_num,
config.close_log, config.actor_model);

//日志
server.log_write();

//数据库
server.sql_pool();

//线程池
server.thread_pool();

Expand Down
2 changes: 1 addition & 1 deletion makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
server: main.cpp ./timer/lst_timer.h ./timer/lst_timer.cpp ./threadpool/threadpool.h ./http/http_conn.cpp ./http/http_conn.h ./lock/locker.h ./log/log.cpp ./log/log.h ./log/block_queue.h ./CGImysql/sql_connection_pool.cpp ./CGImysql/sql_connection_pool.h webserver.h webserver.cpp config.h config.cpp
g++ -o server main.cpp ./timer/lst_timer.h ./timer/lst_timer.cpp ./threadpool/threadpool.h ./http/http_conn.cpp ./http/http_conn.h ./lock/locker.h ./log/log.cpp ./log/log.h ./CGImysql/sql_connection_pool.cpp ./CGImysql/sql_connection_pool.h webserver.h webserver.cpp config.h config.cpp -lpthread -lmysqlclient
g++ -o server main.cpp ./timer/lst_timer.h ./timer/lst_timer.cpp ./threadpool/threadpool.h ./http/http_conn.cpp ./http/http_conn.h ./lock/locker.h ./log/log.cpp ./log/log.h ./CGImysql/sql_connection_pool.cpp ./CGImysql/sql_connection_pool.h webserver.h webserver.cpp config.h config.cpp -lpthread -lmysqlclient -g

CGISQL.cgi:./CGImysql/sign.cpp ./CGImysql/sql_connection_pool.cpp ./CGImysql/sql_connection_pool.h
g++ -o ./root/CGISQL.cgi ./CGImysql/sign.cpp ./CGImysql/sql_connection_pool.cpp ./CGImysql/sql_connection_pool.h -lmysqlclient -lpthread
Expand Down
63 changes: 55 additions & 8 deletions threadpool/threadpool.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ class threadpool
{
public:
/*thread_number是线程池中线程的数量,max_requests是请求队列中最多允许的、等待处理的请求的数量*/
threadpool(connection_pool *connPool, int thread_number = 8, int max_request = 10000);
threadpool(int actor_model, connection_pool *connPool, int thread_number = 8, int max_request = 10000);
~threadpool();
bool append(T *request);
bool append(T *request, int state);
bool append_p(T *request);

private:
/*工作线程运行的函数,它不断从工作队列中取出任务并执行之*/
Expand All @@ -30,9 +31,10 @@ class threadpool
locker m_queuelocker; //保护请求队列的互斥锁
sem m_queuestat; //是否有任务需要处理
connection_pool *m_connPool; //数据库
int m_actor_model; //模型切换
};
template <typename T>
threadpool<T>::threadpool( connection_pool *connPool, int thread_number, int max_requests) : m_thread_number(thread_number), m_max_requests(max_requests), m_threads(NULL),m_connPool(connPool)
threadpool<T>::threadpool( int actor_model, connection_pool *connPool, int thread_number, int max_requests) : m_actor_model(actor_model),m_thread_number(thread_number), m_max_requests(max_requests), m_threads(NULL),m_connPool(connPool)
{
if (thread_number <= 0 || max_requests <= 0)
throw std::exception();
Expand All @@ -59,7 +61,22 @@ threadpool<T>::~threadpool()
delete[] m_threads;
}
template <typename T>
bool threadpool<T>::append(T *request)
bool threadpool<T>::append(T *request, int state)
{
m_queuelocker.lock();
if (m_workqueue.size() >= m_max_requests)
{
m_queuelocker.unlock();
return false;
}
request->m_state = state;
m_workqueue.push_back(request);
m_queuelocker.unlock();
m_queuestat.post();
return true;
}
template <typename T>
bool threadpool<T>::append_p(T *request)
{
m_queuelocker.lock();
if (m_workqueue.size() >= m_max_requests)
Expand Down Expand Up @@ -96,10 +113,40 @@ void threadpool<T>::run()
m_queuelocker.unlock();
if (!request)
continue;

connectionRAII mysqlcon(&request->mysql, m_connPool);

request->process();
if (1 == m_actor_model)
{
if (0 == request->m_state)
{
if (request->read_once())
{
request->improv = 1;
connectionRAII mysqlcon(&request->mysql, m_connPool);
request->process();
}
else
{
request->improv = 1;
request->timer_flag = 1;
}
}
else
{
if (request->write())
{
request->improv = 1;
}
else
{
request->improv = 1;
request->timer_flag = 1;
}
}
}
else
{
connectionRAII mysqlcon(&request->mysql, m_connPool);
request->process();
}
}
}
#endif
1 change: 1 addition & 0 deletions timer/lst_timer.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "lst_timer.h"
#include "../http/http_conn.h"

sort_timer_lst::sort_timer_lst()
{
Expand Down
Loading

0 comments on commit 7a16b8f

Please sign in to comment.