使用智能指针,一方面因为纯虚类不能构造对象,另一方面减少对象拷贝




检查连接过期

改成多进程版本
在创建套接字之后,设置选项SO_REUSEPORT选项

子进程继承父进程的Listener(子进程也可以重新创建),每个子进程都是一个Reactor,内核会做负载均衡,父进程只等待连接,连接到来,内核会回负载均衡地把到来的连接分给子进程,子进程只关心父进程给自己fd的事件

核心原理是首先调用eventfd,创建子进程,父子进程拿到同一个文件描述符,子进程调用read,父进程不写,子进程就阻塞,父进程写入,子进程就被唤醒,通过这种机制就可以实现父进程通知子进程,连接到来了,并且把到来连接fd的值写入,子进程读到自然就知道要处理哪个fd的读写事件

flags如果是EFD_CLOEXEC,表明,如果发生程序替换,fd会被关闭,不会被新程序继承
如果设置了EFD_SEMAPHORE,每次读返回1,计数器-1,直到为0;如果不设置,就会一次性把值读出来,计数器清0
智能指针的拓展
因为Reactor里的_conns使用shared_ptr指向每一个连接,连接类里有字段指回Reactor,这个回指指针不能直接用shared_ptr



Reactor模式是一种事件驱动的编程模式,用于高效地处理并发IO操作,通过一个或多个事件循环(Event Loop)来监听和处理各种请求(如网络请求,定时器事件等),从而实现高效的并发处理,而无需为每个连接创建一个线程或进程
单进程Reactor:Redis
多进程Reactor:Nginx
下面这种直接把inbuffer+sockfd交给线程池,也就是Reactor进行epoll_wait,收到一个就绪事件就把fd交给从线程,从线程读取缓冲区,这样会有同步问题,一个fd可能就绪多次,分给多个线程,但是拿到早的数据线程可能晚处理完,这样写到outbuffer就会出现时序错误


下面主进程等待新连接,新连接到来,负载均衡向管道写,slaver进程检测到管道就绪,读取之后丢弃(写的这个值只是为了通知),回调acceptor,获取连接,把fd添加到自己的epoller,处理连接的就绪事件
主进程就通过管道负载均衡地告诉子进程连接来了

下面是多线程版本,线程池的队列存储收到的fd,问题在于,如果回调函数设置为从管道读取fd,那么也就是说从线程会等管道的事件就绪,但是读取fd肯定要放到自己的epoll,要等文件描述符的事件就绪,而等待队列要使用条件变量,那到底是在条件变量下等还是在epoll_wait下等

下面这种情况使用eventfd,子线程会把eventfd添加到epoll_wait,主线程把收到的fd放到队列,通过向eventfd写来通知从线程,从线程被唤醒,执行回调,读取fd放到epoll_wait
