从0.1最初形成到一点一点改进到0.6,到最终看了muduo,痛下决心重写,最终的版本完全从头再来了,但有了前面的经验,写起来顺畅了不少,但花了比之前所有加起来还要长的时间
第一版是看了很多Github上别人写的服务器,以及博客上的一些总结,结合自己的理解写出来的。模型结构如下:
-
使用了epoll边沿触发+EPOLLONESHOT+非阻塞IO
-
使用了一个固定线程数的线程池
-
实现了一个任务队列,由条件变量触发通知新任务的到来
-
实现了一个小根堆的定时器及时剔除超时请求,使用了STL的优先队列来管理定时器
-
解析了HTTP的get、post请求,支持长短连接
-
mime设计为单例模式
-
线程的工作分配为:
- 主线程负责等待epoll中的事件,并把到来的事件放进任务队列,在每次循环的结束剔除超时请求和被置为删除的时间结点
- 工作线程阻塞在条件变量的等待中,新任务到来后,某一工作线程会被唤醒,执行具体的IO操作和计算任务,如果需要继续监听,会添加到epoll中
-
锁的使用有两处:
- 第一处是任务队列的添加和取操作,都需要加锁,并配合条件变量,跨越了多个线程。
- 第二处是定时器结点的添加和删除,需要加锁,主线程和工作线程都要操作定时器队列。
第一版的服务器已经相对较完整了,该有的功能都已经具备了
在第一版的基础上,优化了代码结构,自己设计了RAII锁机制,使锁能够自动释放,并修复了一些小的bug
- 几乎全部的裸指针被智能指针替代
- 利用weak_ptr解决时间结点和HTTP类互指的问题
- 任务队列的任务结构从函数指针+参数指针转换为C++11的function
这一版还是花了不少时间的,毕竟对象的生命周期不由自己控制了
这个时候买了陈硕的《Linux多线程服务端编程》书,看了一部分,从前面几章获得启发
- 为不提供拷贝构造和赋值运算符的类添加了noncopyable基类
- 重写了RAII机制的锁,学习muduo中的做法
- 重写了单例模式,将双重加锁改为更简单而安全的pthread_once()
- 修复了一些bug,稍微调整了类的结构
- 封装了条件变量
- 仿照muduo,写了4个缓冲区的异步Log日志,没有区分优先级,其它基本都具备了
不知道该给自己的服务器取什么名字好,随便叫个吧……最后一版被我改的面目全非,也是下了很大的决心。之前的版本无非修修补补,算是自我检讨的过程,但是闭门造车并不可取,于是我把陈硕的《Linux多线程服务端编程》看完了,书上虽然贴了部分源码,但我看的还是朦朦胧胧,很多地方不明白,花了几天时间把源码看了,不懂的地方再回过来看书,总算是弄明白了。看了大牛的代码,再看自己的……哎我重写总行了吧
顺便吐槽一下自己,之前在知乎上看到陈硕一直推销自己的书,我还觉得这人好功利,后来被师兄推荐,看了一下目录,觉得可以参考一下就买了。没想到书就一点一点这么看完了……还看了好几遍,源码也是看了好几遍……
当然,这不是最终篇,可改进的地方还有很多,绝对不敢说看了几本书就敢说自己写的东西比大牛写的好,我还会继续改进自己的server的
最后版本的东西没有在这里介绍,写在了模型结构里,这里只想写一下自己的心路历程,记录一下小白成长之路~