一、概述
以IO多路复用为核心,采取单Reactor
模式,接受处理请求和发送响应(目前仅能处理静态文件请求)。用时间堆管理的定时器来处理超时链接。
项目大概2000行代码,只是我看完《Linux高性能服务器》
来实践的项目,性能肯定和muduo
这种网络库没法比,算是新手上路项目吧。最主要的逻辑在HttpServer
部分,另外复杂点就是定时器的部分。通过了压测并且部署到了实际服务器上,有什么问题欢迎留言或者私信交流。
项目地址:https://github.com/MisakiOfScut/SingHttpServer
二、运行
mkdir build && cd build
cmake ..
make
./main
#由于设置成了守护进程,所以需要用kill命令去结束运行
kill -SIGINT `pid` #或者把main函数里面skeleton_daemon()注释掉
三、整体架构
采取单Reactor模式,一个主线程多个工作线程,主线程负责监听和接受链接,工作线程负责处理请求和读写任务。当fd有可读/写事件时,主线程将可调用对象放入工作队列,工作线程通过竞争条件获取任务执行。
features:
- 使用 线程池 +
epoll
水平触发 +Reactor
事件处理模式的并发模型 - 使用状态机解析
HTTP
请求,支持解析GET
请求 - 支持
HTTP
长连接,使用最小堆管理的定时器处理超时链接 - 经
Webbench
压力测试验证可以实现上万的并发链接的数据交换
更多细节:https://github.com/MisakiOfScut/SingHttpServer/blob/main/docs/intro.md
四、测试
准备
- 原版的Webbench是不支持长连接的,需要改写,我用了https://github.com/linyacool/WebBench这个大佬改写的
- 用
ulimit
查看系统允许打开的文件描述符、进程数量上限、core文件大小,我用的Ubuntu18.04
默认值是不够的,直接改了个10万的文件描述符和进程数量无限制;不然用webbench
并发数一多就会Resources temporarily unavailable
测试
先简单测测,可以用浏览器或者curl
,看看功能是否正常;复杂一点可以用wireshark
抓包看看生成的报文是否正确;这里就测测复杂页面、错误请求、传输大文件等功能是否正常
压力测试
多线程编程,很多问题一并发就凸显出来了,这也是最折磨人的部分,如果你的设计有问题不能保证线程安全,比如对容器不加锁并发地读写,那到一定并发数量后就会出现内存泄漏的问题。
并发测试不要连续进行,测试完用netstat -t
可以查看系统的TCP链接,你会发现有很多TIME_WAIT
状态的链接,这样会对下次测试的结果造成影响。
通过回环地址测试的,看了乐乎就好
五、有待改进
架构
最大能提升的地方是架构,项目采用单Reactor多线程模型,存在两个问题:
- 线程需要对工作队列频繁加锁,这是一个很浪费时间的操作
- 一个线程一次只能处理一个client的请求,当请求数量越来越多时,工作队列会堆积越来越多的任务对象,服务器对客户端的响应将会变慢;通过增加线程数并不能解决这个问题因为如果多于cpu核数的话线程间的切换也会限制性能
因此有主从Reactor模型来改进这个问题,主Reactor只负责监听listenfd,有链接时accept然后派给从Reactor,从Reactor有自己的epoll_wait来处理IO,这样一个线程可以处理多个请求。感兴趣的可以去看muduo
文件发送
目前文件发送采用了用mmap
文件映射到内存然后往socket写的方式,需要DMA先把文件从磁盘拷贝到内核(第一次cp),进程通过内存映射能访问文件并把文件写到socket的发送缓冲区(第二次cp),内核把socket的数据发往网卡(第三次cp),一共需要三次copy
如果采用sendfile,那么需要DMA先把文件从磁盘拷贝到内核(第一次cp),内核直接将数据发到网卡(二次cp),cp次数更少
功能
- 日志功能(可以用
syslog
,但是自己写一个肯定更加分) - 解析
POST
请求
编译
把文件置于一个目录下,makelist
会好写点