一、概述
Tengine 是一个由淘宝网发起的 Web 服务器项目。它基于 Nginx 然后针对大访问量网站的需求,添加了很多高级功能和特性,从 2011 年 12 月开始,Tengine 正式开源。Tengine 的性能和稳定性已经100多家大型网站如淘宝网,天猫商城、Youku、AliExpress、 Lazada 、Alibaba Cloud等得到了生产型检验。Tengine定位于打造一个高效、稳定、安全、易用的 Web 平台 目前已100% 兼容 Nginx的配置语法和模块,用户还可无缝迁移现有Nginx服务,同时享受增强功能,但需要注意安全漏洞,近期安全通报显示部分版本存在漏洞,请及时加固更新。另外,Tengine 没有像 Spring Cloud Gateway 那样为微服务架构提供原生支持(比如直接与 Eureka、Consul 等服务发现组件集成),但 Tengine 仍然可以通过 反向代理的方式 与微服务架构组合使用,尤其是在 Kubernetes 环境中,通过 Ingress 的方式实现负载均衡和路由功能。
- 继承 Nginx-1.24.0 的所有特性,100% 兼容 Nginx 的配置;
- 动态模块加载(DSO)支持。加入一个模块不再需要重新编译整个 Tengine,支持动态调整 servers, locations and upstreams而无需 reloading 或restarting worker 进程;
- 支持HTTP/3 (QUIC v1 和draft-29方式);支持异步SSL/TLS模式,可以使用QAT(intel QuickAssist Technology)来卸载和加速SSL,显著提升性能。
- 支持基于标准和自定义的 HTTP header, cookie, query and weights.来动态配置路由,可动态添加和追加自定义的http 头和请求参数;无需重新加载引擎的就可向HTTP响应添加自定义报头;支持动态配置TLS versions, timeout setting, SSL Redirects, CORS and enabling/disabling robots for the server and location;支持proxy_connection的隧道连接;
- 更多负载均衡算法支持。如会话保持,一致性 hash 等;
- 输入过滤器机制支持。通过使用这种机制 Web 应用防火墙的编写更为方便;
- 动态脚本语言 Lua 支持。扩展功能非常高效简单;
- 支持管道(pipe)和 syslog(本地和远端)形式的日志以及日志抽样;
- 组合多个 CSS、JavaScript 文件的访问请求变成一个请求;
- 可以对后端的服务器进行主动健康检查,根据服务器状态自动上线下线;
- 自动根据 CPU 数目设置进程个数和绑定 CPU 亲缘性;支持内核旁路的高速UDP传输,
- 监控系统的负载和资源占用从而对系统进行保护,包括: asynchronous log & rollback, DNS caching, memory usage;
- 显示对运维人员更友好的出错信息,便于定位出错机器;
- 更强大的防攻击(访问速度限制)模块;
- 更方便的命令行参数,如列出编译的模块列表、支持的指令等;
- 可以根据访问文件类型设置过期时间;
- 支持直接提交未缓冲的数据给到HTTP和FastCGI 后端服务器,避免了本地缓冲需先将文件数据写入磁盘缓冲区,然后再从缓冲区读取数据进行处理,这样可以减少磁盘I/O操作,提高文件处理速度;
关联资源:tengine仓库、tengine产品官网、tengine-ingress统一接入[Tengine-Ingress结束](https://mp.weixin.qq.com/s/VbNXHvjdCD07LOXaOIGXCw)
二、架构及原理
2.1 tengine对Nginx的主要改进模块
2.2 改进优化
1)计时器优化
Timers(计时器)是网络服务器中一个很重要的基础设置,它用来管理读写超时和应用逻辑的超时等。其常见操作有添加超时、删除超时以及查找最小的超时值。Nginx使用Red-black tree(红黑树)作为其计时器的数据结构。红黑树对应于添加、删除和查找最小值的算法复杂度都是O(logn)。在Tengine中,我们将Nginx的计时器数据结构改为了4-heap
(四叉最小堆)。四叉堆是二叉堆的变种,比二叉堆有更浅的深度和更好的CPU Cache命中率。最小堆的添加、删除的复杂度和红黑树一样都是O(log n),但在查找最小值时,它的算法复杂度是0(1),即只要取出堆顶的第一个元素即可,因此比Nginx的红黑树更适合频繁获取最小值的场景,特别是在处理大量连接时,用最小堆性能提升比较明显。
2)浏览器和爬虫的判断优化
判断浏览器的类型是Web服务器的一个常见需求。Nginx中判断浏览器的方法是对关注的浏览器种类在User-Agent头中做暴力查找(strstr) 。strstr本身的算法复杂度是0(n2),Nginx查找的是多个串,因此其最终算法复杂度是0(n3)。随
着现在移动端的浏览器增多,原有模块的复杂度成指数增长,性能不高。在Tengine中,我们开发了一个全新的user_agent模块
,使用了trie
(前缀
树)来搜索多个可能的浏览器匹配串。它将所有的匹配字符串构造出一个自动机,每次匹配,它的算法复杂度只需要O(n)
。因此复杂度不会随着匹
配串数量的增加而增加。
3)自动绑定CPU亲缘性
原有的Nginx CPU绑定需要手工操作,在Tengine中我们将Worker进程和CPU进行自动绑定,可以减少因CPU的Cache失效带来的性能损失,从而提高性能。另外,这样也减少了运维配置的工作量。
4)Lua模块(ngx_lua) 将Lua直接嵌入进Nginx核中
这样,借助Lua的协程和Nginx的事件模型实现同步、非阻塞的I/O操作,开发者在Nginx配置文件中可串行同步编写Lua脚本来处理业务逻辑,既可以用它来黏合各种上游(Proxy、Drizzle、Redis、Memcached等)的输出,也可以使用它的Cosocket接口来编写访问上游的客户端。得益于Lua解释器极低的开销和JIT技术(LuaJIT),用户不用编写复杂的C模块就能获得极高的吞吐性
能。也可以动态更改逻辑,不用再重新编译Nginx代码,从而带来了极大的灵活性。
Lua模块在初始化时为每个Nginx工作进程创建一个Lua/LuaJIT实例(LuaVM),同一进程处理的所有请求将共享该实例
,并且Lua模块将用户Lua代码包装为协程工厂缓存在Nginx内,一个请求到来时协程工厂为它分配一个独立协程来运行业务逻辑。在需要进行阻塞的I/O操作时,Lua模块自动将I/O操作委托给Nginx的事件处理模型,并保存正在运行的协程上下文,返回到Nginx工作进程中处理其他请求,等到I/O操作完成时,又会恢复该协程继续运行。
5)动态模块支持
Tengine中加入了动态模块功能,对模块实现了动态编译,加入模块不再需要静态编译整个Tengine代码。使用方法类似Apache,在使用时可以当场
动态编译想加人的模块,非常方便。
- 1.我们提供类似apxs的编译工具,将模块编译成动态链接库。
- 2.在Tengine启动时通过
动态链接库
读入模块的模块结构体,这个结构体包含了模块处理的所有信息。- 3.Tengine有内置的模块加载顺序表,也可在配置文件中显式的指定模块的加载顺序,保证模块加载顺序正常。
- 4.Tengine内部通过两个版本号(Major和Minor)来控制动态链接库(.so文件)的前后兼容性。当Major版本号相同时,较新版本的Tengine兼容较旧版本的.so文件(Tengine的Minor大于.so文件)。只有当Tengine的API发生重大变化时,Major的版本号才发生变化。增加新的API只会增加Minor版本号。
2.3 Tengine-Ingress 高性能高可用的云原生网关架构
三、部署配置
3.1 编译安装
yum -y install gcc pcre-devel openssl-devel
useradd -r -s /sbin/nologin nginx
#下载源码包编译
cd /usr/local/src
wget https://tengine.taobao.org/download/tengine-2.4.1.tar.gz
md5sum tengine-2.4.1.tar.gz #8a3c754741539723af977246dfb260dd
tar xf tengine-2.4.1.tar.gz
cd tengine-2.4.1/
#或
git clone https://github.com/alibaba/tengine.git
cd tengine./configure --prefix=/usr/local/tengine-2.4.1 --user=nginx --group=nginx --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_stub_status_module --with-http_gzip_static_module --with-pcremake && make install
ln -s /usr/local/tengine-2.4.1/sbin/* /usr/sbin/
#启动
nginx#动态添加 Lua 模块
yum -y install lua-devel
./configure --prefix=/usr/local/tengine-2.4.1 --user=nginx --group=nginx --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_stub_status_module --with-http_gzip_static_module --with-pcre --with-http_lua_module=shared
#将动态模块的so文件拷贝到目标目录,这个目录可以通过'--dso-path'设置。默认是在Tengine安装目录下面的modules目录
make dso_install
#或如下这样简化生产动态模块,--add-dynamic-module 参数编译模块
./configure --add-dynamic-module=/path/to/module
make modules
#更新配置文件以加载 Lua 模块,生成的动态模块文件通常位于 objs/ 目录下
vim /usr/local/tengine-2.4.1/conf/nginx.conf #如下所示
dso {load ngx_http_lua_module.so;
}
#或配置文件中使用 load_module 指令加载模块:
server {listen 443 ssl http2; # 启用 HTTP/2server_name example.com;load_module modules/ngx_http_lua_module.so;ssl_certificate /path/to/cert.pem;ssl_certificate_key /path/to/key.pem;location / {root /usr/share/nginx/html;}
}
#检查配置并重启 Tengine:
nginx -t
nginx -s reload#Concat 模块:Concat 模块用于合并多个文件在一个响应报文中,类似于 Apache 的 mod_concat 模块。这有助于减少 HTTP 请求数量,提高网站加载速度和用户体验
./configure --help | grep http_concat #检查当前tengine支持的Concat 模块版本
#编译
./configure --prefix=/usr/local/tengine-2.4.1 --user=nginx --group=nginx --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_stub_status_module --with-http_gzip_static_module --with-pcre --with-http_lua_module=shared --with-http_concat_module=shared
make dso_install
#完成后,更新配置
location /static/css/ {concat on;concat_max_files 20;
}location /static/js/ {concat on;concat_max_files 30;
}
#加载
nginx -t
nginx -s reload## 查看加载模块
tengine -V 2>&1 | grep dynamic
3.2 配置示例
worker_processes auto;
worker_rlimit_nofile 65535;
events {worker_connections 65535;
}
http {
#以备日志切割
log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" '' "$http_user_agent" "$http_x_forwarded_for"';access_log /var/log/nginx/access.log main;
gzip_min_length 1k;# 负载均衡,默认轮询:请求被依次分配给每个服务器;IP 哈希(ip_hash):根据客户端 IP 地址分配流量,使同一 IP 的用户访问同一台服务器
upstream backend {server 172.18.1.101:8080 weight=5; # 权重为5server 172.18.1.102:8080 weight=3; # 权重为3server 172.18.1.103:8080 backup; # 备用服务器
}
#或
upstream backend {ip_hash; # 启用 IP 哈希server 172.18.1.101:8080;server 172.18.1.102:8080;
}# 开启健康检查,rise=2:连续检查 2 次成功视为健康;fall=5:连续检查 5 次失败视为故障;timeout=1000:检查超时时间(单位毫秒);check_http_send:发送的 HTTP 请求内容;check_http_expect_alive:期望的响应状态码。check interval=3000 rise=2 fall=5 timeout=1000 type=http;check_http_send "HEAD / HTTP/1.0\r\n\r\n";check_http_expect_alive http_2xx http_3xx;# 使用负载均衡
server {listen 80;location / {proxy_pass http://backend;}
}
#waf示例
location / {access_by_lua_block {#Lua 脚本实现 WAF 规则local args = ngx.req.get_uri_args()if args["id"] and tonumber(args["id"]) < 0 thenngx.exit(403)end}
}
#流量限速:限制每个 IP 的并发请求数或每秒请求数,防止恶意请求或流量突增
http {# 定义限速区域,$binary_remote_addr 表示根据客户端 IP 地址限速,限速区域大小为 10MBlimit_req_zone $binary_remote_addr zone=req_zone:10m rate=10r/s; server {listen 80;location / {limit_req zone=req_zone burst=5; # 每秒限制 10 个请求,允许 5 个突发请求proxy_pass http://backend;}}
}#缓存加速:可以将后端服务器的响应存储到本地磁盘或内存中,从而加速用户的访问
http {proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=1g inactive=60m use_temp_path=off;server {listen 80;location / {proxy_cache my_cache; # 启用缓存proxy_cache_valid 200 302 10m; # 状态码 200 和 302 的内容缓存 10 分钟proxy_cache_valid 404 1m; # 状态码 404 的内容缓存 1 分钟proxy_pass http://backend;}#防盗链,定义允许访问的 Referer,none:允许没有 Referer 的请求;blocked:允许 Referer 被代理隐藏的请求。location /images/ {valid_referers none blocked *.example.com;if ($invalid_referer) {return 403;}}
}
#日志切割
vi /etc/logrotate.d/nginx #保存半年
/var/log/nginx/*.log {dailymissingokrotate 190compressdelaycompressnotifemptycreate 0640 www-data www-datasharedscriptspostrotate/usr/local/sbin/nginx -s reopenendscript
}
3.3 图形管理工具
1)Tengine Dashboard功能
2)Prometheus监控方案+Grafana可视化面板
3)ELK日志分析方案
四、应用案例
4.1、电商场景
1)大促期间动态限流配置
upstream mall {check interval=3000 rise=2 fall=3;server 172.18.1.101:8080;
}
location ~* \.(jpg|css)$ {expires 30d;
}
2)静态资源加速方案
4.2 金融行业
1)双向证书校验实现
2)敏感数据加密传输
3)长连接保持配置
4.3 MQTT协议支持案例
五、FAQ
1)兼容性问题
2)与原生Nginx模块冲突处理
3)版本升级注意事项
4)性能问题
5)高并发场景优化建议
6)TIME_WAIT状态处理
7)动态加载模块失败排查
8)自定义日志格式配置