- 简单介绍一下项目
这个项⽬是我在学习计算机⽹络和Linux socket编程过程中独⽴开发的轻量级Web服务器,服务器的⽹络模型是单reactor加线程池的模式,IO处理使⽤了⾮阻塞IO和IO多路复⽤技术,具备处理多个客户端的http请求,以及对外提供轻量级储存的能⼒。这个项⽬从1⽉份开始做,到3⽉份完成了项⽬的整体功能。
项⽬中的⼯作可以分为两部分,⼀部分是服务器⽹络框架、⽇志系统、存储引擎等⼀些基本系统的搭建,另⼀部分
是为了提⾼服务器性能所做的⼀些优化,⽐如缓存机制、内存池等⼀些额外系统的搭建。最后还对系统中的部分功
能进⾏了功能和压⼒测试。在本地测试下,在1000以内的客户端连接下都可以保持10000左右的QPS。
在开发阶段对于⼀些功能的不同实现⽅法,我先思考⼀下各个⽅法的应⽤场景和效率,再进⾏选择,通过这个项
⽬,我学习了两种Linux下的⾼性能⽹络模式,熟悉了Linux环境下的编程,并在后期优化的过程中了解了⼀些服务
端性能调优的⽅法。 - 项⽬中遇到的困难?是如何解决的?
- ⼀⽅⾯是对不同的技术理解不够深刻,难以选出最合适的技术框架。这部分的话我主要是反复阅读作者在
GitHub提供的⼀些技术⽂档,同时也去搜索⼀些技术对⽐的⽂章去看,如果没有任何相关的资料我会尝试去
联系作者。 - 另⼀⽅⾯是编程期间遇到的困难,在代码编写的过程中由于⼯程能⼒不⾜,程序总会出现⼀些bug。这部分的
话我⾸先是通过⽇志去定位bug,然后推断bug出现的原因并尝试修复,如果是⾃⼰⽬前⽔平⽆法修复的
bug,我会先到⽹上去查找有没有同类型问题的解决⽅法,然后向同学或者直接到StackOverflow等⼀些国外
知名论坛上求助。
- 针对项⽬做了哪些优化?(未做完)
- 程序本身
- 减少程序等待 IO 的事件:⾮阻塞 IO + IO 多路复⽤
- 设计⾼性能⽹络框架,同步 IO(主从 reactor + 线程池)和异步 IO(proactor)
- 【减少系统调⽤】避免频繁申请/释放内存:线程池、内存池和缓存机制
- 【减少系统调⽤】对于⽂件发送,使⽤零拷⻉函数 sendFile() 来发送,避免拷⻉数据到⽤户态
- 【减少系统调⽤】尽量减少锁的使⽤,如果需要,尽量减⼩临界区(⽇志系统和线程池)
- 系统参数调优
- 最⼤⽂件描述符数(⽤户级和系统级)
- tcp 连接的参数(半连接/连接队列的⻓度、tcp syncookies)
-
这个web服务器是你⾃⼰申请的域名吗
我没有申请域名,但是阿⾥云服务器上⾃带⼀个公⽹ip,可以直接通过公⽹ip来访问服务器。但是本地测试的话,
我⼀般使⽤本地回环ip,127.0.0.1来进⾏访问。能 ping 通 127.0.0.1 说明本机的⽹卡和IP协议安装都没有问题 -
C++ ⾯向对象特性在项⽬中的体现
c++⾯向对象特性有封装、继承、多态。
⾸先是封装,我在项⽬中将各个模块使⽤类进⾏封装,⽐如连接⽤ httpconnection/ftpconnection 类来封装,⽇
志就⽤ log 类来封装,将类的属性私有化,⽐如请求的解析状态,并且对外的接⼝设置为公有,⽐如连接的重置,
不对外暴露⾃身的私有⽅法,⽐如读写的回调函数等。还有⼀个就是,项⽬中每个模块都使⽤了各⾃的命名空间进
⾏封装,避免了命名冲突或者名字污染。
然后是继承,项⽬中的继承⽤得⽐较少,主要是对⼯具类的继承,项⽬中多个地⽅使⽤到 noncopyable 和
enable_shared_from_this,保证了代码的复⽤性。实际上对共有功能可以设计⼀个基类来继承,⽐如我项⽬中的
connection⽬前有 httpconnection 和 tcpconnection 两种,可以通过继承 connection 基类来减少重复代码,因
为我当时做的时候只考虑到 http连接,ftp是后⾯加上去的,所以就没⽤这样设计,后⾯可以优化⼀下。
最后是多态,我项⽬中的多态主要⽤了静态多态,动态多态没有涉及。静态多态在⽇志系统中对流输⼊运算符进⾏
了重载,以及在⽇志系统和内存池中都有各种函数模板的泛型编程。实际上刚刚说的 httpconnection 和
ftpconnection 从 connection 派⽣出来后是可以使⽤动态多态的。 -
采用什么网络模型?
采用单Reactor多线程模型。主线程是一个Reactor,进行IO多路复用实现连接事件和读写事件的监听,同时主线程负责新连接的建立,子线程则负责数据的读写还有业务逻辑处理。
服务器程序通常需要处理三类事件:I/O 事件、信号及定时事件。有两种高效的事件处理模式:Reactor和 Proactor,同步 I/O 模型通常用于实现 Reactor 模式,异步 I/O 模型通常用于实现 Proactor 模式。
Reactor模式
要求主线程(I/O处理单元)只负责监听文件描述符上是否有事件发生,有的话就立即将该事件通知工作线程(逻辑单元),将 socket 可读可写事件放入请求队列,交给工作线程处理。除此之外,主线程不做任何其他实质性的工作。读写数据,接受新的连接,以及处理客户请求均在工作线程中完成。
Proactor模式
Proactor 模式将所有 I/O 操作都交给主线程和内核来处理(进行读、写),工作线程仅仅负责业务逻辑。
reactor、proactor模型的区别? -
Reactor 是⾮阻塞同步⽹络模式,感知的是就绪可读写事件。在每次感知到有事件发⽣(⽐如可读就绪事
件)后,就需要应⽤进程主动调⽤ read ⽅法来完成数据的读取,也就是要应⽤进程主动将 socket 接收缓存
中的数据读到应⽤进程内存中,这个过程是同步的,读取完数据后应⽤进程才能处理数据。 -
Proactor 是异步⽹络模式, 感知的是已完成的读写事件。在发起异步读写请求时,需要传⼊数据缓冲区的地
址(⽤来存放结果数据)等信息,这样系统内核才可以⾃动帮我们把数据的读写⼯作完成,这⾥的读写⼯作全
程由操作系统来做,并不需要像 Reactor 那样还需要应⽤进程主动发起 read/write 来读写数据,操作系统完
成读写⼯作后,就会通知应⽤进程直接处理数据。 -
Reactor模式中,各个模式的区别?
Reactor模型是⼀个针对同步I/O的⽹络模型,主要是使⽤⼀个reactor负责监听和分配事件,将I/O事件分派给对应的Handler。新的事件包含连接建⽴就绪、读就绪、写就绪等。reactor模型中⼜可以细分为单reactor单线程、单reactor多线程、以及主从reactor模式。 -
单reactor单线程模型就是使⽤ I/O 多路复⽤技术,当其获取到活动的事件列表时