面试题答案
一键面试- 新连接接入
- 使用epoll_create创建epoll实例。
- 在监听套接字上使用epoll_ctl将其添加到epoll实例中,监听EPOLLIN事件。
- 当监听到新连接时,调用accept接收新连接,并立即使用epoll_ctl将新连接的套接字加入到epoll实例中,同时设置合适的事件掩码(如EPOLLIN | EPOLLOUT | EPOLLET,ET模式下需要更小心处理读写),以便后续监控其读写事件。
- 已有连接的读写操作
- 读操作:
- 当epoll_wait检测到EPOLLIN事件时,使用recv等函数进行数据读取。为避免读操作阻塞,设置套接字为非阻塞模式。
- 循环读取数据,直到recv返回EAGAIN或EWOULDBLOCK错误,表明数据读取完毕或当前无数据可读。
- 将读取到的数据进行相应处理,如解析协议、业务逻辑处理等。
- 写操作:
- 当有数据需要发送时,先尝试使用send函数发送。同样,套接字需设置为非阻塞模式。
- 如果send返回EAGAIN或EWOULDBLOCK错误,说明当前发送缓冲区已满,将该连接的写事件(EPOLLOUT)加入到epoll实例中进行监听。
- 当epoll_wait检测到EPOLLOUT事件时,再次调用send函数发送剩余数据,直到数据全部发送完毕,然后将该连接的写事件从epoll实例中移除(可根据实际情况决定是否移除)。
- 读操作:
- 连接断开情况
- 当recv返回0时,表示对方关闭了连接,此时调用close关闭套接字,并使用epoll_ctl将该套接字从epoll实例中移除。
- 如果recv或send返回错误,且错误码表示连接异常(如ECONNRESET、EPIPE等),同样关闭套接字并从epoll实例中移除。
- 避免性能瓶颈
- 内存管理:
- 使用内存池技术来管理连接相关的内存,避免频繁的内存分配和释放操作,提高内存使用效率。
- 对于缓冲区的分配,根据业务需求合理设置大小,避免过大或过小导致的性能问题。
- 事件处理优化:
- 尽量减少epoll_wait回调函数中的处理逻辑,将复杂的业务逻辑放到单独的线程或进程中处理,避免阻塞epoll_wait循环。
- 对于ET模式,确保在每次事件触发时尽可能多地处理数据,避免事件反复触发导致的性能损耗。
- 资源复用:
- 复用已有的连接资源,如连接池技术,对于短连接业务可以避免频繁创建和销毁连接带来的开销。
- 负载均衡:
- 如果是分布式系统,可以使用负载均衡器将大量并发连接均匀分配到多个后端服务器上,避免单个服务器负载过高。
- 内存管理: