结合Nginx工作流程理解Epoll机制和Reactor模型

结合Nginx工作流程理解Epoll机制和Reactor模型
结合Nginx工作流程理解Epoll机制和Reactor模型Nginx工作流程启动准备nginx启动创建一个Master进程。Master进程解析配置文件创建监听Socket。一个ip和端口号一个监听SocketMaster进程创建Worker进程。每个Worker进程在启动后创建自己的epoll实例来完成事件驱动。Worker进程采用异步非阻塞的事件驱动模型Epoll来管理成千上万个请求同时将继承到的所有监听Socket的读事件注册到自己的epoll实例中。epoll实例监听到读事件之后通知Worker进程处理。注意为了避免惊群效应现在的nginx采用的是锁机制或者SO_REUSEPORT机制确保一个监听Socket只由有一个Worker线程负责。惊群效应当监听Socket有新的连接请求进来时所有的Worker进程都会被唤醒但最终只会有一个Worker线程拿到请求。客户端连接请求客户端创建socket。客户端调用socket()在内核创建一个socket。发送连接请求。调用connect()内核为该socket分配本地端口并记录五元组随后发送SYN包进行三次握手。建立连接后客户端发送HTTP请求数据内核将数据从用户缓冲区拷贝到内核socket写缓冲区中。内核协议栈从写缓冲区取出数据封装成TCP段通过网卡发送到nginx所在服务器的网卡。内核:HTTP请求数据包通过网络到达Nginx所在服务器网卡。网卡将数据拷贝到内核的内存区域。内核通过数据包的目标ip和端口找到对应的监听socket。由内核进行TCP连接的三次握手建立连接。连接完成后 将请求放入到监听socket的全连接队列中。此时监听socket的“可读”事件被触发。Worker进程连接请求epoll监听到监听socket的可读事件后同时通知worker进程。当有空闲的worker进程时worker进程调用accept系统调用内核逐个获取监听socket的全连接队列中的连接并创建新的socket返回给worker进程。Worker进程为新的socket在自己的内存空间分配一块内存。并将新的socket的读事件注册到epoll实例中。数据到达当新的socket有数据进来时epoll实例会通知Worker进程。Worker进程会通过内核将内核Socket缓冲区的数据拷贝到用户空间。接着在用户空间解析数据判断数据是否完整数据完整通过配置文件找到后端服务器与它建立连接。建立连接后将请求数据包从用户空间拷贝到与网卡关联的内核Socket缓冲区。由网卡将缓冲区的数据发送到网络。请求数据包通过网络到达后端服务器的网卡。如果是HTTPS解析前还需要经过SSL层的解密。后端服务器后端服务器接收到请求数据后进行处理。通过相同的方式将响应数据发送到nginxnginx再发送给客户端。结合Nginx看epoll机制Worker线程通过事件循环主动调用一次epoll_wait方法epoll_wait方法会去查询当前线程的epoll实例的就绪队列中是否有等待处理的socket。如果有当前线程就直接去处理没有的话当前进程就会进入睡眠状态并将当前线程放到等待队列中。当网卡收到数据内核将数据从网卡拷贝到内核缓冲区中后通过解析数据的目标ip和端口号找到对应的Socket如果是没有创建连接的请求则是监听socket已经建立连接的则是连接socket。内核再去检查是哪个epoll实例在监听这个socket。找到之后将这个socket加入到epoll实例的就绪队列中。直到epoll实例的就绪队列中有数据内核就会唤醒等待队列中的进程由进程去处理。处理完就绪队列中的数据后再次进入循环。Reactor模型Reactor模型实现异步非阻塞的事件驱动模型也是同样的原理。首先Reactor在创建Epoll实例的时候会告诉内核我关心的是什么比如是接收请求接收缓冲区有数据还是发送请求发送缓冲区有数据。然后让内核根据类型去判断是否该发起通知了。Reactor能做什么取决于内核能通知什么本质是对内核数据的判断。