感谢前人的总结,让一个小白快速成长,那我也贡献一份自己的力量~
- 大框架梳理
- 从main函数开始学习
大框架梳理
先摆图:
目光先放到最上面的两个小框架,半同步/半反应堆线程池和异步日志系统,日志系统晓得伐?非常重要但不属于项目业务实现的核心,所以咱先主要看这个半同步/反应堆线程池,它在其中维护了一个请求队列,这个队列非常非常重要,主要用来处理请求(废话。。。
在这个线程池中的主线程通过epoll来监听socket,看有没有新请求,并将请求队列中的任务分配给线程池中的工作线程,OK到下一个绿框框了,工作线程可以处理的任务有三类:
- 日志输出
- 定时器处理非活动连接
- 处理HTTP请求
以上就是文字描述的核心业务,如果更加清晰简单描述的话就是下面这样:
MainReactor将与Client建立的连接发给Acceptor,Acceptor再将连接分配给一些SubReactor的模块,在SubReactor中对具体的连接进行读、编码、计算、解码和写操作(即对client请求的响应)。
上面这句话是我抄的,有没有和我一样看这句话也会冒出很多问题的朋友?
比如,什么是SubReactor,为什么图片上只画了两个,建立什么样的具体连接…
我直接说了,冒出这种问题是因为前置知识不足
这里用的是主从Reactor多线程,图其实画的…我不知道为什么会一传十十传百,我查资料之后感觉有写问题,如果不对还请评论指出下面以我查完资料的思路讲解一下:
我认为正确的图,来源看水印:
Reactor多线程方案说明:
- 主Reactor通过Selector监听连接事件,收到通知后。通过Acceptor处理连接事件
- 当Acceptor处理连接事件之后,MainReactor将事件分配给SubReactor
- SubReactor将这个事件加入到队列中,并且创建Handler去处理
- Handler对事件分发到Worker线程池并且进行处理
- 主Reactor可以对应多个子Reactor
该方案的优点是:
- 父线程与子线程的数据交互简单职责明确,父线程只负责接收新连接,子线程完成后续的业务处理
- 父线程与子线程的数据交互简单,Reactor主线程只需要把新连接传给子线程,子线程无需返回数据
现在再看框架就明白多了
框架就了解到这里吧,接下来梳理一下细节
从main函数开始学习
作者在main函数中进行了参数配置,设置日志的相关参数(存储路径、日志等级…),然后实例化了一个主循环对象,初始化了Webserver单例对象,最后启动了Webserver并开启了主循环。
EventLoop mainLoop;
Server myHTTPServer(&mainLoop, threadNum, port);
myHTTPServer.start();
mainLoop.loop();
其中最重要的函数是myHTTPServer.start();
和mainLoop.loop();
,先着重分析他俩
先看第一个函数myHTTPServer.start();
:
主要工作:
- 启动线程池
- 设置了acceptChannel_的handler,分别为读回调和连接回调
- 将acceptChannel_加入Poller
可以看出来是做了一些连接操作,但什么是Poller?什么是acceptChannel_?
现在很多还不好解释,我先解释一下acceptChannel_的读回调函数,读回调函数就是有新连接时执行的函数
他的主要工作是:
- 通过accept(3)来建立TCP连接
- 从线程池中获取一个eventLoop事件循环
- 将建立的新连接放入eventLoop事件循环
再看下mainLoop.loop();
:
做了以下工作:
- 从poller中取出所有活动事件
- 调用活动事件的回调函数
- 执行额外的函数
- 执行超时的回调函数