• nginx事件模块 第七篇 创建连接


    微信公众号:郑尔多斯
    关注可了解更多的Nginx知识。任何问题或建议,请公众号留言;
    关注公众号,有趣有内涵的文章第一时间送达!

    事件驱动框架

    我们前面分析过,在ngx_event_process_init()中,会将每个监听端口的rev->handler设置为ngx_event_accept()函数,然后把对应的读事件加入到epoll驱动模块中。这样,在执行ngx_epoll_process_events()的时候,如果有新连接事件出现,那么就会调用ngx_event_accept()方法进行建立连接。
    Nginx为了解决惊群现象和负载均衡问题,使用了accept_mutex以及post事件处理机制,那么什么是post事件处理机制?
    所谓的post事件处理机制就是允许事件延后执行。Nginx设计了两个队列,一个是由被触发的监听连接的读事件构成的ngx_posted_accept_events队列,另一个是由普通读/写事件构成的ngx_posted_events队列。

    epoll_wait()产生的事件,分别存放到这两个队列中。让存放新连接事件的ngx_posted_accept_events队列优先执行,存放普通事件的ngx_posted_events队列最后执行,这样就可以保证建立连接的及时性,同时这也是解决惊群和负载均衡的关键所在。

    ngx_event_accept

      1void
    2ngx_event_accept(ngx_event_t *ev)
    3
    {
    4    socklen_t          socklen;
    5    ngx_err_t          err;
    6    ngx_log_t         *log;
    7    ngx_uint_t         level;
    8    ngx_socket_t       s;
    9    ngx_event_t       *rev, *wev;
    10    ngx_sockaddr_t     sa;
    11    ngx_listening_t   *ls;
    12    ngx_connection_t  *c, *lc;
    13    ngx_event_conf_t  *ecf;
    14#if (NGX_HAVE_ACCEPT4)
    15    static ngx_uint_t  use_accept4 = 1;
    16#endif
    17
    18    if (ev->timedout) {
    19        if (ngx_enable_accept_events((ngx_cycle_t *) ngx_cycle) != NGX_OK) {
    20            return;
    21        }
    22
    23        ev->timedout = 0;
    24    }
    25
    26    ecf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_event_core_module);
    27
    28    if (!(ngx_event_flags & NGX_USE_KQUEUE_EVENT)) {
    29        ev->available = ecf->multi_accept;
    30    }
    31
    32   // 事件关联的data,也即当前事件对应的connection
    33    lc = ev->data; 
    34   // connection对应的监听端口结构
    35    ls = lc->listening;
    36 // ready=0,表示当前事件没有就绪,因为我们已经消费了读事件
    37    ev->ready = 0;
    38
    39
    40
    41    do { 
    42        socklen = sizeof(ngx_sockaddr_t);
    43// 使用accept创建一个连接
    44        s = accept(lc->fd, &sa.sockaddr, &socklen);
    45        if (s == (ngx_socket_t-1) {
    46           /*accept()失败的时候处理逻辑,这里可以忽略*/
    47            return;
    48        }
    49
    50#if (NGX_STAT_STUB)
    51     /*这里是用于统计每个worker进程的统计数据,比如accept的数量等*/
    52        (void) ngx_atomic_fetch_add(ngx_stat_accepted, 1);
    53#endif
    54
    55        ngx_accept_disabled = ngx_cycle->connection_n / 8
    56                              - ngx_cycle->free_connection_n;
    57
    58        c = ngx_get_connection(s, ev->log);
    59
    60        if (c == NULL) {
    61           /*获取connection失败*/
    62            return;
    63        }
    64
    65        c->type = SOCK_STREAM;
    66
    67        c->pool = ngx_create_pool(ls->pool_size, ev->log);
    68        if (c->pool == NULL) {
    69            ngx_close_accepted_connection(c);
    70            return;
    71        }
    72
    73        if (socklen > (socklen_tsizeof(ngx_sockaddr_t)) {
    74            socklen = sizeof(ngx_sockaddr_t);
    75        }
    76
    77        c->sockaddr = ngx_palloc(c->pool, socklen);
    78        if (c->sockaddr == NULL) {
    79            ngx_close_accepted_connection(c);
    80            return;
    81        }
    82
    83        ngx_memcpy(c->sockaddr, &sa, socklen);
    84
    85        log = ngx_palloc(c->pool, sizeof(ngx_log_t));
    86        if (log == NULL) {
    87            ngx_close_accepted_connection(c);
    88            return;
    89        }
    90
    91        /* set a blocking mode for iocp and non-blocking mode for others */
    92
    93        if (ngx_inherited_nonblocking) {
    94            if (ngx_event_flags & NGX_USE_IOCP_EVENT) {
    95                /*这里是windows的处理逻辑*/
    96            }
    97
    98        } else {
    99            if (!(ngx_event_flags & NGX_USE_IOCP_EVENT)) {
    100                if (ngx_nonblocking(s) == -1) {
    101
    102                    ngx_close_accepted_connection(c);
    103                    return;
    104                }
    105            }
    106        }
    107
    108        *log = ls->log;
    109
    110        c->recv = ngx_recv;
    111        c->send = ngx_send;
    112        c->recv_chain = ngx_recv_chain;
    113        c->send_chain = ngx_send_chain;
    114
    115        c->log = log;
    116        c->pool->log = log;
    117
    118        c->socklen = socklen;
    119        c->listening = ls;
    120        c->local_sockaddr = ls->sockaddr;
    121        c->local_socklen = ls->socklen;
    122
    123#if (NGX_HAVE_UNIX_DOMAIN)
    124        if (c->sockaddr->sa_family == AF_UNIX) {
    125            c->tcp_nopush = NGX_TCP_NOPUSH_DISABLED;
    126            c->tcp_nodelay = NGX_TCP_NODELAY_DISABLED;
    127#if (NGX_SOLARIS)
    128            /* Solaris's sendfilev() supports AF_NCA, AF_INET, and AF_INET6 */
    129            c->sendfile = 0;
    130#endif
    131        }
    132#endif
    133
    134        rev = c->read;
    135        wev = c->write;
    136
    137        wev->ready = 1;
    138
    139        if (ngx_event_flags & NGX_USE_IOCP_EVENT) {
    140            rev->ready = 1;
    141        }
    142
    143        if (ev->deferred_accept) {
    144            rev->ready = 1;
    145#if (NGX_HAVE_KQUEUE || NGX_HAVE_EPOLLRDHUP)
    146            rev->available = 1;
    147#endif
    148        }
    149
    150        rev->log = log;
    151        wev->log = log;
    152
    153        c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);
    154
    155        if (ls->addr_ntop) {
    156            c->addr_text.data = ngx_pnalloc(c->pool, ls->addr_text_max_len);
    157            if (c->addr_text.data == NULL) {
    158                ngx_close_accepted_connection(c);
    159                return;
    160            }
    161
    162            c->addr_text.len = ngx_sock_ntop(c->sockaddr, c->socklen,
    163                                             c->addr_text.data,
    164                                             ls->addr_text_max_len, 0);
    165            if (c->addr_text.len == 0) {
    166                ngx_close_accepted_connection(c);
    167                return;
    168            }
    169        }
    170
    171
    172        if (ngx_add_conn && (ngx_event_flags & NGX_USE_EPOLL_EVENT) == 0) {
    173            if (ngx_add_conn(c) == NGX_ERROR) {
    174                ngx_close_accepted_connection(c);
    175                return;
    176            }
    177        }
    178
    179        log->data = NULL;
    180        log->handler = NULL;
    181
    182        ls->handler(c);
    183
    184    } while (ev->available);
    185}

    上面的代码我做了删减,把一些错误判断和非Linux平台代码删除,这个并不影响代码的功能和阅读,现在我们简单的分析一下这个源码:

    • 首先这个是一个do…while循环,循环的条件就是ev->available,该条件控制是否一次建立多个连接。
    • 循环体首先使用accept()函数创建一个连接
    • 设置ngx_accept_disabled变量,然后调用ngx_get_connection()获取一个连接。
    • 下面就是设置我们上一步获取的连接的各个属性。

    最重要的一步是最后的代码:
    ls->handler(c);我们前面知道,ls->handler其实就是ngx_http_init_connection,从这个函数开始,我们就进入了http初始化的过程了。

    multi_accept的作用

    Syntax: multi_accept on | off;
    Default: multi_accept off;
    Context: events

    If multi_accept is disabled, a worker process will accept one new connection at a time. Otherwise, a worker process will accept all new connections at a time.
    The directive is ignored if kqueue connection processing method is used, because it reports the number of new connections waiting to be accepted.
    中文翻译:
    如果multi_accept被禁止了,nginx一个工作进程只能同时接受一个新的连接。否则,一个工作进程可以同时接受所有的新连接。
    如果nginx使用kqueue连接方法,那么这条指令会被忽略,因为这个方法会报告在等待被接受的新连接的数量。

    如果启用了这个参数,那么当一个worker进程获取到accept_mutex之后,会一次尽可能的多的建立连接。


    喜欢本文的朋友们,欢迎长按下图关注订阅号郑尔多斯,更多精彩内容第一时间送达

    郑尔多斯郑尔多斯
  • 相关阅读:
    C# 与Sql server 获取数据和执行命令
    关于*.ashx 处理程序调试时 未能创建类型 错误
    winform 利用Http向服务器上传与下载文件
    CSS 使用absolute 是<div>居中
    C# int[,] 和 int[][]
    《Head First JavaScript》 学习笔记
    【单片机】关于头文件
    【单片机】【710】定时器
    【C#】委托
    【C#】关于接口的理解
  • 原文地址:https://www.cnblogs.com/zhengerduosi/p/10207816.html
Copyright © 2020-2023  润新知