From 7a16b8f97867f81080dad1faa60df85f39684ab7 Mon Sep 17 00:00:00 2001
From: qinguoyi <1532979219@qq.com>
Date: Mon, 11 May 2020 21:28:41 +0800
Subject: [PATCH] :sparkles: && :bug: add reactor && fix bug
---
README.md | 29 +++++++++----
config.cpp | 10 ++++-
config.h | 3 ++
http/http_conn.cpp | 24 ++++++-----
http/http_conn.h | 13 ++++--
main.cpp | 9 ++--
makefile | 2 +-
threadpool/threadpool.h | 63 +++++++++++++++++++++++----
timer/lst_timer.cpp | 1 +
timer/lst_timer.h | 21 ++++++++-
webserver.cpp | 96 ++++++++++++++++++++++++++++++++---------
webserver.h | 8 +---
12 files changed, 217 insertions(+), 62 deletions(-)
diff --git a/README.md b/README.md
index b82ac646..fdcf2001 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@ TinyWebServer
===============
Linux下C++轻量级Web服务器,助力初学者快速实践网络编程,搭建属于自己的服务器.
-* 使用**线程池 + epoll(ET和LT均实现) + 模拟Proactor模式**的并发模型
+* 使用**线程池 + epoll(ET和LT均实现) + 反应堆(Reactor和Proactor均实现)**的并发模型
* 使用**状态机**解析HTTP请求报文,支持解析**GET和POST**请求
* 访问服务器数据库实现web端用户**注册、登录**功能,可以请求服务器**图片和视频文件**
* 实现**同步/异步日志系统**,记录服务器运行状态
@@ -59,13 +59,21 @@ Demo演示
-------------
在关闭日志后,使用Webbench对服务器进行压力测试,在ET非阻塞和LT阻塞模式下均可实现上万的并发连接.
-> * ET非阻塞,57838 QPS
+> * Proactor,LT阻塞,84016 QPS
-
+
-> * LT阻塞,64525 QPS
+> * Proactor,ET非阻塞,83419 QPS
-
+
+
+> * Reactor,LT阻塞,60218 QPS
+
+
+
+> * Reactor,ET非阻塞,58138 QPS
+
+
> * 并发连接总数:10500
> * 访问服务器时间:5s
@@ -89,6 +97,7 @@ Demo演示
- [x] main函数封装重构
- [x] 新增命令行日志开关,关闭日志后更新压力测试结果
- [x] 改进编译方式,只配置一次SQL信息即可
+- [x] 新增Reactor模式,并完成压力测试
目前有两个版本,版本间的代码结构有较大改动,文档和代码运行方法也不一致. 重构版本更简洁,原始版本(raw_version)更大保留游双代码的原汁原味,从原始版本更容易入手.
@@ -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]
```
温馨提示:以上参数不是非必须,不用全部使用,根据个人情况搭配选用即可.
@@ -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
@@ -201,6 +213,7 @@ Demo演示
- [x] 数据库连接池内有10条连接
- [x] 线程池内有10条线程
- [x] 关闭日志
+- [x] Reactor反应堆模型
庖丁解牛
------------
@@ -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).
diff --git a/config.cpp b/config.cpp
index 1f5241fa..03a0d13f 100644
--- a/config.cpp
+++ b/config.cpp
@@ -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)
@@ -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;
}
diff --git a/config.h b/config.h
index 92856727..701cb3e3 100644
--- a/config.h
+++ b/config.h
@@ -36,6 +36,9 @@ class Config
//是否关闭日志
int close_log;
+
+ //并发模型选择
+ int actor_model;
};
#endif
\ No newline at end of file
diff --git a/http/http_conn.cpp b/http/http_conn.cpp
index 0b82ed37..f6f60488 100644
--- a/http/http_conn.cpp
+++ b/http/http_conn.cpp
@@ -1,5 +1,5 @@
#include "http_conn.h"
-#include "../log/log.h"
+
#include
#include
@@ -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 users;
-void http_conn::initmysql_result(connection_pool *connPool, map &users)
+void http_conn::initmysql_result(connection_pool *connPool)
{
//先从连接池中取一个连接
MYSQL *mysql = NULL;
@@ -46,7 +47,7 @@ void http_conn::initmysql_result(connection_pool *connPool, map
}
}
-void http_conn::initresultFile(connection_pool *connPool, map &users)
+void http_conn::initresultFile(connection_pool *connPool)
{
ofstream out("./CGImysql/id_passwd.txt");
//先从连接池中取一个连接
@@ -135,6 +136,7 @@ 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--;
@@ -142,7 +144,7 @@ void http_conn::close_conn(bool real_close)
}
//初始化连接,外部调用初始化套接字地址
-void http_conn::init(int sockfd, const sockaddr_in &addr, char *root, map &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;
@@ -153,7 +155,6 @@ void http_conn::init(int sockfd, const sockaddr_in &addr, char *root, map(name, password));
+ users.insert(pair(name, password));
m_lock.unlock();
if (!res)
@@ -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");
@@ -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(name, password));
+ users.insert(pair(name, password));
m_lock.unlock();
if (!res)
diff --git a/http/http_conn.h b/http/http_conn.h
index 95bbe68a..89af3b64 100644
--- a/http/http_conn.h
+++ b/http/http_conn.h
@@ -20,8 +20,11 @@
#include
#include
#include