搜索
首页后端开发php教程四:深入Nginx之事件和连接 (之一)

Nginx 本质上是基于事件驱动的Web服务器,事件 处理框架所要解决的问题就是如何收集、管理、分发事件。
事件主要 以

  • 网络事件(TCP网络事件为主 )
  • 定时器事件

Nginx 定义一个 核心模块ngx_event_module,参考博客一深入理解Nginx的 模块化 ,全局观,Nginx在启动时会调用ngx_init_cycle方法 解析配置文件时,一旦 在nginx.conf中找到感兴趣的是”events {}”配置项,ngx_event_module模块开始工作了。在 核心模块ngx_event_module中的ngx_event_core_module模块定义了这个模块会使用哪种事件驱动机制以及如何管理 事件。

事件 模块是 一种新的模块类型,nginx_module_t表示Nginx模块接口,而针对每一种 不同类型的模块,都有一个结构体来描述这一类模块的通用接口,这个接口保存在ngx_module_t结构体的 ctx成员中。核心 模块的 通用接口是ngx_core_module_t,事件通用 接口 是 ngx_event_module_t结构 体:

<code><span>typedef</span><span>struct</span> {
<span>//位于文件 ngx_event.h</span><span>//事件模块 的名字</span>
    ngx_str_t              *name;
    <span>//在解析 配置前,这个回调 方法用于创建存储配置选项参数的结构体</span><span>void</span>                 *(*create_conf)(ngx_cycle_t *cycle);
    <span>// 在解析配置项完成 之后,init_conf方法会被调用,用以综合处理当前事件模块感兴趣的全部配置项</span><span>char</span>                 *(*init_conf)(ngx_cycle_t *cycle, <span>void</span> *conf);
    <span>// 对于事件驱动机制,每个事件模块需要实现10个抽象方法 </span>
    ngx_event_actions_t     actions;
} ngx_event_module_t;</code>

ngx_event_module_t中的actions成员是定义事件驱动模块的核心方法。

<code><span>typedef</span><span>struct</span> {
    <span>/* 添加事件方法,它将负责把1个感兴趣 事件添加到操作 系统提供的事件驱动机制 */</span>
    ngx_int_t  (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
    <span>/*删除事件方法,它将1个已经存在于事件驱动机制中的事件移除 */</span>
    ngx_int_t  (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
    <span>/*启用 1个 事件,目前事件框架不会调用 这个 方法,大部分事件驱动模块对于该方法的实现都是与上面的add方法完全一致的*/</span>
    ngx_int_t  (*enable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
    <span>/*禁用1个事件,目前事件框架不会 调用这个方法 ,大部分事件驱动器对于这个方法的实现与上面的del方法完全一致*/</span>
    ngx_int_t  (*disable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
    <span>/*向事件驱动机制中添加一个新的连接,这意味着连接上的读写事件都添加到事件驱动机制中*/</span>
    ngx_int_t  (*add_conn)(ngx_connection_t *c);
    <span>/*从事件驱动机制中移除一个连接的读写事件*/</span>
    ngx_int_t  (*del_conn)(ngx_connection_t *c, ngx_uint_t flags);
    <span>/*仅在多线程环境下会被调用,目前Nginx在产品环境下还不会以多线程方式运行*/</span>
    ngx_int_t  (*notify)(ngx_event_handler_pt handler);
    <span>/*在正常的工作循环中,将调用process_events方法来 处理事件*/</span>
    ngx_int_t  (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer,
                                 ngx_uint_t flags);
   <span>/*初始化事件驱动模块的方法*/</span>
    ngx_int_t  (*init)(ngx_cycle_t *cycle, ngx_msec_t timer);
    <span>// 退出事件驱动模块前调用的方法</span><span>void</span>       (*done)(ngx_cycle_t *cycle);
} ngx_event_actions_t;</code>

ngx_event_core_module 和9个事件驱动模块都必须ngx_module_t结构体的ctx成员 实现ngx_event_module_t 接口

<code><span>struct</span> ngx_event_s {
    <span>/*事件相关的对象,通常data指向ngx_connection_t连接对象。开启文件异步I/O 时,它可能会指向*/</span><span>void</span>            *data;
     <span>/* 标志位,标识事件可写,意味着对应的TCP连接可写,也即连接处于发送网络包状态 */</span><span>unsigned</span>         write:<span>1</span>;
     <span>/* 标志位,标识可建立新的连接,一般是在ngx_listening_t对应的读事件中标记 */</span><span>unsigned</span>         accept:<span>1</span>;
     <span>/*检测当前事件是否是过期的,它仅仅是给驱动模块使用的,而事件消费模块可以不用关心 */</span><span>/* used to detect the stale events in kqueue and epoll */</span><span>unsigned</span>         instance:<span>1</span>;

    <span>/*
     * the event was passed or would be passed to a kernel;
     * in aio mode - operation was posted.
     */</span><span>unsigned</span>         active:<span>1</span>;

    <span>unsigned</span>         disabled:<span>1</span>;

    <span>/* the ready event; in aio mode 0 means that no operation can be posted */</span><span>unsigned</span>         ready:<span>1</span>;

    <span>unsigned</span>         oneshot:<span>1</span>;

    <span>/* aio operation is complete */</span><span>unsigned</span>         complete:<span>1</span>;

    <span>unsigned</span>         eof:<span>1</span>;
    <span>unsigned</span>         error:<span>1</span>;

    <span>unsigned</span>         timedout:<span>1</span>;
    <span>unsigned</span>         timer_set:<span>1</span>;

    <span>unsigned</span>         delayed:<span>1</span>;

    <span>unsigned</span>         deferred_accept:<span>1</span>;

    <span>/* the pending eof reported by kqueue, epoll or in aio chain operation */</span><span>unsigned</span>         pending_eof:<span>1</span>;

    <span>unsigned</span>         posted:<span>1</span>;

    <span>unsigned</span>         closed:<span>1</span>;

    <span>/* to test on worker exit */</span><span>unsigned</span>         channel:<span>1</span>;
    <span>unsigned</span>         resolver:<span>1</span>;

    <span>unsigned</span>         cancelable:<span>1</span>;

<span>#if (NGX_WIN32)</span><span>/* setsockopt(SO_UPDATE_ACCEPT_CONTEXT) was successful */</span><span>unsigned</span>         accept_context_updated:<span>1</span>;
<span>#endif</span><span>#if (NGX_HAVE_KQUEUE)</span><span>unsigned</span>         kq_vnode:<span>1</span>;

    <span>/* the pending errno reported by kqueue */</span><span>int</span>              kq_errno;
<span>#endif</span><span>/*
     * kqueue only:
     *   accept:     number of sockets that wait to be accepted
     *   read:       bytes to read when event is ready
     *               or lowat when event is set with NGX_LOWAT_EVENT flag
     *   write:      available space in buffer when event is ready
     *               or lowat when event is set with NGX_LOWAT_EVENT flag
     *
     * epoll with EPOLLRDHUP:
     *   accept:     1 if accept many, 0 otherwise
     *   read:       1 if there can be data to read, 0 otherwise
     *
     * iocp: TODO
     *
     * otherwise:
     *   accept:     1 if accept many, 0 otherwise
     */</span><span>#if (NGX_HAVE_KQUEUE) || (NGX_HAVE_IOCP)</span><span>int</span>              available;
<span>#else</span><span>unsigned</span>         available:<span>1</span>;
<span>#endif</span>    ngx_event_handler_pt  handler;


<span>#if (NGX_HAVE_IOCP)</span>
    ngx_event_ovlp_t ovlp;
<span>#endif</span>    ngx_uint_t       index;

    ngx_log_t       *<span>log</span>;

    ngx_rbtree_node_t   timer;

    <span>/* the posted queue */</span>
    ngx_queue_t      <span>queue</span>;

<span>#if 0</span><span>/* the threads support */</span><span>/*
     * the event thread context, we store it here
     * if $(CC) does not understand __thread declaration
     * and pthread_getspecific() is too costly
     */</span><span>void</span>            *thr_ctx;

<span>#if (NGX_EVENT_T_PADDING)</span><span>/* event should not cross cache line in SMP */</span>    uint32_t         padding[NGX_EVENT_T_PADDING];
<span>#endif</span><span>#endif</span>
};


<span>#if (NGX_HAVE_FILE_AIO)</span><span>struct</span> ngx_event_aio_s {
    <span>void</span>                      *data;
    ngx_event_handler_pt       handler;
    ngx_file_t                *file;

<span>#if (NGX_HAVE_AIO_SENDFILE)</span>
    ssize_t                  (*preload_handler)(ngx_buf_t *file);
<span>#endif</span>    ngx_fd_t                   fd;

<span>#if (NGX_HAVE_EVENTFD)</span>
    int64_t                    res;
<span>#endif</span><span>#if !(NGX_HAVE_EVENTFD) || (NGX_TEST_BUILD_EPOLL)</span>
    ngx_err_t                  err;
    size_t                     nbytes;
<span>#endif</span>    ngx_aiocb_t                aiocb;
    ngx_event_t                event;
};

<span>#endif</span></code>

Nginx的连接

 被动连接

这种连接是指 客户端发起的,服务器被动接受的连接

<code><span>//在 文件ngx_connection.h中</span><span>struct</span> ngx_connection_s {
  <span>/*  连接未使用时,data成员用于充当连接池中空闲链表中的next指针。当连接被使用时,data的意义由使用它的Nginx模块而定。在HTTP模块中,data指向ngx_http_request_t请求*/</span><span>void</span>               *data;
    <span>// 连接对应的读事件 </span>
    ngx_event_t        *read;
    <span>// 连接对应的写事件 </span>
    ngx_event_t        *write;
    <span>// 套接字句柄 </span>
    ngx_socket_t        fd;
    <span>//直接接收网络字符流的方法</span>
    ngx_recv_pt         recv;
    <span>// 直接发送网络字符流的办法</span>
    ngx_send_pt         send;
    <span>// 以ngx_chain_t链表为 参数来 接收 网络 字符流的方法 </span>
    ngx_recv_chain_pt   recv_chain;
    <span>// 以ngx_chain_t链表为 参数来 发送 网络 字符流的方法 </span>
    ngx_send_chain_pt   send_chain;
    <span>/*这个连接对应的ngx_listening_t监听对象,此连接由listening监听端口的事件建立*/</span>
    ngx_listening_t    *listening;
    <span>//这个连接上已经发送出去的字节数</span>
    off_t               sent;
    <span>// 可以记录日志的ngx_log_t对象</span>
    ngx_log_t          *<span>log</span>;
    <span>/* 内存池。一般在accept一个新连接时,会创建一个 内存池,而在这个 连接结束时会销毁内存池。所有的ngx_connectionn_t结构 体都是预分配,这个内存池的大小将由上面的listening 监听对象中的 pool_size成员决定*/</span>
    ngx_pool_t         *pool;

    <span>int</span>                 type;
    <span>// 连接客户端的sockaddr结构体</span><span>struct</span> sockaddr    *sockaddr;
    <span>// 连接 sockaddr结构体的 长度</span>
    socklen_t           socklen;
    <span>// 连接客户端字符串形式的IP地址</span>
    ngx_str_t           addr_text;

    ngx_str_t           proxy_protocol_addr;

    in_port_t           proxy_protocol_port;

<span>#if (NGX_SSL)</span>
    ngx_ssl_connection_t  *ssl;
<span>#endif</span><span>/*本机监听端口 对应 的sockaddr结构 体 ,也就是listening监听对象中的sock
    addr成员*/</span><span>struct</span> sockaddr    *local_sockaddr;
    socklen_t           local_socklen;
     <span>/*用于接收、缓存客户端 发来的字节流,每个事件消费模块可自由决定从连接池中分配多大空间给 buffer这个 缓存字段*/</span>
    ngx_buf_t          *buffer;

    ngx_queue_t         <span>queue</span>;
    <span>// 连接使用次数。ngx_connection_t结构体每次建立一条来自客户端的连接,或者主动向后端服务器发起连接时,number都会加1*/</span>
    ngx_atomic_uint_t   number;
    <span>// 处理 请求次数</span>
    ngx_uint_t          requests;

    <span>unsigned</span>            buffered:<span>8</span>;

    <span>unsigned</span>            log_error:<span>3</span>;     <span>/* ngx_connection_log_error_e */</span><span>unsigned</span>            timedout:<span>1</span>;
    <span>unsigned</span>            error:<span>1</span>;
    <span>unsigned</span>            destroyed:<span>1</span>;

    <span>unsigned</span>            idle:<span>1</span>;
    <span>unsigned</span>            reusable:<span>1</span>;
    <span>unsigned</span>            close:<span>1</span>;
    <span>unsigned</span>            shared:<span>1</span>;

    <span>unsigned</span>            sendfile:<span>1</span>;
    <span>unsigned</span>            sndlowat:<span>1</span>;
    <span>unsigned</span>            tcp_nodelay:<span>2</span>;   <span>/* ngx_connection_tcp_nodelay_e */</span><span>unsigned</span>            tcp_nopush:<span>2</span>;    <span>/* ngx_connection_tcp_nopush_e */</span><span>unsigned</span>            need_last_buf:<span>1</span>;

<span>#if (NGX_HAVE_IOCP)</span><span>unsigned</span>            accept_context_updated:<span>1</span>;
<span>#endif</span><span>#if (NGX_HAVE_AIO_SENDFILE)</span><span>unsigned</span>            busy_count:<span>2</span>;
<span>#endif</span><span>#if (NGX_THREADS)</span>
    ngx_thread_task_t  *sendfile_task;
<span>#endif</span>
};</code>

主动连接

作为Web服务器,Nginx也需要向其他服务器发起连接,使用ngx_peer_connection_t结构体来表示 主动连接,一个待处理连接的许多特性在 被动连接ngx_connection_t中都被定义过,因此ngx_peer_connection_t结构体中引用了ngx_connection_t这个结构体。

<code><span>// 当使用长连接与上游服务器通信时,可通过该方法由连接池获取 一个新的连接</span><span>typedef</span> ngx_int_t (*ngx_event_get_peer_pt)(ngx_peer_connection_t *pc,
    <span>void</span> *data);
<span>// 当 使用长连接与上游服务器通信时,通过该方法 将使用完毕 的连接释放给连接池</span><span>typedef</span><span>void</span> (*ngx_event_free_peer_pt)(ngx_peer_connection_t *pc, <span>void</span> *data,
    ngx_uint_t state);
<span>struct</span> ngx_peer_connection_s {
    <span>/*一个主动连接实际 上也需要ngx_connection_t结构体的大部分成员,并且处于重用的考虑 而定义 了connecion*/</span>
    ngx_connection_t                *connection;
    <span>// 远端服务器的socketaddr</span><span>struct</span> sockaddr                 *sockaddr;
    <span>// sockaddr地址长度</span>
    socklen_t                        socklen;
    <span>// 远端服务器的名称 </span>
    ngx_str_t                       *name;
    <span>// 表示在连接 一个 远端服务器,当前连接出现 异常失败后可以重试的次数,也就是允许的最多失败的次数</span>
    ngx_uint_t                       tries;
    ngx_msec_t                       start_time;

    ngx_event_get_peer_pt            get;
    ngx_event_free_peer_pt           <span>free</span>;
    <span>void</span>                            *data;

<span>#if (NGX_SSL)</span>
    ngx_event_set_peer_session_pt    set_session;
    ngx_event_save_peer_session_pt   save_session;
<span>#endif</span><span>// 本机地址信息 </span>
    ngx_addr_t                      *local;

    <span>int</span>                              type;
    <span>// 套接字的接收缓冲区大小</span><span>int</span>                              rcvbuf;
    <span>// 记录日志的ngx_log_t对象</span>
    ngx_log_t                       *<span>log</span>;

    <span>unsigned</span>                         cached:<span>1</span>;
<span>#if (NGX_HAVE_TRANSPARENT_PROXY)</span><span>unsigned</span>                         transparent:<span>1</span>;
<span>#endif</span><span>/* ngx_connection_log_error_e */</span><span>unsigned</span>                         log_error:<span>2</span>;
};</code>
').addClass('pre-numbering').hide(); $(this).addClass('has-numbering').parent().append($numbering); for (i = 1; i ').text(i)); }; $numbering.fadeIn(1700); }); });

以上就介绍了 四:深入Nginx之事件和连接 (之一),包括了方面的内容,希望对PHP教程有兴趣的朋友有所帮助。

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
使用数据库存储会话的优点是什么?使用数据库存储会话的优点是什么?Apr 24, 2025 am 12:16 AM

使用数据库存储会话的主要优势包括持久性、可扩展性和安全性。1.持久性:即使服务器重启,会话数据也能保持不变。2.可扩展性:适用于分布式系统,确保会话数据在多服务器间同步。3.安全性:数据库提供加密存储,保护敏感信息。

您如何在PHP中实现自定义会话处理?您如何在PHP中实现自定义会话处理?Apr 24, 2025 am 12:16 AM

在PHP中实现自定义会话处理可以通过实现SessionHandlerInterface接口来完成。具体步骤包括:1)创建实现SessionHandlerInterface的类,如CustomSessionHandler;2)重写接口中的方法(如open,close,read,write,destroy,gc)来定义会话数据的生命周期和存储方式;3)在PHP脚本中注册自定义会话处理器并启动会话。这样可以将数据存储在MySQL、Redis等介质中,提升性能、安全性和可扩展性。

什么是会话ID?什么是会话ID?Apr 24, 2025 am 12:13 AM

SessionID是网络应用程序中用来跟踪用户会话状态的机制。1.它是一个随机生成的字符串,用于在用户与服务器之间的多次交互中保持用户的身份信息。2.服务器生成并通过cookie或URL参数发送给客户端,帮助在用户的多次请求中识别和关联这些请求。3.生成通常使用随机算法保证唯一性和不可预测性。4.在实际开发中,可以使用内存数据库如Redis来存储session数据,提升性能和安全性。

您如何在无状态环境(例如API)中处理会议?您如何在无状态环境(例如API)中处理会议?Apr 24, 2025 am 12:12 AM

在无状态环境如API中管理会话可以通过使用JWT或cookies来实现。1.JWT适合无状态和可扩展性,但大数据时体积大。2.Cookies更传统且易实现,但需谨慎配置以确保安全性。

您如何防止与会议有关的跨站点脚本(XSS)攻击?您如何防止与会议有关的跨站点脚本(XSS)攻击?Apr 23, 2025 am 12:16 AM

要保护应用免受与会话相关的XSS攻击,需采取以下措施:1.设置HttpOnly和Secure标志保护会话cookie。2.对所有用户输入进行输出编码。3.实施内容安全策略(CSP)限制脚本来源。通过这些策略,可以有效防护会话相关的XSS攻击,确保用户数据安全。

您如何优化PHP会话性能?您如何优化PHP会话性能?Apr 23, 2025 am 12:13 AM

优化PHP会话性能的方法包括:1.延迟会话启动,2.使用数据库存储会话,3.压缩会话数据,4.管理会话生命周期,5.实现会话共享。这些策略能显着提升应用在高并发环境下的效率。

什么是session.gc_maxlifetime配置设置?什么是session.gc_maxlifetime配置设置?Apr 23, 2025 am 12:10 AM

thesession.gc_maxlifetimesettinginphpdeterminesthelifespanofsessiondata,setInSeconds.1)它'sconfiguredinphp.iniorviaini_set().2)abalanceIsiseededeedeedeedeedeedeedto to to avoidperformance andununununununexpectedLogOgouts.3)

您如何在PHP中配置会话名?您如何在PHP中配置会话名?Apr 23, 2025 am 12:08 AM

在PHP中,可以使用session_name()函数配置会话名称。具体步骤如下:1.使用session_name()函数设置会话名称,例如session_name("my_session")。2.在设置会话名称后,调用session_start()启动会话。配置会话名称可以避免多应用间的会话数据冲突,并增强安全性,但需注意会话名称的唯一性、安全性、长度和设置时机。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

SublimeText3 英文版

SublimeText3 英文版

推荐:为Win版本,支持代码提示!

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版

WebStorm Mac版

WebStorm Mac版

好用的JavaScript开发工具

mPDF

mPDF

mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),