Nginx第三方模块试用及十个参数性能优化
最近试用了几个@agentzh写的第三方Nginx模块,甚为愉悦,没想到在 Nginx 可以玩很多技巧和扩展,分享一下。
本文尝试的几个模块大概分为:
- echo
- Memcached
- nginx
- lua
详细模块地址分别为:
- ngx_devel_kithttps://github.com/simpl/ngx_devel_kit
- set-misc-nginx-modulehttps://github.com/agentzh/set-misc-nginx-module
- memc-nginx-modulehttps://github.com/agentzh/memc-nginx-module
- echo-nginx-modulehttps://github.com/agentzh/echo-nginx-module
- lua-nginx-modulehttps://github.com/chaoslawful/lua-nginx-module
- srcache-nginx-modulehttps://github.com/agentzh/srcache-nginx-module
- drizzle-nginx-modulehttps://github.com/chaoslawful/drizzle-nginx-module
- rds-json-nginx-modulehttps://github.com/agentzh/rds-json-nginx-module
为了省事这里一股脑把上面的 module 全部下载好,一起编译。PS:如果更懒惰的可以尝试下openresty项目,它帮你打包好 Nginx 和一堆扩展模块,得感谢@agentzh。
这里编译和 drizzle 和 lua 模块,在编译 Nginx 之前需要设置一下这两个库的 LIB 和 INCLUDE 文件地址:
-- lua -- export LUA_LIB=/path/to/lua/lib export LUA_INC=/path/to/lua/include -- drizzle -- export LIBDRIZZLE_INC=/opt/drizzle/include/libdrizzle-1.0 export LIBDRIZZLE_LIB=/opt/drizzle/lib
Nginx 编译选项如下,请注意先后顺序:
./configure --prefix=/opt/nginx \ --with-pcre=../pcre \ --add-module=../ngx_devel_kit \ --add-module=../set-misc-nginx-module \ --add-module=../memc-nginx-module \ --add-module=../echo-nginx-module \ --add-module=../lua-nginx-module \ --add-module=../srcache-nginx-module \ --add-module=../drizzle-nginx-module \ --add-module=../rds-json-nginx-module
重新编译 Nginx 二进制,Nginx 需要 quit 再启动。而普通配置更新则 reload 即可:
1. kill -HUP `cat /path/nginx/logs/nginx.pid` 2. /path/nginx/sbin/nginx -s reload
PS:详细的模块编译可以参考各自模块的文档说明。
好吧,假设到这里已经安装和编译好 Nginx 以及一堆第三方扩展。下面开始尝试上面那些有趣的模块
场景 1:Helloworld
程序里 helloworld 很简单,nginx 的 hello 如何实现呢?
1. 最简单的helloworld location /hello1 { echo "hello 1111!"; } 2. 异步请求其他echo请求 location /hello2 { echo "hello 2222!"; echo_location_async /hello1; } 3. 输出GET请求参数,假设参数名是name,这里并对name参数进行解码 location /hello3 { set_unescape_uri $name $arg_name; set_if_empty $name "None"; echo "hello, $name!"; }
是不是很帅气有趣。例 2 中的 subrequest 是完全 non-blocking 的。echo 模块还有 timer 扩展,页面输出加载时间也是小 case 了。
场景 2:Memcached HTTP 接口
想当年哥为了兼容不同语言的 API,自己封装了 Memcached 操作,真是挺苦逼的,当时还非常羡慕 tokyotyrant 的 http 接口来着,现在用 memc 模块效果一样,还有 upstream 和 keepalive 功能哦 ~
location /memcached { set $memc_cmd $arg_cmd; set $memc_key $arg_key; set $memc_value $arg_val; set $memc_exptime $arg_exptime; memc_pass 127.0.0.1:11211; }
URL 访问类似 http://host:port/memcached?cmd=aaa&key=bbb&val=ccc
还需要自己封装么?完全的基于 url 编程了,不再考虑各自语言的 Memcached API,cluster 也简化了。
场景 3:数据库查询
这里用了 libdrizzle 模块,兼容 MySQL、Drizzle、SQLite 数据库。有时候为了一些查询或者接口,还需要用语言来封装一些数据库查询操作,现在直接通过 drizzle 模块可以很轻松的做到 ~~
upstream mysql { drizzle_server 127.0.0.1:3306 dbname=test user=smallfish password=123 protocol=mysql; } location ~ '^/mysql/(.*)' { set $name $1; set_quote_sql_str $quote_name $name; drizzle_query $sql; drizzle_pass mysql; rds_json on; } location /mysql-status { drizzle_status; }
场景 4:lua 扩展
内嵌 lua 语言,超级牛力哦 ~~~ 想干啥都可以了,而且还可以充分发挥 lua coroutine 的风骚,淘宝量子统计完全是 lua 扩展。
location /lua1 { default_type 'text/plain'; content_by_lua 'ngx.say("hello, lua")'; } # 请求另外的url location /lua2 { content_by_lua ' local res = ngx.location.capture("/hello1") ngx.say("data: " .. res.body) '; }
Lua 支持的选项很多,具体可参考官网 WIKI 文档。
嗯,这里尝试玩了几个模块,详细的例子可以参考各自的模块文档,里面都有详细的说明和选项配置。可以看出和传统编程有些微不同之处,完全是基于 URL 的 HTTP 接口,比传统的方式更为简单,更为清晰。还有 non-blocking 的实现,更见轻便。
1. 优化 Nginx 进程数量
配置参数如下:
worker_processes1;# 指定 Nginx 要开启的进程数,结尾的数字就是进程的个数,可以为 auto
这个参数调整的是 Nginx 服务的 worker 进程数,Nginx 有 Master 进程和 worker 进程之分,Master 为管理进程、真正接待“顾客”的是 worker 进程。
进程个数的策略:worker 进程数可以设置为等于 CPU 的核数。高流量高并发场合也可以考虑将进程数提高至 CPU 核数 x 2。这个参数除了要和 CPU 核数匹配之外,也与硬盘存储的数据及系统的负载有关,设置为 CPU 核数是个好的起始配置,也是官方建议的。
当然,如果想省麻烦也可以配置为worker_processes auto;
,将由 Nginx 自行决定 worker 数量。当访问量快速增加时,Nginx 就会临时 fork 新进程来缩短系统的瞬时开销和降低服务的时间。
2. 将不同的进程绑定到不同的 CPU
默认情况下,Nginx 的多个进程有可能运行在同一个 CPU 核上,导致 Nginx 进程使用硬件的资源不均,这就需要制定进程分配到指定的 CPU 核上处理,达到充分有效利用硬件的目的。配置参数如下:
worker_processes 4; worker_cpu_affinity 0001 0010 0100 1000;
其中worker_cpu_affinity
就是配置 Nginx 进程与 CPU 亲和力的参数,即把不同的进程分给不同的 CPU 核处理。这里的0001 0010 0100 1000
是掩码,分别代表第 1、2、3、4 核 CPU。上述配置会为每个进程分配一核 CPU 处理。
当然,如果想省麻烦也可以配置worker_cpu_affinity auto;
,将由 Nginx 按需自动分配。
3. Nginx 事件处理模型优化
Nginx 的连接处理机制在不同的操作系统中会采用不同的 I/O 模型,在 Linux 下,Nginx 使用 epoll 的 I/O 多路复用模型,在 Freebsd 中使用 kqueue 的 I/O 多路复用模型,在 Solaris 中使用 /dev/poll 方式的 I/O 多路复用模型,在 Windows 中使用 icop,等等。
配置如下:
events { use epoll; }
events
指令是设定 Nginx 的工作模式及连接数上限。use
指令用来指定 Nginx 的工作模式。Nginx 支持的工作模式有 select、 poll、 kqueue、 epoll 、 rtsig 和/ dev/poll。当然,也可以不指定事件处理模型,Nginx 会自动选择最佳的事件处理模型。
4. 单个进程允许的客户端最大连接数
通过调整控制连接数的参数来调整 Nginx 单个进程允许的客户端最大连接数。
events { worker_connections 20480; }
worker_connections
也是个事件模块指令,用于定义 Nginx 每个进程的最大连接数,默认是 1024。
最大连接数的计算公式如下:
max_clients = worker_processes * worker_connections;
如果作为反向代理,因为浏览器默认会开启 2 个连接到 server,而且 Nginx 还会使用 fds(file descriptor)从同一个连接池建立连接到 upstream 后端。则最大连接数的计算公式如下:
max_clients = worker_processes * worker_connections / 4;
另外,进程的最大连接数受 Linux 系统进程的最大打开文件数限制,在执行操作系统命令ulimit -HSn 65535
或配置相应文件后,worker_connections
的设置才能生效。
5. 配置获取更多连接数
默认情况下,Nginx 进程只会在一个时刻接收一个新的连接,我们可以配置multi_accept
为on
,实现在一个时刻内可以接收多个新的连接,提高处理效率。该参数默认是off
,建议开启。
events { multi_accept on; }
6. 配置 worker 进程的最大打开文件数
调整配置 Nginx worker 进程的最大打开文件数,这个控制连接数的参数为worker_rlimit_nofile
。该参数的实际配置如下:
worker_rlimit_nofile65535;
可设置为系统优化后的ulimit -HSn
的结果
7. 优化域名的散列表大小
http { server_names_hash_bucket_size 128; }
参数作用:设置存放域名( server names)的最大散列表的存储桶( bucket)的大小。 默认值依赖 CPU 的缓存行。
server_names_hash_bucket_size
的值是不能带单位 的。配置主机时必须设置该值,否则无法运行 Nginx,或者无法通过测试 。 该设置与server_ names_hash_max_size
共同控制保存服务器名的 hash 表, hash bucket size 总是等于 hash 表的大小, 并且是一路处理器缓存大小的倍数。若 hash bucket size 等于一路处理器缓存的大小,那么在查找键时, 最坏的情况下在内存中查找的次数为 2。第一次是确定存储单元的地址,第二次是在存储单元中查找键值 。 若报 出 hash max size 或 hash bucket size 的提示,则需要增加server_names_hash_max size
的值。
8. TCP 优化
http { sendfile on; tcp_nopush on; keepalive_timeout 120; tcp_nodelay on; }
第一行的sendfile
配置可以提高 Nginx 静态资源托管效率。sendfile 是一个系统调用,直接在内核空间完成文件发送,不需要先 read 再 write,没有上下文切换开销。
TCP_NOPUSH 是 FreeBSD 的一个 socket 选项,对应 Linux 的 TCP_CORK,Nginx 里统一用tcp_nopush
来控制它,并且只有在启用了sendfile
之后才生效。启用它之后,数据包会累计到一定大小之后才会发送,减小了额外开销,提高网络效率。
TCP_NODELAY 也是一个 socket 选项,启用后会禁用 Nagle 算法,尽快发送数据,某些情况下可以节约 200ms(Nagle 算法原理是:在发出去的数据还未被确认之前,新生成的小数据先存起来,凑满一个 MSS 或者等到收到确认后再发送)。Nginx 只会针对处于 keep-alive 状态的 TCP 连接才会启用tcp_nodelay
。
9. 优化连接参数
http { client_header_buffer_size 32k; large_client_header_buffers 4 32k; client_max_body_size 1024m; client_body_buffer_size 10m; }
这部分更多是更具业务场景来决定的。例如client_max_body_size
用来决定请求体的大小,用来限制上传文件的大小。上面列出的参数可以作为起始参数。
10. 配置压缩优化
我们在上线前,代码(JS、CSS 和 HTML)会做压缩,图片也会做压缩(PNGOUT、Pngcrush、JpegOptim、Gifsicle 等)。对于文本文件,在服务端发送响应之前进行 GZip 压缩也很重要,通常压缩后的文本大小会减小到原来的 1/4 - 1/3。
http { gzip on; gzip_buffers 16 8k; gzip_comp_level 6; gzip_http_version 1.0; gzip_min_length 1000; gzip_proxied any; gzip_vary on; gzip_types text/xml application/xml application/atom+xml application/rss+xml application/xhtml+xml image/svg+xml text/javascript application/javascript application/x-javascript text/x-json application/json application/x-web-app-manifest+json text/css text/plain text/x-component font/opentype application/x-font-ttf application/vnd.ms-fontobject image/x-icon; gzip_disable "MSIE [1-6]\.(?!.*SV1)"; }
这部分内容比较简单,只有两个地方需要解释下:
gzip_vary
用来输出 Vary 响应头,用来解决某些缓存服务的一个问题,详情请看我之前的博客:HTTP 协议中 Vary 的一些研究。
gzip_disable
指令接受一个正则表达式,当请求头中的 UserAgent 字段满足这个正则时,响应不会启用 GZip,这是为了解决在某些浏览器启用 GZip 带来的问题。
默认 Nginx 只会针对 HTTP/1.1 及以上的请求才会启用 GZip,因为部分早期的 HTTP/1.0 客户端在处理 GZip 时有 Bug。现在基本上可以忽略这种情况,于是可以指定 gzip_http_version 1.0 来针对 HTTP/1.0 及以上的请求开启 GZip。
站外相关阅读
相关阅读
- Frp 内网穿透,实现个人相册、视频随时随地查看
- 可高级自定义的开源 Piwigo 相册,您的照片管理、浏览专家【强烈推荐】
- OpenResty中的ngx.location.capture和ngx.location.capture_multi使用
- Chrome 67版本后无法拖拽离线安装crx扩展插件的解决办法
- MacType 美化 Windows10 字体,体验类 Mac 顺滑字体
- 无限空间相册管理软件 - 一刻相册
- NoteExpress 实现文献检索、下载、管理、Word参考文献自动引用
- Tomcat 配置导致启动时,项目加载两次的解决办法
- ECharts保存背景透明PNG图片
- Idea 中使用 JRebel 实现 SpringBoot 热部署