首页 » Web前端 » php端口监听技巧_零根本开拓 nginx 模块

php端口监听技巧_零根本开拓 nginx 模块

duote123 2024-10-31 0

扫一扫用手机浏览

文章目录 [+]

nginx 利用 C 措辞开拓,C/C++ 构建工具浩瀚,如手写 Makefile, GNU Autoconf, cmake 等,一些项目乃至专门为自己开拓了构建工具,如 boost 库等。
nginx 利用哪种构建工具呢?很遗憾,末了一种,自己开拓。
nginx 利用 shell 脚本掩护了一套自动天生 Makefile 的构建脚本,类似简化定制版的 Autoconf 。
构建脚本位于代码库 auto/ 目录下,C 源码则位于 src/ 目录下。

nginx 构建脚本同时也用来编译附加模块。

php端口监听技巧_零根本开拓 nginx 模块

显然,在 nginx 模块中可以自由利用 nginx 主体代码供应的 API 。
须要把稳的是, 构建时的 nginx 版本必须与运行时的 nginx 版本精确匹配 ,否则 nginx 将谢绝加载。
这大概是 nginx 作者开拓环境

nginx 所需开拓环境非常大略,我利用 Ubuntu 18.04 ,利用下列命令即可安装所需最小依赖。

php端口监听技巧_零根本开拓 nginx 模块
(图片来自网络侵删)

sudo apt-get updatesudo apt-get install build-essential libpcre3-dev zlib1g-dev -y

接下来确定目标 nginx 版本,可利用 nginx -v 查看 nginx 版本,如 Ubuntu 18.04 自带 nginx 版本为 1.14.0 。

$ nginx -vnginx version: nginx/1.14.0 (Ubuntu)

获取目标 nginx 版本源码,可从 github 拉取。
利用 -b 指定拉取版本,--depth 1 表示仅拉取 1 个提交,不要提交历史,这样可以快速完成拉取。

git clone -b release-1.14.0 --depth 1 https://github.com/nginx/nginx.git

在 nginx 代码仓库目录下实行如下命令即可构建天生 nginx 可实行文件。

auto/configure && makeauto/configure 脚本检讨开拓环境和所需依赖,天生 Makefile 脚本,如果有报错按提示修复后重试即可。
make 命令利用 Makefile 构建天生 nginx 可实行文件。
默认在代码仓库目录下新建一个名为 objs/ 的目录作为构建目录,构建脚本自动天生的干系文件和终极编译天生的 nginx 可实行文件也在该目录下。

测试运行刚刚天生的 objs/nginx 可实行文件,结果如下。

$ objs/nginx -vnginx version: nginx/1.14.0

至此,最简 nginx 开拓环境准备就绪。

把稳: 此 nginx 版本仅用最小依赖和最简配置构建,仅供开拓测试动态模块时利用,不可替代生产环境的 nginx 版本。

源码配置与目录构造

模块源码在独立的文件夹下掩护 (又称之为插件 addon)。
模块源码目录下需供应一个名为 config 的 shell 配置脚本,供应模块信息。
nginx 构建脚本将 ngx_addon_dir 变量设置为模块源码路径,并实行 config 脚本获取模块信息。

在 nginx 代码仓库阁下新建一个名为 nginx-hello-module 的模块文件夹,创建一个 config 脚本文件和一个 C 措辞源码文件 hello_module.c,即得到一个最大略的模块示例,目录构造如下。

nginx/ # nginx 代码仓库├── auto/ # nginx 构建脚本目录└── src/ # nginx 源码目录, 其他文件夹暂未列出。
nginx-hello-module/ # 模块源码目录├── config # 模块配置脚本, shell 脚本└── hello_module.c # 模块源码文件

编写 config 配置脚本内容如下:

# vim: set ft=sh et:ngx_addon_name=ngx_http_hello_modulengx_module_type=HTTPngx_module_name="$ngx_addon_name"ngx_module_srcs="$ngx_addon_dir/hello_module.c". auto/module插件名 ngx_addon_name 和模块名 ngx_module_name 设置为 ngx_http_hello_module 。
模块类型 ngx_module_type 设置为 HTTP 。
源码文件列表 ngx_module_srcs 设置为 $ngx_addon_dir/hello_module.c。
把稳: 源码路径必须添加 $ngx_addon_dir/ 前缀,构建脚本才能精确找到源码文件。
语句 . auto/module 调用 nginx 供应的模块配置脚本,这条语句固定添加到 config 文件末了。

模块代码开拓我们稍后再说,现在可以先建一个空源码文件 hello_module.c 。

在 nginx 代码仓库下实行如下命令,增加配置上述 nginx-hello-module 模块。

auto/configure --add-dynamic-module=../nginx-hello-module/

在 nginx 代码仓库下实行如下命令编译模块。

make modules

竟然编译成功了!
得到动态模块文件 objs/ngx_http_hello_module.so 。
但此时模块还不可用 (考试测验加载此模块将报错),由于我们还没有写任何代码。

一个空模块

我们知道,一个 C 程序的入口是 main() 函数。
而一个 nginx 动态模块的入口是一个 ngx_module_t 工具,其构造定义如下。

typedef struct ngx_module_s ngx_module_t;struct ngx_module_s { / 私有字段 ... ... / void ctx; ngx_command_t commands; ngx_uint_t type; ngx_int_t (init_master)(ngx_log_t log); ngx_int_t (init_module)(ngx_cycle_t cycle); ngx_int_t (init_process)(ngx_cycle_t cycle); ngx_int_t (init_thread)(ngx_cycle_t cycle); void (exit_thread)(ngx_cycle_t cycle); void (exit_process)(ngx_cycle_t cycle); void (exit_master)(ngx_cycle_t cycle); / 扩展备用字段 ... ... /};

撤除私有字段和扩展备用字段,用户干系的字段可分为 3 个部分:

模块类型 ngx_uint_t type 和模块类型特定的信息 void ctx 。
模块类型必须与 config 脚本配置的类型同等,本例即为 HTTP ,源码中用 NGX_HTTP_MODULE 表示。
模块供应的指令列表 ngx_command_t commands 。
列表以 ngx_null_command 结尾,列表可以为空 (仅包含一个 ngx_null_command 结尾标记) 。
别的为模块生命周期管理函数,可全部设置为 NULL 。

HTTP 模块对应的模块信息 (void ctx 字段) 为 ngx_http_module_t 类型,可注册多少 HTTP 模块处理函数,可全部设置为 NULL 。

#define NGX_HTTP_MODULE 0x50545448 / "HTTP" /typedef struct { ngx_int_t (preconfiguration)(ngx_conf_t cf); ngx_int_t (postconfiguration)(ngx_conf_t cf); void (create_main_conf)(ngx_conf_t cf); char (init_main_conf)(ngx_conf_t cf, void conf); void (create_srv_conf)(ngx_conf_t cf); char (merge_srv_conf)(ngx_conf_t cf, void prev, void conf); void (create_loc_conf)(ngx_conf_t cf); char (merge_loc_conf)(ngx_conf_t cf, void prev, void conf);} ngx_http_module_t;

下面来编写 hello_module.c 源码,为大略起见,首先开拓一个空模块吧。

首先引入 nginx 头文件,声明模块入口 ngx_module_t 工具,变量名必须为 config 脚本中配置的模块名,本例中即为 ngx_http_hello_module 。

#include <ngx_config.h>#include <ngx_core.h>#include <ngx_http.h>extern ngx_module_t ngx_http_hello_module;

接下来设置 HTTP 模块信息 ngx_http_module_t ,干系处理函数全部设置为 NULL 。

static ngx_http_module_t ngx_http_hello_module_ctx = { NULL, / preconfiguration / NULL, / postconfiguration / NULL, / create main configuration / NULL, / init main configuration / NULL, / create server configuration / NULL, / merge server configuration / NULL, / create location configuration / NULL / merge location configuration /};

指令列表 ngx_command_t[] 设置为一个空列表,仅包含 ngx_null_command 结尾标记。

static ngx_command_t ngx_http_hello_commands[] = { ngx_null_command};

末了,定义模块入口工具 ngx_module_t 。
开头私有字段利用 NGX_MODULE_V1 表示,结尾扩展备用字段利用 NGX_MODULE_V1_PADDING 表示。
设置上述定义的 HTTP 模块信息 ngx_http_hello_module_ctx 和指令列表 ngx_http_hello_commands ,生命周期管理函数全部设置为 NULL 。

ngx_module_t ngx_http_hello_module = { NGX_MODULE_V1, &ngx_http_hello_module_ctx, / module context / ngx_http_hello_commands, / module directives / NGX_HTTP_MODULE, / module type / NULL, / init master / NULL, / init module / NULL, / init process / NULL, / init thread / NULL, / exit thread / NULL, / exit process / NULL, / exit master / NGX_MODULE_V1_PADDING};

至此,一个空模块开拓完成。
这可以作为开拓 HTTP 模块的初始模板,我们将在此根本上逐渐增加功能。

在 nginx 代码仓库目录下实行 make modules ,即可重新编译天生动态模块文件 objs/ngx_http_hello_module.so 。
由于我们没有修正模块配置,没有添加或删除源码文件,以是不须要重新实行 auto/configure 配置脚本,直接实行 make modules 即可。

测试运行 nginx

在 nginx 代码仓库目录下新建一个测试配置文件 objs/nginx.conf ,内容如下:

# vim: set ft=nginx et:daemon off; # default onpid objs/nginx.pid;error_log stderr notice;load_module objs/ngx_http_hello_module.so;events {}http { access_log objs/access.log; server { listen 8080 default_server; return 200 "test\n"; }}daemon off; 设置 nginx 进程不要后台化,保持前台运行,按 Ctrl+C 即可退出 nginx 。
error_log stderr notice; 缺点日志直接输出到终端,方便测试运行时查看缺点日志,设置日志级别为 notice 。
load_module objs/ngx_http_hello_module.so; 加载我们开拓的动态模块 ngx_http_hello_module.so 。
listen 8080 default_server; HTTP 做事器监听 8080 端口,这样利用普通用户即可运行测试。
return 200 "test\n"; HTTP 要求直接返回 "test" 字符串。

在 nginx 代码仓库目录下利用如下命令测试运行 nginx 。

objs/nginx -p "$PWD" -c objs/nginx.conf-p "$PWD" 设置 nginx prefix 为当前目录。
配置文件路径和配置文件中利用的相对路径利用相对付 prefix 的路径。
-c objs/nginx.conf 设置配置文件路径。

可看到 nginx 启动并打印日志,按 Ctrl+C 后 nginx 退出。
此时我们的模块还是空模块,没有发挥任何浸染。

Nginx 配置指令 - 天下你好

当我们学习一种新的开拓技能时,第一个程序常日是 "hello world": 打印一条 "hello world" 语句,向天下问声好。
第一次打仗 nginx 开拓时,我们不得不花韶光做一些准备事情。
现在,终于是时候伸开双臂,说一声 "天下你好" 了。

我最早学习利用的是 Apache HTTP 做事器,其至今仍旧是一款精良强大的开源软件。
一些团队由于分外缘故原由开始考试测验新产品,俄罗斯程序员 Igor Sysoev 开拓的 nginx 很快因其稳定性和高性能而声名鹊起。

最初学习利用 nginx 的感想熏染是,nginx 的配置文件彷佛比 apache 要大略友好一些 (在我对两者都不熟习的情形下) 。
nginx 的配置文件彷佛是一种脚本,以是 nginx 配置项被称作指令 (directive) 。
没错,nginx 不但是一个 HTTP 做事器,还是一个被设计得大略小巧的脚本措辞阐明器,并支持开拓添加新的指令。
nginx 指令常日用于配置,我们称之为配置指令,换一种唬人的说法,叫做声明式指令。

现在我们设计一个 hello 指令输出 "hello world" 语句。

创建配置存储构造体

HTTP 配置分为 http/server/location 3 层构造。
我们设计 hello 指令仅在最顶层 http {} 主区块 (block) 下利用和生效。
HTTP 模块默认无配置存储空间,可设置 ngx_http_module_t::create_main_conf 函数创建主区块配置构造体。

我们设计本模块仅包含一个字符串参数,即要输出的语句。
nginx 字符串类型为 ngx_str_t ,编写创建主配置构造体的函数 hello_create_main_conf() 如下:

static voidhello_create_main_conf(ngx_conf_t cf){ ngx_str_t conf; conf = ngx_pcalloc(cf->pool, sizeof(ngx_str_t)); if (conf == NULL) { return NULL; } return conf;}从配置内存池 cf->pool 分配一个字符串 ngx_str_t, 分配构造体将初始化为 0, 对 ngx_str_t 即空字符串。
如果函数返回 NULL 则表示分配失落败, nginx 将报错退出。

更新 ngx_http_module_t ngx_http_hello_module_ctx ,设置 create_main_conf 为 hello_create_main_conf() 函数。

static ngx_http_module_t ngx_http_hello_module_ctx = { NULL, / preconfiguration / NULL, / postconfiguration / hello_create_main_conf, / create main configuration / NULL, / init main configuration / NULL, / create server configuration / NULL, / merge server configuration / NULL, / create location configuration / NULL / merge location configuration /};

创建指令

一个指令用一个 ngx_command_t 类型的数据构造表示。

typedef struct ngx_command_s ngx_command_t;struct ngx_command_s { ngx_str_t name; ngx_uint_t type; char (set)(ngx_conf_t cf, ngx_command_t cmd, void conf); ngx_uint_t conf; ngx_uint_t offset; void post;};#define ngx_null_command { ngx_null_string, 0, NULL, 0, 0, NULL }name 指定指令名,如 hello 。
type 是一个稠浊构造,包含指令类型、指令利用位置、指令参数个数等多种特性信息。
利用 NGX_HTTP_MAIN_CONF 表示指令可在 http 主配置利用,NGX_CONF_TAKE1 表示指令接管 1 个参数。
set 为指令处理函数,即 nginx 配置设置函数。
conf 指示保存配置构造体的位置。
利用 NGX_HTTP_MAIN_CONF_OFFSET 表示指令配置在 http 主配置下存储生效。
offset 指示指令配置字段的位置。
常日一个模块的配置是一个构造体,而一个指令的配置是个中一个字段,set 函数通过 offset 访问字段,这样不须要知道构造体的类型 (构造),就可以读写配置字段。
模块只有一个配置项时,设置为 0 即可。
post 对特定处理函数可增加后置处理函数,或增加传入参数。
常日不该用,设为 NULL 。

声明指令处理函数 hello() :

static char hello(ngx_conf_t cf, ngx_command_t cmd, void conf);

创建 hello 指令如下:

static ngx_command_t ngx_http_hello_commands[] = { { ngx_string("hello"), NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1, hello, NGX_HTTP_MAIN_CONF_OFFSET, 0, NULL }, ngx_null_command};

编写指令处理函数

指令实行处理:

nginx 根据指令 type 字段设置的特性自动校验指令位置,参数个数等信息,并将指令语句解析为字符串数组 (类似 shell 命令行) ,保存到 cf->args ,再调用指令处理函数。
指令处理函数实行成功时返回 NGX_CONF_OK ,发生缺点时返回缺点。
为了简化和统一指令处理, nginx 预定义了许多标准指令处理函数,如 ngx_conf_set_str_slot() 将一个字符串参数解析保存为一个 ngx_str_t 配置项。
hello 指令可复用 ngx_conf_set_str_slot() 函数获取参数值,再添加额外逻辑打印 hello 语句。

编写指令处理函数 hello() 如下:

static charhello(ngx_conf_t cf, ngx_command_t cmd, void conf){ ngx_str_t str = conf; char rv; rv = ngx_conf_set_str_slot(cf, cmd, str); if (rv != NGX_CONF_OK) { return rv; } ngx_log_error(NGX_LOG_NOTICE, cf->log, 0, "HELLO %V", str); return NGX_CONF_OK;}ngx_log_error() 是一个宏,终极将调用 ngx_log_error_core() 函数。
ngx_log_error() 第 3 个参数 err 表示系统缺点码,无对应缺点码时利用 0 。
nginx 未利用 C 标准库的 snprintf() 字符串格式化函数,而是自己实现了 ngx_snprintf() 函数,并自定义了类似的格式化字符串,个中 %V 表示输出 ngx_str_t 指针指向的字符串。

至此,代码开拓完成。
在 nginx 代码仓库目录下实行 make modules 重新编译天生动态模块文件。

在配置文件 objs/nginx.conf http 配置下添加如下配置:

hello Nginx;

在 nginx 代码仓库目录下实行如下命令,nginx 日志将输出 "HELLO Nginx" 语句,按 Ctrl-C 退出 nginx 。

objs/nginx -p "$PWD" -c objs/nginx.confHTTP 要求处理器

nginx 定义了多个 HTTP 要求处理阶段 (phase) ,如读取完 HTTP 要求头后即进入 NGX_HTTP_POST_READ_PHASE 阶段。
可在 HTTP 要求处理的各个阶段添加处理器函数,类似于 Java Servlet 中的 HTTP 过滤器 (Filter) 。

HTTP 处理器函数署名 (函数类型) 如下:

typedef ngx_int_t (ngx_http_handler_pt)(ngx_http_request_t r);参数 r 为 HTTP 要求构造体。
返回值为 NGX_DECLINED 时,表示连续实行下一个处理器。
发生缺点时,返回 HTTP 缺点码,如做事器缺点 500 NGX_HTTP_INTERNAL_SERVER_ERROR ,nginx 将立即返回要求。

编写 HTTP 要求处理器 hello_handler() 如下,对每个 HTTP 要求打印一次 hello 语句,同时打印解析后的要求 uri 。
利用 ngx_http_get_module_main_conf() 从 HTTP 要求工具获取 ngx_http_hello_module 模块关联的配置数据。

static ngx_int_thello_handler(ngx_http_request_t r){ ngx_str_t str = ngx_http_get_module_main_conf(r, ngx_http_hello_module); ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0, "HELLO %V, uri: %V", str, &r->uri); return NGX_DECLINED;}

为 HTTP 模块编写一个 postconfiguration 函数 hello_init() ,将 HTTP 处理器 hello_handler() 注册到 NGX_HTTP_POST_READ_PHASE 阶段。
nginx 将在完成配置解析 (实行完配置指令) 后实行 HTTP 模块的 postconfiguration 函数,以完成模块初始化。

static ngx_int_thello_init(ngx_conf_t cf){ ngx_http_handler_pt h; ngx_http_core_main_conf_t cmcf; cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); h = ngx_array_push(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers); if (h == NULL) { return NGX_ERROR; } h = hello_handler; return NGX_OK;}

更新 ngx_http_module_t ngx_http_hello_module_ctx ,设置 postconfiguration 为 hello_init() 函数。

static ngx_http_module_t ngx_http_hello_module_ctx = { NULL, / preconfiguration / hello_init, / postconfiguration / hello_create_main_conf, / create main configuration / NULL, / init main configuration / NULL, / create server configuration / NULL, / merge server configuration / NULL, / create location configuration / NULL / merge location configuration /};

至此,开拓完成。
在 nginx 代码仓库目录下实行 make modules 重新编译天生动态模块文件,然后实行如下命令启动 nginx 。

objs/nginx -p "$PWD" -c objs/nginx.conf

利用浏览器或 curl 命令访问 http://localhost:8080/ ,每访问一次将看到 nginx 打印一次 hello 语句,及当前要求 uri 。
类似如下输出:

2020/05/16 22:46:26 [notice] 7279#0: 1 HELLO Nginx, uri: /, client: 127.0.0.1, server: , request: "GET / HTTP/1.1", host: "localhost:8080"2020/05/16 22:46:27 [notice] 7279#0: 1 HELLO Nginx, uri: /, client: 127.0.0.1, server: , request: "GET / HTTP/1.1", host: "localhost:8080"热更新 (reload)

nginx 还支持热更新 (reload) ,这是一个很有用的高等特性。
在一直止 nginx 的情形下将配置文件中的 hello 指令修正如下:

hello "阿泉";

在 nginx 代码仓库目录下实行如下 reload 命令:

objs/nginx -p "$PWD" -c objs/nginx.conf -s reload

reload 命令将看到如下输出:

2020/05/16 23:09:31 [notice] 9617#0: HELLO 阿泉2020/05/16 23:09:31 [notice] 9617#0: signal process started

原 nginx 进程将看到如下输出。
nginx 将重新进行配置初始化,创建新 worker 进程,并优雅退出旧 worker 进程。

2020/05/16 23:09:31 [notice] 9384#0: signal 1 (SIGHUP) received from 9617, reconfiguring2020/05/16 23:09:31 [notice] 9384#0: reconfiguring2020/05/16 23:09:31 [notice] 9384#0: HELLO 阿泉# ... ...2020/05/16 23:09:31 [notice] 9384#0: start worker process 96232020/05/16 23:09:31 [notice] 9385#0: gracefully shutting down

再次访问 http://localhost:8080/ 时,可看到 nginx 日志打印的 hello 语句也随之变成了新配置的 hello 语句。

2020/05/16 23:09:49 [notice] 9623#0: 3 HELLO 阿泉, uri: /, client: 127.0.0.1, server: , request: "GET / HTTP/1.1", host: "localhost:8080"

热更新 (reload) 功能非常有用,但在生产利用时一定要非常小心以避免故障。
实际利用中可能用的并不多。

教程到此结束,下面扯些题外话。

吐槽与闲聊

Nginx 文档还算完善,代码还算优雅,阅读 Nginx 源码对提升开拓水平颇有裨益,但其过程实在是烧脑和痛楚 (对我而言) 。
Nginx 源码险些攒齐了传统 C 措辞编程的所有缺点。
比如利用整数定义缺点码和列举类型,利用了迪杰斯特拉 (Dijkstra) 师长西席不建议利用的 goto 语句,一个整数字段 (如 cmd->type) 整合了多种列举类型信息,许多地方利用了动态类型 void 等。
这些用法不受工具 (静态) 检讨和约束 (原作者的脑中可能有一幅清晰的场景图表?),对不熟习的开拓者来说不仅难以理解,而且非常危险!
但其背后每每又是出于性能 (和某种简洁性) 的考虑,大概是利用 C 措辞的情形下所能做出的最大努力。
换句话说,(很多时候) 这是 C 措辞的局限性,而不是 Nginx 的问题。
缺点处理的精确解法该当是 Java 受检讨的非常,但 C 措辞短缺非常 (Exception) 等高等特性,合理利用 (无效业务值) 缺点码和 goto 语句是优雅且高效的最佳实践之一。

措辞之争

本段内容随意马虎引起不适,建议跳过。

有时候想,Nginx 为什么不该用更高等的开拓措辞 (比如 C++) 编写,或者至少可以复用 Apache 根本库 APR 吧 ?实在又何止 Apache 根本库, Apache HTTP 做事器该当有很多组件都可以复用。
但如果这样的话,Nginx 又怎么能叫 Nginx 呢 ? 大概只能是一个分外版本的 Apache HTTP 做事器,影响力和竞争力都很难超越官方正版(就像许多 Nginx 修正版很难超越 Nginx 一样) 。
不止是 3 方根本库,Nginx 连 C 措辞标准库都试图避免直策应用,比如自己开拓了 ngx_snprintf() (但 Nginx 也不是全都自己来,比如合理利用了 pcre, zlib, openssl 等 3 方库) 。
很多 C 措辞项目实在都在利用自己分外定制版的 C 措辞 (又一个范例缺陷) 。
这让我想起《黑客与画家》文集上提到的 迎难而生 的问题 (值得其余开贴谈论) ,如果一个问题太随意马虎,谁都可以复制 (抄袭),那么它的核心竞争力在哪里?

Nginx 及其模块开拓本身是有一定门槛的,乃至 Nginx 本身建议不要滥用模块开拓 (而只管即便用 nginx 配置或内置的 perl/njs 脚本) 。

有 nodejs 粉说用 nodejs 几条语句就可以写出一个高性能 HTTP 做事器,如果 nginx 这样写成,结果会若何 ?在大家都在喊着 nodejs/python/php/golang/kotlin 天下第一的时候,老态龙钟的 C 措辞荣获 TIOBE 编程措辞排行榜 2019 年度措辞,最近 (2020 年 5 月) 又重夺排行榜第一。
我不是针对谁,我是说 javascript/php/golang 等都是垃圾措辞 (python 和 kotlin 还算能用?)。
我也不推举 C 措辞,C 措辞显然有很多缺陷 (过于底层),如果能够加上一些 C++ 特性 (特殊是类和 RAII) 那肯定会好很多。
但是 C++ 特性太多,切实其实是一团浆糊,以是许多团队和项目不得不精心掌握一些边界,设计一个定制版的 C++ 措辞 (与 C 措辞类似)。
这导致 C++ 措辞分裂,是个不好的旗子暗记,也是这个缘故原由导致许多声称办理这些问题的新措辞不断涌现。

结论: 贴近系统和硬件编程,C/C++ 是不错的选择,高等措辞首选 Java ,其他一些快速粗糙 (quick and dirty) 的场景可适当选用其他措辞。
但一定要小心避免垃圾措辞 (不再逐一点名了) 和所谓的领域专用措辞 (DSL) 。

代码风格

首选吐槽一下。
Nginx 只利用 C 风格的注释 / / (不该用 C++ 的双斜杠 // 注释) 。
利用 4 个空格缩进 (而不是 tab) 。
变量名常常太短 (导致含义不直不雅观) 。
单行源码不超过 80 个字符 (可能也是导致变量名过短的缘故原由)。
这几点个人不太喜好。

听说 nginx 作者有代码洁癖,哀求字段名 (变量名) 排版对齐。
我也有代码洁癖,我反对这种对齐,表面上视觉整洁了,实际上掩护跟踪很麻烦 (特殊是没有工具支持的情形下) 。
再看 nginx 代码,不仅哀求对齐,而且是抛开润色符后的单词对齐 (嗯,奇怪的排版) 。
如 struct ngx_command_s 定义如下。

struct ngx_command_s { ngx_str_t name; ngx_uint_t type; char (set)(ngx_conf_t cf, ngx_command_t cmd, void conf); ngx_uint_t conf; ngx_uint_t offset; void post;};

nginx 代码是很吝惜注释的,但并非没有注释,恰当的时候会有注释,而更多的时候让代码自己说话。
如 ngx_http_core_generic_phase() 函数的这段代码,结合注释可知这里已经考虑列举了 rc 的所有可能取值。
这点我是比较讴歌的,不过个人建议可以适当添加更多注释 (特殊是逻辑繁芜的地方) 。

if (rc == NGX_DECLINED) { r->phase_handler++; return NGX_AGAIN; } if (rc == NGX_AGAIN || rc == NGX_DONE) { return NGX_OK; } / rc == NGX_ERROR || rc == NGX_HTTP_... / ngx_http_finalize_request(r, rc); return NGX_OK;

其余,nginx 代码鼓励用空行分割语义块 (哪怕只有一行) ,如 ngx_conf_handler() 函数包含如下代码块:

/ set up the directive's configuration context / conf = NULL; if (cmd->type & NGX_DIRECT_CONF) { conf = ((void ) cf->ctx)[cf->cycle->modules[i]->index]; } else if (cmd->type & NGX_MAIN_CONF) { conf = &(((void ) cf->ctx)[cf->cycle->modules[i]->index]); } else if (cf->ctx) { confp = (void ) ((char ) cf->ctx + cmd->conf); if (confp) { conf = confp[cf->cycle->modules[i]->ctx_index]; } }

if 子句和 else 子句实行不同的逻辑,用一个空行分开,构造更加清晰,这一点值得学习。
顺便说句,这段代码较难读懂,也容许以再适当添加部分注释。

末了,很多人可能听过类似 "单个函数不要超过 100 行" (更有严格的说 50 行, 20 行) 这样的最佳实践。
但如果我们看许多精良开源项目的代码,大佬们写起代码来根本停不下来,洋洋洒洒几百行的核心函数纯属正常。
只管即便保持函数功能单一和简短当然是最近实践,但是 不用去世守规则 。
规则每每是由强者制订来约束弱者,黑客从来不应该受任何详细规则的束缚,唯一的规则便是精确、简短、健壮,然后越快越好。
别给我说那些婆婆妈妈的编程规范。

我的代码又快又稳定,然后你跑来说我排版不好看 (是的我说了) ?滚一边去!

后浪

初次打仗一种开拓技能,彷佛来到一座花园,想要到某个目的地取采摘一朵花 (开拓需求)。
陌生的花园犹如迷宫,一开始我们跌跌撞撞,可能被荆棘扎手,可能走错方向,但终极来到玫瑰花栏,摘下一朵花。
于是我沿途做下暗号,小心避开荆棘和弯路,就成了这篇文章。

所有本文更适宜作为大略的快速参考 (沿路暗号),而读者可能会充满 “这里为什么要这样?” 的疑问。
许多疑问都可以在 Nginx 官方 开拓指南 和 源码 里找到答案,那才是真正的藏宝图。
只有我们亲自摸索熟习了这座花园,才会创造许许多多的宝藏,你大概会创造,阁下花栏有更俏丽的郁金喷鼻香和暗香的茉莉花。
先读代码,后浪。

本文为阿里云原创内容,未经许可不得转载。

标签:

相关文章

七层协议原理,构建互联网通信的基石

互联网作为当今世界最广泛的信息传播平台,承载着海量数据的传输和交换。为了实现不同类型网络设备的互联互通,国际标准化组织(ISO)提...

Web前端 2024-12-27 阅读0 评论0

三大平衡,构建和谐社会的基石

自古以来,我国传统文化就强调“平衡”的重要性。所谓“天时、地利、人和”,正是对三大平衡的生动诠释。在新时代背景下,三大平衡成为构建...

Web前端 2024-12-27 阅读0 评论0

_代采协议,打造高品质家居环境的明智之选

随着我国经济的快速发展,人们对居住环境的要求越来越高。装修作为改善居住环境的重要手段,越来越受到广大消费者的关注。装修过程中涉及到...

Web前端 2024-12-27 阅读0 评论0

WSFL语言,构建未来智能服务生态的关键

随着信息技术的飞速发展,人工智能逐渐渗透到我们生活的方方面面。智能服务作为人工智能的重要应用领域,正逐渐改变着我们的生活方式。WS...

Web前端 2024-12-27 阅读0 评论0