C++ & HTML
单线程IO复用+多线程业务处理:
- 主线程负责接受连接和读写,工作线程负责解析处理请求(proactor)->读写并发数为1,即使业务处理很快,入口(读)和出口(写)只有一个,是瓶颈
- 或者主线程只负责接受连接,工作线程负责读写和解析处理请求(reactor)->读写并发数为n(工作线程个数),每个工作线程都是出入口,比proactor好一点
- Ubuntu 18.04
- MySQL 5.7.31
-
将main.cc中数据库连接池初始化参数修改为你自己的数据库用户名、用户密码和数据库名称
connpool->init("localhost",0,"root","123","db",5);
-
将http_conn.cc文件中的html文件所在的根目录改成你系统中该root文件夹所在路径
const char* doc_root="/home/sing/code/fight/my_web_server/root";
在Makefile所在文件夹运行命令行
make
在程序所在文件夹运行命令行,其中ip改成你电脑的IP,端口改成任意不冲突端口即可(不用加[])
./server [your ip] [port]
半同步半反应堆式:主线程负责接受新连接和网络数据读写,工作线程只负责解析和处理客户请求
- 定时器(基于升序链表)
- 日志(基于阻塞队列)
- 线程池
- 线程间通信:信号量、互斥锁、条件变量
- 数据库和连接池
- I/O复用(epoll)
- 统一事件源(socketpair管道)
- 监听描述符可读->初始化该连接描述符对应的客户对象,添加该连接的定时器
- 异常事件->关闭连接
- 信号管道可读
- 终止信号->将终止服务器标志设为true
- SIGALRM信号->将timeout标志设为true,所有就绪事件处理完毕后将调用定时器的Tick函数,清理到期的定时器
- 连接描述符可读->
- 主线程读取客户数据,放在客户对象中
- 将客户对象指针放入请求队列中
- 调整定时器
- 连接描述符可写->将客户对象中写缓冲区的数据发送给客户端
- 阻塞等待请求到来
- 从请求队列中取出客户对象指针,对其中的读缓冲区内容进行解析和处理
- 将响应写入客户对象的写缓冲区中,使epoll改为监听该连接描述符上的可写事件
- 定时器容器整合:在原有基于升序链表的定时器容器类的基础上,加上了时间堆和时间轮。利用简单工厂模式,实现了三种定时器容器在使用方法上的统一。现在,只需要给定类型,就可以生成相应的定时器容器,完成定时器的添加、删除、调整和Tick。
- 增加了session功能,通过unordered_map存储会话信息,包括过期时间、是否验证通过等,实现了客户连接在登录后一段时间内不需要重复登录的功能; 增加了注销登录按钮,使用户通过同一个浏览器可以登录不同账户。
- 主线程与工作线程、其他线程与日志线程,都统一通过阻塞队列进行通信。将原本的线程detach属性去掉,改为由主线程进行回收。当需要结束程序时,主线程会在析构函数将stop标志位置为true,并调用条件变量的NotifyAll函数唤醒阻塞线程,它们将检测到结束标志并安全退出。
- 通过宏和条件编译增加了reator模式,即把网络I/O工作交给工作线程,主线程只负责接受连接,在请求到来时往任务队列中加入任务。