目录
model
http请求解析:
这个层层调用还是有些复杂,要好好捋一捋
1.开启线程池和事件循环
- Main函数开始运行,首先创建
EventLoop mainLoop;
Server myHTTPServer(&mainLoop, threadNum, port); //在此构造函数中new了一个EventLoopThreadPool
- Main函数中开启主事件循环(reactor线程)运行
myHTTPServer.start();
mainLoop.loop();
- 在 myHTTPServer构造函数中,创建了线程池eventLoopThreadPool_(new EventLoopThreadPool(loop_,threadNum)),其线程数量由threadNum指定
- 在myHTTPServer.start()中,启动了线程池:eventLoopThreadPool_->start();
好,在启动线程池之后,为线程池添加事件循环线程:
for (int i = 0; i < numThreads_; ++i) {
std::shared_ptr<EventLoopThread> t(new EventLoopThread()); //根据传入的线程数创建指向loop线程的智能指针
threads_.push_back(t); //将管理循环线程的指针填入已创建线程的数组,便于统一管理
loops_.push_back(t->startLoop()); //开启新建线程的循环,并放入管理循环的数组中
}
在new EventLoopThread()中,我们一层一层往下看:
在EventLoopThread的构造函数中,有
thread_(bind(&EventLoopThread::threadFunc, this), "EventLoopThread"), //绑定线程的函数
//其中
void EventLoopThread::threadFunc() {
EventLoop loop;
{
MutexLockGuard lock(mutex_);
loop_ = &loop; //引用 ,到这里loop_就不为NULL了,即条件变量等到了
cond_.notify();
}
loop.loop();
//assert(exiting_);
loop_ = NULL; //就是起到一个EventLoopThread创建EventLoop并启动事件循环的作用
}
其中thread_变量是一个Thread类,相当于我们调用了他的构造函数并绑定好了创建loop的函数,如下:
Thread::Thread(const ThreadFunc &func, const string &n)
:started_(false),
joined_(false),
pthreadId_(0),
tid_(0),
func_(func),
name_(n),
latch_(1)
{
setDefaultName();
}
接着我们看loops_.push_back(t->startLoop()); 这个函数的目的是开启线程的loop,并push进loops_数组中,便于管理。
我们看看他往下到底做了啥:
EventLoop* EventLoopThread::startLoop() { //由EventLoopThreadPool::start()调用
assert(!thread_.started());
thread_.start();
{
MutexLockGuard lock(mutex_); //将锁搞成一个类,在函数段内创建临时锁对象并加锁,当退出函数段时自动调用对象析构函数进行解锁
// 一直等到threadFun在Thread里真正跑起来
while (loop_ == NULL)
cond_.wait(); //等在条件变量上 , 条件变量也封装为一个条件类,并且this类中包含了cond的对象
}
return loop_;
}
调用了thread_.start(); 追踪一波:
void Thread::start() {
assert(!started_);
started_ = true;
ThreadData* data = new ThreadData(func_, name_,&tid_, &latch_);
if (pthread_create(&pthreadId_, NULL, &startThread, data)) {
started_ = false;
delete data;
}
else { //注意,pthread_create创建成功是返回0的
latch_.wait();
assert(tid_ > 0);
}
}
好了,实际上start就是pthread_create创建线程并绑定好我们之前给他的函数。
创建好的线程会运行threadFunc函数,创建loop并唤醒主线程,好返回自己的loop并填进loops_中。
至此完成IO线程的loop创建步骤。
- 然后,在创建完线程之后,myHTTPServer紧接着配置他的channel:
void Server::start() {
eventLoopThreadPool_->start(); //开启线程池
//acceptChannel_->setEvents(EPOLLIN | EPOLLET | EPOLLONESHOT);
acceptChannel_->setEvents(EPOLLIN | EPOLLET);
acceptChannel_->setReadHandler(bind(&Server::handNewConn, this)); //处理新连接事件
acceptChannel_->setConnHandler(bind(&Server::handThisConn, this)); //处理读写事件
loop_->addToPoller(acceptChannel_, 0); //将负责处理接受连接的channel 挂到epoll去
started_ = true;
}
这里不得不仔细看看myHTTPServer的构造函数主要做了哪些工作:
Server::Server(EventLoop *loop, int threadNum, int port)
: loop_(loop),
threadNum_(threadNum),
eventLoopThreadPool_(new EventLoopThreadPool(loop_,threadNum)), //新建事件循环线程池
started_(false),
acceptChannel_(new Channel(loop_)), //新建channel
port_(port),
listenFd_(socket_bind_listen(port_)) //绑定端口,初始化listenfd
{
acceptChannel_->setFd(listenFd_);
handle_for_sigpipe(); //在util中实现
if (setSocketNonBlocking(listenFd_) < 0) { //设置非阻塞监听
perror("set socket non block failed");
abort();
}
}
原来他已经创建好了acceptchannel、listenfd并且绑定了端口、设置了非阻塞监听。
回到上一个程序段,一系列的set操作将acceptchannel 对新连接的处理函数进行了配置,
并且loop_->addToPoller(acceptChannel_, 0); //将负责处理接受连接的channel 挂到epoll红黑树上去。
自此,主线程已经可以开始监听新连接事件了。
2.主线程处理连接事件
- 准备工作完成后,主线程就开始loop了:mainLoop.loop();
好,在主线程loop中有:
void EventLoop::loop() {
assert(!looping_); //确保线程中的eventloop未启动
assert(isInLoopThread()); //确保eventloop已绑定线程
looping_ = true;
quit_ = false;
//LOG_TRACE << "EventLoop " << this << " start looping";
std::vector<SP_Channel> ret; //SP即shared_ptr
while (!quit_) { //不断循环取出eventloop中epoll获得的事件
//cout << "doing" << endl;
ret.clear();
ret = poller_->poll(); //获得带有事件信息的channel指针数组
eventHandling_ = true;
for (auto &it : ret)
it->handleEvents(); //线程逐个处理,在poll中把epoll_wait拿到的事件都放在ret数组中channel对象的revent里,这里逐个拿出来处理
eventHandling_ = false;
doPendingFunctors();
poller_->handleExpired(); //主线程处理过期连接(定时器大根堆)
}
looping_ = false;
}
在while循环中,调用 ret = poller_->poll(); //将channel事件拷贝过来存进vector中,然后逐个处理。
- 我们细看他是怎么拿到事件的:
ret = poller_->poll(); 下一层是:
std::vector<SP_Channel> Epoll::poll() {
while (true) { //不停取,直到取到事件就退出
int event_count = epoll_wait(epollFd_, &*events_.begin(), events_.size(), EPOLLWAIT_TIME); //取到事件并放入events_数组中
if (event_count < 0)
perror("epoll wait error");
std::vector<SP_Channel> req_data = getEventsRequest(event_count);
if (req_data.size() > 0)
return req_data;
}
}
//其中
// 分发处理函数
std::vector<SP_Channel> Epoll::getEventsRequest(int events_num) {
std::vector<SP_Channel> req_data;
for (int i = 0; i < events_num; ++i) {
// 获取有事件产生的描述符
int fd = events_[i].data.fd;
SP_Channel cur_req = fd2chan_[fd]; //从数组中拿到当前channel
if (cur_req) {
cur_req->setRevents(events_[i].events); //revents 输出 , 此处将活跃事件赋值给channel对象的revents_变量
cur_req->setEvents(0);
// 加入线程池之前将Timer和request分离
//cur_req->seperateTimer();
req_data.push_back(cur_req);
}
else {
LOG << "SP cur_req is invalid";
}
}
return req_data;
}
首先epoll_wait拿到活跃事件,并放入数组events_中,(对于主线程,所有事件都是对listenfd的连接事件)然后调用getEventsRequest 拿到带有每个活跃事件信息的channel数组(数组中每个channel带着一个活跃事件)。
- 好,拿到活跃事件(装在channel)后,逐个处理
for (auto &it : ret)
it->handleEvents();
//线程逐个处理,在poll中把epoll_wait拿到的事件都放在ret数组中channel对象的r