编辑推荐

适读人群:《Nginx完全开发指南:使用C、C++和OpenResty》结构严谨、脉络清晰、论述精确、详略得当、图文并茂,值得广大软件开发工程师、系统运维工程师和编程爱好者拥有。

精心雕琢,认真原创!

专注、深入、聚集!

资深技术专家Nginx源码研习书强势升级!

深入新版Nginx源码(Stable1.16.0)!

详细剖析Nginx核心运行机制!

便捷、轻松地开发和定制Nginx!

内容简介

Nginx是著名的Web服务器,性能优异,运行效率远超传统的Apache、Tomcat,广泛应用于国内外诸多**互联网公司。Nginx的一个突出特点是其灵活优秀的模块化架构,可以在不修改核心的前提下增加任意功能,自2004年发布至今,已经拥有百余个官方及非官方的功能模块(如proxy、mysql、redis、rtmp、lua等),使得Nginx成长为了一个近乎“全能”的服务器软件。Nginx功能强大,架构复杂,学习、维护和开发的门槛较高。为了帮助读者跨越这一障碍,《Nginx完全开发指南:使用C、C++、JavaScript和Lua》深入*新的Nginx源码(Stable1.16.0),详细剖析了模块体系、动态插件、功能框架、内存分配、进程模型、事件驱动、线程池、TCP/UDP/HTTP处理等Nginx核心运行机制,在此基础上讲解如何使用C、C++、JavaScript、Lua等语言来增强扩展Nginx,让任何人都能够便捷、轻松地开发和定制Nginx,进而应用到自己的实际工作中,创造出更多的价值。《Nginx完全开发指南:使用C、C++、JavaScript和Lua》结构严谨、脉络清晰、论述精确、详略得当、图文并茂,值得广大软件开发工程师、系统运维工程师和编程爱好者拥有。

作者简介

罗剑锋1996年就读于东北财经大学;1997年开始接触C/C++;1998年参加计算机软件专业技术资格和水平考试,获高级程序员资质;2003年毕业于北京理工大学,获计算机专业硕士学位。主要研究方向为C/C++、设计模式、高性能网络服务器开发,业余爱好是阅读、欣赏音乐和旅游。

目录

目录
第0章导读1
0.1于《Nginx完全开发指南:使用C、C++、JavaScript和Lua》1
0.2读者对象3
0.3读者要求4
0.4运行环境5
0.5《Nginx完全开发指南:使用C、C++、JavaScript和Lua》的结构5
0.6如何阅读《Nginx完全开发指南:使用C、C++、JavaScript和Lua》6
0.7《Nginx完全开发指南:使用C、C++、JavaScript和Lua》的源码7
第1章Nginx入门8
1.1关于Nginx8
1.1.1历史9
1.1.2特点9
1.1.3进程模型10
1.1.4版本12
1.2安装Nginx12
1.2.1准备工作13
1.2.2快速安装13
1.2.3运行命令14
1.2.4验证安装15
1.2.5定制安装16
1.3配置Nginx18
1.3.1语法格式19
1.3.2进程管理20
1.3.3动态模块22
1.3.4运行日志22
1.3.5事件机制22
1.4HTTP服务23
1.4.1基本配置24
1.4.3location配置25
1.4.4file配置26
1.5TCP/UDP服务27
1.6反向代理28
1.6.1上游集群28
1.6.2负载均衡29
1.6.3代理转发30
1.7变量30
1.8总结32
第2章Nginx开发准备33
2.1源码结构33
2.2源码特点34
2.2.1代码风格34
2.2.2代码优化35
2.2.3面向对象思想35
2.3头文件36
2.4总结36
第3章Nginx基础设施37
3.1常数37
3.1.1环境信息37
3.1.2版本信息38
3.1.3错误码38
3.2整数类型39
3.2.1标准整数类型39
3.2.2自用整数类型40
3.2.3无效值40
3.3内存池42
3.3.1结构定义42
3.3.2操作函数43
3.3.3用法示例44
3.4字符串44
3.4.1结构定义44
3.4.2操作函数45
3.4.3用法示例48
3.5时间49
3.5.1结构定义49
3.5.2操作函数49
3.5.3用法示例50
3.6日期50
3.6.1结构定义50
3.6.2操作函数51
3.6.3用法示例52
3.7运行日志52
3.7.1结构定义52
3.7.2操作函数53
3.7.3用法示例54
3.8摘要算法54
3.8.1Times3355
3.8.2CRC55
3.8.3MurmurHash56
3.8.4MD557
3.8.5SHA-157
3.9数据编码58
3.9.1Base6458
3.9.2HTML/JSON59
3.10总结60
第4章Nginx高级数据结构61
4.1动态数组61
4.1.1结构定义62
4.1.2操作函数63
4.1.3用法示例64
4.2单向链表65
4.2.1结构定义65
4.2.2操作函数66
4.2.3用法示例66
4.3双端队列68
4.3.1结构定义68
4.3.2操作函数69
4.3.3用法示例71
4.4红黑树72
4.4.1结构定义73
4.4.2操作函数75
4.4.3用法示例76
4.5缓冲区78
4.5.1结构定义78
4.5.2操作函数80
4.5.3用法示例81
4.6数据块链82
4.6.1结构定义82
4.6.2操作函数83
4.6.3用法示例83
4.7总结84
第5章Nginx开发概述85
5.1开发示例85
5.1.1模块设计85
5.1.2配置解析86
5.1.3处理函数88
5.1.4模块集成90
5.1.5编译脚本91
5.1.6测试验证92
5.2开发流程92
5.2.1设计93
5.2.2开发93
5.2.3编译94
5.2.4测试验证94
5.2.5调优94
5.2.6流程图95
5.3编译脚本95
5.3.1运行机制96
5.3.2脚本变量96
5.3.3添加模块97
5.3.4脚本格式97
5.3.5旧式脚本98
5.4总结99
第6章Nginx模块体系100
6.1模块架构100
6.1.1结构定义100
6.1.2模块的签名102
6.1.3模块的种类103
6.1.4模块的函数指针表104
6.1.5模块的类图105
6.1.6模块的组织形式106
6.1.7模块的静态加载108
6.1.8模块的动态加载110
6.2配置解析113
6.2.1结构定义113
6.2.2基本流程116
6.2.3存储模型118
6.2.4访问配置数据122
6.2.5配置数据的位置123
6.2.6配置数据的解析124
6.2.7配置数据的合并126
6.2.8配置指令的类型127
6.3源码分析128
6.3.1ngx_core_module128
6.3.2ngx_errlog_module130
6.4总结132
第7章Nginx功能框架134
7.1框架简介134
7.1.1模块分类134
7.1.2处理流程135
7.1.3请求的处理阶段137
7.1.4请求结构体138
7.1.5请求的环境数据140
7.2处理引擎141
7.2.1函数原型141
7.2.2处理函数的存储方式141
7.2.3内容处理函数142
7.2.4引擎的数据结构143
7.2.5引擎的初始化144
7.2.6引擎的运行机制145
7.2.7日志阶段的处理148
7.3过滤引擎148
7.3.1函数原型148
7.3.2过滤函数链表149
7.3.3过滤函数的顺序150
7.3.4过滤链表的运行机制152
7.3.5请求体过滤153
7.4源码分析153
7.4.1ngx_http_static_module154
7.4.2ngx_http_not_modified_filter_module155
7.5总结156
第8章Nginx请求处理158
8.1状态码158
8.2请求结构体159
8.3请求行160
8.3.1请求方法160
8.3.2协议版本号161
8.3.3资源标识符161
8.4请求头162
8.5请求体163
8.5.1结构定义163
8.5.2操作函数164
8.6响应头164
8.6.1结构定义164
8.6.2操作函数165
8.7响应体166
8.8源码分析166
8.8.1ngx_http_static_module166
8.8.2ngx_http_not_modified_filter_module168
8.9开发示例:contenthandler169
8.9.1模块设计169
8.9.2配置数据169
8.9.3处理函数170
8.9.4注册函数171
8.9.5模块集成172
8.9.6编译脚本173
8.9.7测试验证173
8.10开发示例:filter173
8.10.1模块设计173
8.10.2配置数据174
8.10.3环境数据174
8.10.4注册过滤函数175
8.10.5过滤响应头175
8.10.6过滤响应体176
8.10.7模块集成178
8.10.8编译脚本179
8.10.9测试验证179
8.11总结180
第9章Nginx请求转发181
9.1框架简介181
9.1.1工作原理182
9.1.2请求结构体183
9.1.3上游结构体184
9.1.4上游配置参数185
9.2请求转发186
9.2.1回调函数186
9.2.2初始化188
9.2.3设置参数189
9.2.4启动连接190
9.2.5处理响应头190
9.2.6处理响应体191
9.3负载均衡192
9.3.1结构定义192
9.3.2初始化模块入口196
9.3.3初始化地址列表197
9.3.4初始化算法199
9.3.5执行算法200
9.4源码分析200
9.4.1ngx_http_memcached_module201
9.4.2ngx_http_upstream_ip_hash_module203
9.5开发示例:upstream206
9.5.1模块设计206
9.5.2配置数据206
9.5.3上行数据208
9.5.4下行数据208
9.5.5启动转发209
9.5.6注册函数210
9.5.7模块集成210
9.5.8编译脚本211
9.5.9测试验证212
9.6开发示例:balance212
9.6.1模块设计212
9.6.2配置数据212
9.6.3算法数据结构213
9.6.4模块入口213
9.6.5算法实现214
9.6.6模块集成215
9.6.7编译脚本216
9.6.8测试验证216
9.7总结216
第10章Nginx子请求218
10.1框架简介218
10.1.1工作原理219
10.1.2请求结构体220
10.1.3回调函数221
10.1.4待处理请求链表223
10.1.5子请求存储结构223
10.2运行机制223
10.2.1创建子请求224
10.2.2处理引擎228
10.2.3数据整理229
10.3开发示例230
10.3.1模块设计231
10.3.2配置数据231
10.3.3环境数据231
10.3.4回调函数231
10.3.5处理函数232
10.3.6注册函数233
10.3.7测试验证234
10.4总结234
第11章Nginx变量236
11.1结构定义236
11.1.1变量237
11.1.2复杂变量238
11.1.3变量的存储239
11.1.4请求结构体239
11.2操作变量240
11.2.1添加变量240
11.2.2获取变量241
11.2.3修改变量242
11.2.4编译复杂变量242
11.2.5获取复杂变量242
11.3开发示例:变量243
11.3.1模块设计243
11.3.2定义变量243
11.3.3添加变量244
11.3.4获取变量244
11.3.5测试验证245
11.4开发示例:复杂变量246
11.4.1模块设计246
11.4.2定义复杂变量246
11.4.3编译复杂变量246
11.4.4获取复杂变量247
11.4.5测试验证247
11.5总结247
第12章Nginx内存管理机制249
12.1基本系统调用250
12.1.1malloc250
12.1.2posix_memalign251
12.1.3free251
12.2块式内存池252
12.2.1结构定义252
12.2.2常量定义255
12.2.3创建内存池255
12.2.4分配内存257
12.2.5分配大块内存258
12.2.6分配小块内存259
12.2.7释放内存264
12.2.8清理机制264
12.2.9清空内存池265
12.2.10销毁内存池266
12.3页式内存池267
12.3.1结构定义268
12.3.2常量定义270
12.3.3初始化内存池271
12.3.4分配内存273
12.3.5分配大块内存275
12.3.6分配小块内存277
12.3.7释放内存280
12.4总结282
第13章Nginx进程机制284
13.1基本系统调用284
13.1.1errno284
13.1.2getrlimit285
13.2进程系统调用285
13.2.1getpid285
13.2.2fork286
13.2.3waitpid286
13.3信号系统调用287
13.3.1kill287
13.3.2sigaction288
13.3.3sigsuspend288
13.4结构定义288
13.4.1ngx_cycle_t288
13.4.2ngx_core_conf_t289
13.4.3ngx_process_t290
13.5全局变量291
13.5.1命令行相关291
13.5.2操作系统相关292
13.5.3进程功能相关292
13.5.4信号功能相关293
13.6启动过程293
13.6.1基本流程293
13.6.2解析命令行294
13.6.3版本和帮助信息294
13.6.4初始化cycle294
13.6.5测试配置296
13.6.6发送信号297
13.6.7守护进程化297
13.6.8启动工作进程298
13.6.9流程图298
13.7信号处理299
13.7.1信号处理函数300
13.7.2发送信号300
13.7.3处理信号301
13.8单进程模式302
13.8.1single进程302
13.8.2single进程流程图304
13.9多进程模式304
13.9.1产生子进程304
13.9.2master进程306
13.9.3master进程流程图309
13.9.4worker进程310
13.9.5worker进程流程图312
13.10总结313
第14章Nginx进程间通信机制315
14.1基本系统调用315
14.1.1atomic315
14.1.2sched_yield316
14.1.3semaphore316
14.1.4mmap317
14.2共享内存(Ⅰ)317
14.2.1结构定义317
14.2.2创建共享内存317
14.2.3使用共享内存318
14.3自旋锁318
14.3.1自旋锁定319
14.3.2解除锁定320
14.3.3使用自旋锁320
14.4互斥锁320
14.4.1结构定义320
14.4.2创建互斥锁321
14.4.3互斥锁定322
14.4.4解除锁定323
14.4.5销毁互斥锁324
14.4.6使用互斥锁324
14.5读写锁325
14.5.1写锁定325
14.5.2读锁定325
14.5.3解除锁定326
14.5.4降级锁定326
14.5.5使用读写锁327
14.6共享内存(Ⅱ)327
14.6.1结构定义327
14.6.2添加共享内存328
14.6.3创建共享内存329
14.6.4使用共享内存330
14.7总结331
第15章Nginx事件机制333
15.1基本系统调用333
15.1.1errno334
15.1.2ioctl334
15.1.3setitimer334
15.1.4gettimeofday334
15.2socket系统调用335
15.2.1socket335
15.2.2bind335
15.2.3listen336
15.2.4accept336
15.2.5connect336
15.2.6recv336
15.2.7send337
15.2.8setsockopt337
15.2.9close337
15.2.10函数关系图338
15.3epoll系统调用338
15.3.1epoll_create339
15.3.2epoll_ctl339
15.3.3epoll_wait340
15.3.4LT和ET341
15.3.5函数关系图342
15.4结构定义342
15.4.1ngx_event_t342
15.4.2ngx_connection_t343
15.4.3ngx_listening_t345
15.4.4ngx_cycle_t346
15.4.5ngx_os_io_t347
15.4.6ngx_event_actions_t351
15.4.7ngx_posted_events353
15.4.8关系图354
15.5定时器354
15.5.1红黑树354
15.5.2操作函数355
15.5.3超时处理355
15.6模块体系358
15.6.1函数指针表358
15.6.2模块的组织形式359
15.6.3核心配置361
15.6.4epoll模块362
15.7全局变量363
15.7.1更新时间相关363
15.7.2事件机制相关364
15.7.3负载均衡相关365
15.7.4统计相关365
15.8进程初始化366
15.8.1初始化函数366
15.8.2基本参数初始化368
15.8.3事件机制初始化369
15.8.4连接池初始化370
15.8.5监听端口初始化371
15.8.6初始化流程图373
15.9运行机制373
15.9.1添加事件374
15.9.2删除事件377
15.9.3处理事件378
15.9.4接受连接382
15.9.5负载均衡384
15.10避免阻塞389
15.11总结390
第16章Nginx多线程机制392
16.1eventfd系统调用392
16.2pthread系统调用393
16.3结构定义393
16.3.1ngx_thread_task_t394
16.3.2ngx_thread_pool_queue_t394
16.3.3ngx_thread_pool_t395
16.3.4结构关系图396
16.4事件通知396
16.4.1函数接口396
16.4.2初始化397
16.4.3发送通知398
16.4.4处理通知398
16.5运行机制399
16.5.1完成任务队列399
16.5.2创建线程池399
16.5.3创建任务400
16.5.4投递任务401
16.5.5执行任务402
16.5.6任务完成回调404
16.5.7销毁线程池405
16.6开发示例406
16.6.1模块设计406
16.6.2配置数据407
16.6.3线程任务407
16.6.4任务完成回调408
16.6.5投递任务409
16.6.6测试验证410
16.7总结410
第17章NginxStream机制412
17.1模块体系412
17.1.1函数指针表413
17.1.2基础模块413
17.1.3核心模块415
17.1.4结构关系图416
17.1.5存储模型416
17.2监听端口418
17.2.1结构定义418
17.2.2解析配置420
17.2.3启动监听424
17.3处理引擎425
17.3.1阶段定义426
17.3.2函数原型426
17.3.3处理函数的存储方式426
17.3.4引擎数据结构427
17.3.5结构关系图428
17.3.6引擎的初始化428
17.4过滤引擎430
17.4.1函数原型430
17.4.2过滤函数链表430
17.5运行机制431
17.5.1会话结构体431
17.5.2创建会话432
17.5.3执行引擎435
17.5.4通用阶段处理437
17.5.5预读数据438
17.5.6产生响应数据442
17.5.7过滤数据442
17.5.8结束会话442
17.6开发示例443
17.6.1discard协议444
17.6.2time协议446
17.6.3echo协议448
17.7总结450
第18章NginxHTTP机制452
18.1结构定义452
18.1.1ngx_http_state_e452
18.1.2ngx_http_connection_t453
18.1.3ngx_http_request_t453
18.2初始化连接454
18.2.1建立连接455
18.2.2等待数据456
18.2.3读取请求头458
18.3执行引擎463
18.3.1初始化引擎463
18.3.2通用阶段465
18.3.3改写阶段466
18.3.4访问控制阶段467
18.3.5内容产生阶段469
18.4处理请求体470
18.4.1丢弃缓冲区数据470
18.4.2读取并丢弃数据471
18.4.3读事件处理函数473
18.4.4启动丢弃处理474
18.5发送数据475
18.5.1发送初始化475
18.5.2事件处理函数476

18.6结束请求478
18.6.1释放请求资源478
18.6.2检查引用计数结束请求479
18.6.3检查状态结束请求480
18.6.4综合处理结束请求481
18.7总结483
第19章Nginx与设计模式485
19.1设计模式简介485
19.2框架级别的模式485
19.3业务级别的模式487
19.4代码级别的模式488
19.5总结490
第20章NginxC++开发491
20.1语言简介491
20.2开发准备492
20.2.1程序库492
20.2.2头文件492
20.2.3编程范式493
20.2.4实现原则493
20.2.5源码组织494
20.2.6编译脚本495
20.3封装类497
20.3.1基础设施497
20.3.2高级数据结构500
20.3.3功能框架505
20.3.4请求处理509
20.4开发示例:contenthandler510
20.4.1配置信息类510
20.4.2业务逻辑类511
20.4.3模块集成类512
20.4.4实现源文件515
20.5开发示例:filter515
20.5.1配置信息类515
20.5.2环境数据类515
20.5.3业务逻辑类516
20.5.4模块集成类518
20.5.5实现源文件519
20.6总结519
第21章NginxJavaScript开发521
21.1语言简介521
21.2模块简介522
21.3开发准备523
21.4指令简介523
21.5功能接口524
21.5.1运行日志524
21.5.2变量525
21.5.3请求处理525
21.5.4子请求527
21.5.5定时器527
21.5.6流处理527
21.6开发示例528
21.6.1contenthandler528
21.6.2subrequest529
21.6.3A/Btesting530
21.7总结531
第22章NginxLua开发532
22.1语言简介532
22.2模块简介533
22.2.1http_lua533
22.2.2stream_lua533
22.2.3lua-resty-lib534

22.3开发准备534
22.4指令简介535
22.4.1配置指令536
22.4.2功能指令536
22.4.3指令关系图538
22.5应用开发流程538
22.6功能接口539
22.6.1运行日志539
22.6.2时间与日期540
22.6.3变量540
22.6.4正则表达式541
22.6.5请求处理542
22.6.6请求转发544
22.6.7子请求545
22.6.8定时器546
22.6.9共享内存546
22.7开发示例547
22.7.1contenthandler548
22.7.2filter549
22.7.3upstream550
22.7.4subrequest550
22.7.5discard551
22.7.6echo552
22.8总结552
第23章Nginx调试与测试554
23.1调试554
23.1.1调试器554
23.1.2调试断点555
23.1.3调试日志556
23.2功能测试558
23.2.1测试套件559

23.2.2测试用例559
23.2.3运行测试564
23.3性能测试564
23.3.1ab565
23.3.2http_load565
23.3.3wrk566
23.3.4Test::Nginx567
23.4总结568
第24章Nginx性能分析569
24.1简介569
24.2火焰图570
24.3分析工具572
24.3.1使用方式572
24.3.2处理数据573
24.4动态追踪576
24.4.1CPU分析577
24.4.2I/O分析579
24.4.3Memory分析580
24.4.4观测工具581
24.5总结582
第25章结束语583
25.1《Nginx完全开发指南:使用C、C++、JavaScript和Lua》的遗憾583
25.2下一步583
25.3临别赠言584
附录A推荐书目585
附录B字符串格式化587
附录C开发辅助工具589

前言/序言

缘起

最早接触Nginx大概是在2011年,面对着一个全新的Web服务器,和大多数人一样最初我也是一片茫然,能找到的参考资料十分有限,安装、配置、运行几乎都是“摸着石头过河”,犯过许多低级错误。

随着对Nginx逐渐熟悉,它的高并发处理能力给我留下了深刻的印象,作为一个开源软件的爱好者,很自然地想要探究一下它的内部工作原理。我由此开始了对Nginx源码的钻研之路,中间经过了很多的艰辛曲折,走过了不少的弯路。

我最常用的工作语言是C++,所以在阅读Nginx源码时也总以C++的面向对象方式来思考和理解,以对象作为切入点记笔记、画UML:从最简单的ngx_str_t、ngx_array_t入手,然后到ngx_request_t、ngx_upstream_t等复杂的结构,再围绕着这些对象研究相关的功能函数和处理流程,梳理代码逻辑的同时也摸索着使用C++编写Nginx模块的方法,逐渐积累了一些用起来颇为顺手的小工具——当然还是比较初级的形式。

五年多前,我被调到了新的工作岗位,需要重度使用Nginx开发,这让我以前的零散积累终于有了用武之地。那段时间里使用C/C++陆续做了很多东西,也借着机会重新优化了原有的工具代码。

繁忙的工作之余,我有了种进一步整理经验的迫切感,因为只有系统完整地分享这些知识,才能让更多的人基于Nginx二次开发,让Nginx更好地为网络世界服务。

同一时间,市面上也出现了一些Nginx开发相关的资料、书籍,但在我看来却有“粗制滥造”之嫌:行文混乱,“车轱辘话”“口头禅”满天飞,甚至大段照抄指令说明,还有对源码的曲解,未免有点儿“误人子弟”,读起来实在是难受。终于,在“忍无可忍”的心态之下,我动起了写作《Nginx完全开发指南:使用C、C++、JavaScript和Lua》的念头。

经过近一年的努力,现在这《Nginx完全开发指南:使用C、C++、JavaScript和Lua》终于呈现在了读者面前,结构上基本反映了我学习研究Nginx时的心路历程,从最初的“一无所知”起步,逐渐深入到定制开发的层次,希望能与读者“心有戚戚焉”。

Nginx随感

毫无疑问,Nginx是目前这个星球上所能获得的最强劲的Web服务器(没有之一),同时也是目前最成熟、最优秀的TCP/HTTP服务器开发框架。

Nginx资源消耗低,并发处理性能高,配置灵活,能够连接CGI、PHP、MySQL、Memcached等多种后端,还有着出色的负载均衡能力,可以整合封装各种service,构建稳定高效的服务。如今Nginx已经成为了网站架构里不可或缺的关键组件,广泛应用于国内外许多大型IT企业内部。每一个繁忙的网站背后,可能都有Nginx默默工作的身影。

在Nginx出现之前,使用C/C++开发Web服务器是项比较“痛苦”的工作,虽然有很多网络程序库可以使用(例如asio、libevent、thrift等),但它们通常只关注较底层的基础功能实现,离成熟的“框架”相距甚远,不仅开发过程烦琐低效,而且程序员还必须要处理配置管理、进程间通信、协议解析等许多Web服务之外的其他事情,才能开发出一个较为完善的服务器程序。但即使开发出了这样的服务器,通常性能上也很难得到保证,会受到程序库和开发者水平等因素的限制——很长一段时间里,C/C++在Web服务器领域都没有大展拳脚的机会。

Nginx的横空出世为Web服务器开辟了一个崭新的天地,它搭建了一个高性能的服务器开发框架,而且是一个完整的、全功能的服务器。模块化的架构设计很好地分离了底层支撑模块和上层逻辑模块,底层模块处理了配置、并发等服务器的外围功能,核心支撑模块定义了主体的TCP/HTTP处理框架。开发者可以把大部分精力集中在上层的业务功能实现上,再也不必去为其他杂事而分心,提高了软件的开发效率。

在Nginx框架里C/C++程序员可以尽情发挥自己的专长,充分利用Nginx无阻塞处理的优势,打造出高质量的Web应用服务器,与其他系统一较高下。

Nginx和C/C++

IgorSysoev选择用C语言(准确地说是ANSIC)来实现Nginx肯定是经过了认真的考虑。

作为与UNIX一同诞生的编程语言,C语言一直是系统级编程的首选。和其他高级语言相比,它简单可靠,更接近计算机底层硬件,运行效率更高。指针更是C语言的一大特色,善用指针能够完成许多其他语言无法完成的工作。

以C语言实现的Nginx没有“虚拟机”的成本,省略了不必要的中间环节,直接操纵计算机硬件,从根本上提高了Web服务器的处理能力。虽然C语言不直接支持面向对象,但Nginx灵活运用了指针,采用“结构体+函数指针”的形式,达到了同样的效果,从而使软件拥有了良好的结构。

C++是仅次于C的系统级编程语言,在兼容C的同时又增加了异常、模板等新特性,还支持面向对象、泛型、函数式、模板元等多种编程范式,可以说是计算机语言里的一个“庞然大物”。C++的特性很多,有的也很好用,但总体上的确比较复杂,易学难精,容易被误用和滥用,导致低效、难维护的代码,我想这可能是IgorSysoev放弃使用C++的一个重要原因。

另一个可能的原因是C语言本身已经非常稳定,几十年来没有太大的变动,在各个系统里都支持得非常好。而C++在1998年才有了第一个标准,而且现在还在发展之中,语言特性还不够稳定(例如export、register等曾经的关键字在C++11里就已经被废弃),许多编译器对C++的支持程度也有差异,这与Nginx的高可移植性目标明显不符。

但C++毕竟还是有很多的优点,类可以更好地封装信息、异常简化了错误处理、模板能够在编译期执行类型计算。在C++11标准颁布之后C++更是几乎变成了一门“全新”的语言,auto/decltype/nullptr/noexcept等新关键字增强了语言的描述能力,标准库也扩充了相当多的组件,易用性和稳定性都大大提升。

在Nginx里使用C++时要对C++的长处和不足有清醒的认识,避免多层次继承、虚函数等影响效率的编程范式,只使用经过充分验证的、能够切实提高开发效率和性能的语言特性和库,避免华而不实的技术炫耀,尽量做到像Nginx源码那样质朴踏实。只有这样,才能够发挥出“1+1>2”的作用,让Nginx从C++中得到更进一步的发展动力。

Nginx和OpenResty

多年以前Nginx开发使用的语言只能是C和C++,而现在,越来越多的开发者开始转向了OpenResty,使用Lua搭建高并发、高性能、高扩展性的WebServer。

我接触OpenResty的时间并不算很长,大约在五年左右。由于C/C++程序员“天生的傲慢”,一开始对OpenResty确实有点儿“抵触情绪”,总觉得脚本程序比不上C/C++实现。然而随着使用的增多,特别是在研究了它的源码之后,我不得不感慨OpenResty的精致、完美和强大,简直是所有Nginx开发者“梦寐以求的至宝”。

由于agentzh对Nginx的运行机制了如指掌,OpenResty的核心部分——ngx_lua一个模块就涵盖了access/rewrite/content/log等多个处理阶段,再搭配上小巧灵活的Lua和高效的LuaJIT,我们就能够在更高级的业务层次上使用“胶水”代码来调用组合Nginx底层功能,轻松开发出丰富Web服务,极大地节约了宝贵的时间和精力。

当然,OpenResty并不只有ngx_lua,围绕着ngx_lua还有众多的库和辅助工具,构成了一个相当完善的生态环境,这些组件相互支撑,利用得当可以更好地提高生产效率。

OpenResty现在正处于蓬勃发展的阶段,今后的OpenResty也许不仅限于Nginx和WebServer,而将成为一个更通用的开发平台,工作语言也不仅限于Lua,可能还会有其他新的语言(例如agentzh正在做的edgelang和fanlang),让我们拭目以待。

“大事件”

2019年,Nginx身上发生了一件了不得的“大事件”:在独立运营了8年之后,Nginx.Inc公司正式被它的“竞争对手”F5Networks收购。

收购的价格并不算高,只有6.7亿美元。可以比较一下去年被微软收购的源码托管网站GitHub,出价是75亿。对于Nginx这个几乎占据了互联网一半份额的顶尖Web服务器来说,感觉实在是有点低。

这也算得上是开源软件商业化的又一个标志性案例了。再往前,还有IBM以340亿美元收购RedHat、Sun以10亿美元收购MySQL(但后来又被Oracle收购)。昔日的一个个免费开源明星软件,怀着使命和愿景陆续走向商业化,但又在这条道路上最终倒下。理想终究败给了现实,令人不得不感慨这个“美丽而又残酷”的世界。[出自《进击的巨人Season1》片尾曲“美しき残酷な世界”。]

目前“硕果仅存”的同级知名开源软件应该只有Apache和Linux了。值得注意的是它们并没有成立商业公司,而是以基金会的形式运作,通过赞助和会员制等形式来筹措资金。另一方面也确实是因为它们的“体量”足够大,能够把“免费”“开源”的大旗继续打下去。

当然,Nginx和F5都发表了官方声明,表示Nginx会继续保持开源,共同维护开源社区的活力。在这一点上我选择相信他们,相信被收购后的Nginx会有更加光明的未来。

再补上一句私心话:“还等着在Nginx上跑HTTP/3呢。”

新版的变化

我一直是C++语言的坚定拥护者,很早就在积极推动使用C++开发定制Nginx,但这几年下来感觉“收效甚微”:一是C语言开发的惯性太大,二是操作系统对C++标准支持不力,无法用上11/14的特性。几番折腾下来,有点“心灰意冷”了。

所以这次的新版就改以C作为主力开发语言,同时“忍痛割爱”,大幅度删减了各章节的C++代码,C++的全部内容都压缩到一个章节里(第20章),不再详细介绍C++封装类的具体实现和内部工作原理,也许这样会更贴近目前Nginx开发的现状。


其他推荐