NGINX 开发指南 已翻译 100%

baishancloud 投递于 2017/06/02 16:48 (共 58 段, 翻译完成于 06-06)
阅读 13996
收藏 558
35
加载中

本文档是nginx官方文档“Developer Guide”(https://nginx.org/en/docs/dev/development_guide.html)的中文版本,由白山云科技(http://www.baishancloud.com)NGINX开发团队负责翻译。官方文档是HTML页面发布的,我们翻译的时候转成了Markdown,以方便编辑。同时也一并保留了英文的Markdown版本:https://github.com/baishancloud/nginx-development-guide/blob/master/en.md。希望此中文版文档能为广大的nginx以及开源爱好者提供入门指导,开发出优秀的nginx模块,回馈社区。本文的官方版本并没有全部完成,依然处于活跃更新的状态,中文版本会持续保持跟踪并持续更新。

baishancloud
baishancloud
翻译于 2017/06/06 16:58
6

简介

代码结构

  • auto — 编译脚本

  • src

    • unix

    • win32

    • modules — 其他HTTP模块

    • v2 — HTTP/2模块

    • modules — 具体事件机制模块:epoll,kqueue,select等

    • core — 基础数据结构和函数 — 字符串,数组,日志,内存池等

    • event — 事件机制核心模块

    • http — HTTP核心模块和公共代码

    • mail — 邮件协议模块

    • os — 平台相关代码

    • stream — 流模块

    baishancloud
    baishancloud
    翻译于 2017/06/06 17:00
    3

    头文件

    每个nginx文件都应该在开头包含如下两个头文件:

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

    除此之外,HTTP相关的代码还要包含:

    #include <ngx_http.h>

    邮件模块的代码应该包含:

    #include <ngx_mail.h>

    Stream模块的代码应该包含:

    #include <ngx_stream.h>
    baishancloud
    baishancloud
    翻译于 2017/06/06 17:01
    2

    整数

    一般情况下,nginx代码使用如下两个整数类型:ngx_int_t 和 ngx_uint_t,分别用typedef定义成了intptr_t 和 uintptr_t

    baishancloud
    baishancloud
    翻译于 2017/06/06 17:01
    0

    常用返回值

    nginx中的大多数函数使用如下类型的返回值:

    • NGX_OK — 处理成功

    • NGX_ERROR — 处理失败

    • NGX_AGAIN — 处理未完成,函数需要被再次调用

    • NGX_DECLINED — 处理被拒绝,例如相关功能在配置文件中被关闭。不要将此当成错误。

    • NGX_BUSY — 资源不可用

    • NGX_DONE — 处理完成或者在他处继续处理。也可以作为处理成功使用。

    • NGX_ABORT — 函数终止。也可以作为处理出错的返回值。

    baishancloud
    baishancloud
    翻译于 2017/06/06 17:01
    0

    错误处理

    为了获取最近一次系统错误码,nginx提供了ngx_errno宏。该宏被映射到了POSIX平台的errno变量上,而在Windows平台中,则变为对GetLastError()的函数调用。为了获取最近一次socket错误码,nginx提供了ngx_socket_errno宏。同样,在POSIX平台上该宏被映射为errno变量,而在Windows环境中则是对WSAGetLastError()进行调用。考虑到对性能的影响,ngx_errno和ngx_socket_errno不应该被连续访问。如果有连续、频繁访问的需要,则应该将错误码的值存储到类型为ngx_err_t的本地变量中,然后使用本地变量进行访问。如果需要设置错误码,可以使用ngx_set_errno(errno)和ngx_set_socket_errno(errno)这两个宏。

    ngx_errno和ngx_socket_errno变量可以在调用日志相关函数ngx_log_error()和ngx_log_debugX()的时候使用,这样具体的错误文本就会被添加到日志输出中。

    一个使用ngx_errno的例子:

    void
    ngx_my_kill(ngx_pid_t pid, ngx_log_t *log, int signo)
    {
        ngx_err_t  err;
    
        if (kill(pid, signo) == -1) {
            err = ngx_errno;
    
            ngx_log_error(NGX_LOG_ALERT, log, err, "kill(%P, %d) failed", pid, signo);
    
            if (err == NGX_ESRCH) {
                return 2;
            }
    
            return 1;
        }
    
        return 0;
    }

    baishancloud
    baishancloud
    翻译于 2017/06/06 17:02
    0

    字符串

    概述

    nginx使用无符号的char类型指针来表示C字符串:u_char *。

    nginx字符串类型ngx_str_t的定义如下所示:

    typedef struct {
        size_t      len;
        u_char     *data;
    } ngx_str_t;

    结构体成员len存放字符串的长度,成员data指向字符串本身数据。在ngx_str_t中存放的字符串,对于超出len长度的部分可以是NULL结尾('\0'——译者注),也可以不是。在大多数情况是不以NULL结尾的。然而,在nginx的某些代码中(例如解析配置的时候),ngx_str_t中的字符串是以NULL结尾的,这种情况会使得字符串比较变得更加简单,也使得使用系统调用的时候更加容易。

    nginx提供了一系列关于字符串处理的函数。它们在src/core/ngx_string.h文件中定义。其中的一部分就是对C库中字符串函数的封装:

    • ngx_strcmp()

    • ngx_strncmp()

    • ngx_strstr()

    • ngx_strlen()

    • ngx_strchr()

    • ngx_memcmp()

    • ngx_memset()

    • ngx_memcpy()

    • ngx_memmove()

    还有一些nginx特有的字符串函数:

    • ngx_memzero() 内存清0

    • ngx_cpymem() 和ngx_memcpy()行为类似,不同的是该函数返回的是copy后的最终目的地址,这在需要连续拼接多个字符串的场景下很方便。

    • ngx_movemem() 和ngx_memmove()的行为类似,不同的是该函数返回的是move后的最终目的地址。

    • ngx_strlchr() 在字符串中查找一个特定字符,字符串由两个指针界定。

    最后是一些大小写转换和字符串比较的函数:

    • ngx_tolower()

    • ngx_toupper()

    • ngx_strlow()

    • ngx_strcasecmp()

    • ngx_strncasecmp()

    baishancloud
    baishancloud
    翻译于 2017/06/06 17:02
    0

    格式化

    nginx提供了一些格式化字符串的函数。以下这些函数支持nginx特有的类型:

    • ngx_sprintf(buf, fmt, ...)

    • ngx_snprintf(buf, max, fmt, ...)

    • ngx_slpintf(buf, last, fmt, ...)

    • ngx_vslprint(buf, last, fmt, args)

    • ngx_vsnprint(buf, max, fmt, args)

    这些函数支持的全部格式化选项定义在src/core/ngx_string.c文件中,以下是其中的一部分:

    %O — off_t
    %T — time_t
    %z — size_t
    %i — ngx_int_t
    %p — void *
    %V — ngx_str_t *
    %s — u_char * (null-terminated)
    %*s — size_t + u_char *

    'u'修饰符将类型指明为无符号,'X'和'x'则将输出转换为16进制。

    例如:

    u_char     buf[NGX_INT_T_LEN];
    size_t     len;
    ngx_int_t  n;
    
    /* set n here */
    
    len = ngx_sprintf(buf, "%ui", n) — buf;
    baishancloud
    baishancloud
    翻译于 2017/06/06 17:03
    0

    数值转换

    nginx实现了若干用于数值转换的函数:

    • ngx_atoi(line, n) — 将一个指定长度的字符串转换为一个正整数,类型为ngx_int_t。出错返回NGX_ERROR。

    • ngx_atosz(line, n) — 同上,转换类型为ssize_t

    • ngx_atoof(line, n) — 同上,转换类型为off_t

    • ngx_atotm(line, n) — 同上,转换类型为time_t

    • ngx_atofp(line, n, point) — 将一个固定长度的定点小数字符串转换为ngx_int_t类型的正整数。转换结果会左移point指定的10进制位数。字符串中的定点小数不能含有多过point参数指定的小数位。出错返回NGX_ERROR。举例:ngx_atofp("10.5", 4, 2) 返回1050

    • ngx_hextoi(line, n) — 将表示16进制正整数的字符串转换为ngx_int_t类型的整数。出错返回NGX_ERROR。


    baishancloud
    baishancloud
    翻译于 2017/06/06 17:03
    0

    正则表达式

    nginx中的正则表达式接口是对PCRE库的封装。相关的头文件是src/core/ngx_regex.h。

    要使用正则表达式进行字符串匹配,首先需要对正则表达式进行编译,这通常是在配置解析阶段处理的。需要注意的是,因为PCRE的支持是可选的,因此所有使用正则相关接口的代码都需要用NGX_PCRE括起来:

    #if (NGX_PCRE)
    ngx_regex_t          *re;
    ngx_regex_compile_t   rc;
    
    u_char                errstr[NGX_MAX_CONF_ERRSTR];
    
    ngx_str_t  value = ngx_string("message (\\d\\d\\d).*Codeword is '(?<cw>\\w+)'");
    
    ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
    
    rc.pattern = value;
    rc.pool = cf->pool;
    rc.err.len = NGX_MAX_CONF_ERRSTR;
    rc.err.data = errstr;
    /* rc.options are passed as is to pcre_compile() */
    
    if (ngx_regex_compile(&rc) != NGX_OK) {
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc.err);
        return NGX_CONF_ERROR;
    }
    
    re = rc.regex;
    #endif

    编译成功之后,结构体ngx_regex_compile_t的captures和named_captures成员分别会被填上正则表达式中全部以及命名捕获的数量。

    然后,编译过的正则表达式就可以用来进行字符串匹配:

    ngx_int_t  n;
    int        captures[(1 + rc.captures) * 3];
    
    ngx_str_t input = ngx_string("This is message 123. Codeword is 'foobar'.");
    
    n = ngx_regex_exec(re, &input, captures, (1 + rc.captures) * 3);
    if (n >= 0) {
        /* string matches expression */
    
    } else if (n == NGX_REGEX_NO_MATCHED) {
        /* no match was found */
    
    } else {
        /* some error */
        ngx_log_error(NGX_LOG_ALERT, log, 0, ngx_regex_exec_n " failed: %i", n);
    }

    ngx_regex_exec()的参数有:编译了的正则表达式re,待匹配的字符串s,可选的用于存放发现的捕获和其大小的整数数组。捕获数组的大小必须是3的倍数,这是PCRE库的API要求的。在上面例子中,该数组的大小是通过总捕获数加上字符串自身来计算得出的。

    现在,如果成功匹配,则可以对捕获进行访问:

    u_char     *p;
    size_t      size;
    ngx_str_t   name, value;
    
    /* all captures */
    for (i = 0; i < n * 2; i += 2) {
        value.data = input.data + captures[i];
        value.len = captures[i + 1] — captures[i];
    }
    
    /* accessing named captures */
    
    size = rc.name_size;
    p = rc.names;
    
    for (i = 0; i < rc.named_captures; i++, p += size) {
    
        /* capture name */
        name.data = &p[2];
        name.len = ngx_strlen(name.data);
    
        n = 2 * ((p[0] << 8) + p[1]);
    
        /* captured value */
        value.data = &input.data[captures[n]];
        value.len = captures[n + 1] — captures[n];
    }

    ngx_regex_exec_array()函数接受ngx_regex_elt_t元素的数组(其实就是多个编译好的正则表达式以及对应的名字),一个待匹配字符串以及一个log。该函数会对待匹配字符串逐一应用数组中的正则表达式,直到匹配成功或者无一匹配。存在成功的匹配则返回NGX_OK,否则返回NGX_DECLINED,出错返回NGX_ERROR。

    baishancloud
    baishancloud
    翻译于 2017/06/06 17:03
    0
    本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。
    我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
    加载中

    评论(25)

    qwfys
    qwfys
    +1
    533446388
    533446388
    mark
    baishancloud
    baishancloud

    引用来自“月耳”的评论

    翻译的很好,不过英文出处有些地方错了,本可以趁着翻译改正过来。
    1. 当nginx需要将请求转给其他服务器进行处理时,它会调用配置好的负载均衡算法来选择一个地址,并发起连接。选择算法是从ngx_http_upstream_peer_t.peer对象中获取的,该对象的类型是ngx_peer_connection_t:
    ===> 这句当中对应部分改为“选择算法从ngx_http_upstream_t.peer对象中获取的”

    2. init(r, us) — 初始化用于每个请求的ngx_http_upstream_peer_t.peer (不要和之前用于每个upstream的ngx_http_upstream_srv_conf_t.peer搞混了)结构,该结构用于进行负载均衡。该结构会作为所有处理服务器选择的回调函数的data参数传递。
    ===> 这句官方英文文档整体描述都是含糊不清的,想表达是“初始化用于每个请求的ngx_http_upstream_t.peer中的部分成员函数和其data结构”
    感谢您的关注与提醒,我们确认了一下,确实是原文有误,目前已将本翻译中相关部分修改为ngx_http_upstream_t.peer 。我们也会再仔细审核翻译内容以及原文是否还存在其他疏漏,欢迎各位与我们交流互助~
    边城
    边城

    引用来自“Tocy”的评论

    这个翻译这速度可怕,一个人把50+段,瞬间翻译完成
    回复@Tocy : 白山云,力量雄厚,而且有可能之前翻译过的
    Tocy
    Tocy
    这个翻译这速度可怕,一个人把50+段,瞬间翻译完成
    小小丢石头
    mark
    月耳
    月耳
    翻译的很好,不过英文出处有些地方错了,本可以趁着翻译改正过来。
    1. 当nginx需要将请求转给其他服务器进行处理时,它会调用配置好的负载均衡算法来选择一个地址,并发起连接。选择算法是从ngx_http_upstream_peer_t.peer对象中获取的,该对象的类型是ngx_peer_connection_t:
    ===> 这句当中对应部分改为“选择算法从ngx_http_upstream_t.peer对象中获取的”

    2. init(r, us) — 初始化用于每个请求的ngx_http_upstream_peer_t.peer (不要和之前用于每个upstream的ngx_http_upstream_srv_conf_t.peer搞混了)结构,该结构用于进行负载均衡。该结构会作为所有处理服务器选择的回调函数的data参数传递。
    ===> 这句官方英文文档整体描述都是含糊不清的,想表达是“初始化用于每个请求的ngx_http_upstream_t.peer中的部分成员函数和其data结构”
    命运旅者
    命运旅者
    👍
    正在加载2
    正在加载2
    优秀~
    Sheamus
    Sheamus
    >这个评论能使用markdown语法吗?

    `System.out.println("hello world");
    `
    #Ngnix
    返回顶部
    顶部