Nginx源码阅读-网络连接的管理

概述

通过分析Nginx源码中ngx_event_connect.c 和 ngx_event_connect.h了解Nginx是如何通过异步非阻塞方式去处理网络连接,提高服务器并发能力和性能。

整体逻辑梳理

  • 连接初始化:创建套接字、设置非阻塞模式、获取对象等
  • 事件处理:设置读写事件的处理函数,将连接添加到事件驱动模块中
  • 连接处理:调用ngx_connect_peer
  • 错误处理:连接失败的时候,清理资源同时记录日志

 ngx_event_connect.h

主要定义管理连接所用的数据结构以及相应函数

  • 结构体 ngx_event_connect_t :用于保存连接信息,其中包括目标地址、事件处理函数、数据、超时信息以及日志等。
  • 函数声明
    • ngx_event_connect_peer():客户端向服务端发起连接后,服务端通过该函数对客户端发起的连接进行集中管理和初始化
    • ngx_event_connect_peer_failed():处理连接失败的函数
/*
 * Copyright (C) Igor Sysoev
 * Copyright (C) Nginx, Inc.
 */


#ifndef _NGX_EVENT_CONNECT_H_INCLUDED_
#define _NGX_EVENT_CONNECT_H_INCLUDED_


#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>


typedef struct {
    ngx_peer_address_t         *peer;
    ngx_event_handler_pt        handler;
    void                       *data;
    ngx_msec_t                  timeout;

    unsigned                    rcvbuf:1;
    unsigned                    sndbuf:1;
    unsigned                    cached:1;

    ngx_log_t                  *log;

    ngx_pool_t                 *pool;
    ngx_resolver_ctx_t         *ctx;
} ngx_event_connect_t;


ngx_int_t ngx_event_connect_peer(ngx_event_connect_t *connect);
void ngx_event_connect_peer_failed(ngx_event_connect_t *connect);


#endif /* _NGX_EVENT_CONNECT_H_INCLUDED_ */

ngx_event_connect.c

处理函数分析

  • ngx_event_connect_peer
    • 创建套接字
    • 将该套接字设置成非阻塞模式(目的是提高并发性能)
    • 获取并初始化连接对象:从连接池中获取一个连接对象,同时设置其各种属性
    • 设置事件日志:处理这些事件的时候记录到日志
    • 添加到事件驱动模型中,例如epoll中
    • 获取并连接到远程主机
  • ngx_event_connect_peer_failed
    • 获取连接对象
    • 如果套接字存在,则关闭连接
    • 将连接对象设置为空
/*
 * Copyright (C) Igor Sysoev
 * Copyright (C) Nginx, Inc.
 */

#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#include <ngx_event_connect.h>

ngx_int_t
ngx_event_connect_peer(ngx_event_connect_t *connect)
{
    int                rc;
    ngx_socket_t       s;
    ngx_event_t       *rev, *wev;
    ngx_connection_t  *c;

    s = ngx_socket(connect->peer->sockaddr->sa_family, SOCK_STREAM, 0);
    if (s == (ngx_socket_t) -1) {
        ngx_log_error(NGX_LOG_ALERT, connect->log, ngx_socket_errno,
                      ngx_socket_n " failed");
        return NGX_ERROR;
    }

    if (ngx_nonblocking(s) == -1) {
        ngx_log_error(NGX_LOG_ALERT, connect->log, ngx_socket_errno,
                      ngx_nonblocking_n " failed");
        if (ngx_close_socket(s) == -1) {
            ngx_log_error(NGX_LOG_ALERT, connect->log, ngx_socket_errno,
                          ngx_close_socket_n " failed");
        }
        return NGX_ERROR;
    }

    c = ngx_get_connection(s, connect->log);
    if (c == NULL) {
        if (ngx_close_socket(s) == -1) {
            ngx_log_error(NGX_LOG_ALERT, connect->log, ngx_socket_errno,
                          ngx_close_socket_n " failed");
        }
        return NGX_ERROR;
    }

    c->type = SOCK_STREAM;
    c->log = connect->log;
    c->pool = connect->pool;

    c->sockaddr = connect->peer->sockaddr;
    c->socklen = connect->peer->socklen;
    c->addr_text = connect->peer->name;

    c->read->handler = connect->handler;
    c->write->handler = connect->handler;

    c->data = connect->data;
    connect->data = c;

    rev = c->read;
    wev = c->write;

    rev->log = connect->log;
    wev->log = connect->log;

    if (ngx_add_conn) {
        if (ngx_add_conn(c) == NGX_ERROR) {
            goto failed;
        }

    } else {
        if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
            goto failed;
        }

        if (ngx_add_event(wev, NGX_WRITE_EVENT, NGX_CLEAR_EVENT)
            == NGX_ERROR)
        {
            goto failed;
        }
    }

    rc = connect->peer->get(connect->peer, connect);
    if (rc == NGX_ERROR) {
        goto failed;
    }

    if (rc == NGX_DECLINED) {
        ngx_log_error(NGX_LOG_ALERT, connect->log, 0,
                      "no live upstreams");
        goto failed;
    }

    rc = ngx_connect_peer(connect);

    if (rc == NGX_ERROR) {
        goto failed;
    }

    if (rc == NGX_AGAIN) {
        return NGX_AGAIN;
    }

    return NGX_OK;

failed:

    ngx_close_connection(c);

    return NGX_ERROR;
}


void
ngx_event_connect_peer_failed(ngx_event_connect_t *connect)
{
    ngx_connection_t  *c;

    c = connect->data;

    if (c->fd != (ngx_socket_t) -1) {
        ngx_close_connection(c);
    }

    connect->data = NULL;
}

 连接池

上述中从连接池中获取连接对象,使用的是一种池化技术。Nginx处理连接中,并不是每一次都新创建连接对象,而是预先创建好相应的对象。目的是提高资源利用率和系统性能。

类似于工厂预先制作好相应的工作服,工人来打工时发一套,离职的时候收回工作服给下一个工人使用。

工作原理分析

  • 预先创建连接对象
    • Nginx启动后,预先创建一组连接对象,这些对象存储在连接池中,等待调用
    • 连接对象中包含着连接所需要使用的对应资源
  • 复用连接对象
    • 建立新连接的时候,Nginx从连接池中获取一个空闲的连接对象
    • 目的是减少内存分配和释放的开销,提高连接建立的效率
  • 释放和回收连接对象
    • 连接关闭的时候,连接对象会被重置信息然后放回连接池,供下一次连接使用
  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值