epoll+线程池高并发服务器模型在测试的时候产生bug。
bug1:一个客户端连接,服务器却添加了多次业务。 bug2:很多消费者线程被占用 ,导致消费者线程扩容。
1、客户端一次请求,服务端却添加多次业务?
采用epoll监听socket的网络事件,某一时刻serverfd事件就绪 ,serverfd就会变成就绪态socket,需要处理serverfd上的事件,处理就绪的serverfd。clientfd就绪同理。无论是serverfd还是clientfd,变成就绪态的socket,我们一定要处理完就绪socket,serverfd处理就绪建立连接完成,clientfd处理就绪读取所有数据后处理业务完成。
epoll是主线程(生产者线程)调用去监听,serverfd就绪,主线程负责去添加一个业务,业务不是生产者线程处理的,而是消费者线程处理,主线程添加完业务开始第二轮监听,主线程生产者负责监听就绪,普通线程消费者负责处理完就绪,但是从任务投递到取出任务再到任务处理完毕,这之间是有一定的空白期,可以理解为sockfd就绪处理不及时。导致了这个bug。
epoll可以选择两种监听模式去监听socket上的事件 第一种水平触发模式监听 默认的监听模式,所有socket默认都是水平触发监听模式,监听是epoll做的 ,但是可以对不同的sockfd设置不同的监听模式,算是一个socket属性。
水平触发:启动epoll监听,当socket产生就绪后,epoll会不停的检查就绪事件处理情况,如果使用者没有将就绪事件处理完毕,就无法进行新一轮监听。就绪事件未处理完毕,第二轮监听epoll_wait不会阻塞,epoll_wait立即返回 ,返回未处理完的就绪事件数量。
水平触发模式最大限度的保证每一轮就绪事件都被处理完毕。水平触发模式,使用者必须将每一轮的就绪事件处理好,才可以进行新一轮监听。
水平触发模式的优势: 可以协助督促使用者及时处理完所有sock事件,保证就绪事件处理的及时性和有效性。 水平触发模式弊端: 系统开销较大,epoll不停轮询检测事件处理情况,并反复向上层发送处理通知。
2、为什么消费者线程被占用? 水平触发模式导致epoll误判断,造成需要多次连接请求的假象,多次业务被多个消费者拿走,但是多个业务其实都是建立连接的。第一个消费者调用 accept连接成功,第二个消费者去连接就会阻塞,accept默认阻塞,消费者线程被占用,管理者就会去扩容。
第二种边缘触发模式监听 将所有被监听的sockfd改成边缘触发模式监听就可以解决我们的bug。
当第一轮监听,监听到sockfd就绪后,epoll只向使用者发送一次处理通知, 也仅有一次, 后续使用者是否处理就绪与epoll无关 , 随时都可以调用epoll_wait进行新一轮监听。
边缘触发模式的优势: epoll开销较小,因为监听到就绪只返回一次就绪,发送一次通知,不会持续检测就绪处理进度。 边缘触发模式的弊端: 因为边缘模式下的sockfd就绪,epoll只反馈一次通知, 需要使用者及时将事件或事件数据处理掉,否则事件处理不及时,可能会出现事件数据丢失问题。
|