vlambda博客
学习文章列表

测试也应该懂的Nginx



对于服务端测试同学来说Nginx并不陌生,根据Netcraft web服务器排名显示,自2019年4月开始,Nginx的市场份额一直位于榜首。在互联网高速发展的时代,Nginx相对于其他Web服务器有绝对的优势赢得市场的认可。接下来让我用一篇文章带你了解Nginx,希望测试开发的路上你可以用的上~

01


简介



Nginx 是什么

Nginx 是一个跨平台的web服务器,可用在Linux、macOS、Windows等操作系统上。Nginx以性能为王,这也是它如今被宠爱的主要原因。

Nginx为什么受欢迎

高并发、高性能
大概有多高呢,一个32核,64G内存的服务器,可以轻松达到上千万的并发连接,如果是简单的静态资源请求,可以达到上百万的qps
高扩展性
Nginx的模块化设计极具扩展性,无论是自带模块还是第三方模块都很容易被使用。
高可靠性
Nginx持续运行时间长, 且常用模块都很稳定,worker进程间相互独立,某个worker出错时可以迅速拉起新的worker。
热部署
master管理进程和worker工作进程的分离设计,让Nginx可以在不停止服务的情况下重启升级更新Nginx配置。
BSD许可证
自由的BSD许可协议,允许用户免费使用,同时还可修改源码,用于商业

02


Nginx应用场景



静态资源服务

通过本地文件系统提供服务。
反向代理服务
通过Nginx的强大性能来实现缓存加速、负载均衡的功能。
API服务
如 OpenResty,基于 Nginx 的可伸缩的 Web 平台,使用Lua语言,来提供完整的API服务。

03


Nginx安装



两种方式,直接安装和编译安装,直接安装简单便捷,但是Nginx中许多模块并不是默认开启的,或者想添加功能更加强大的第三方模块就必须通过编译安装的方式才能编译进来。

直接安装

linux
yum install nginx
mac
brew install nginx

编译安装

以下安装基于macOS系统,仅供参考

mkdir nginx-src
cd nginx-src
wget http://nginx.org/download/nginx-1.17.8.tar.gz
wget https://ftp.pcre.org/pub/pcre/pcre-8.41.tar.gz
wget https://www.openssl.org/source/openssl-1.1.0g.tar.gz
tar zxvf *.gz
cd nginx-1.17.8
./configure --with-pcre=../pcre-8.41/ --with-http_ssl_module --with-openssl=../openssl-1.1.0g --prefix=/usr/local/nginx
make
make install

说明:
pcre库
支持正则表达式,Nginx的HTTP模块需要靠它来解析正则表达式,所以编译Nginx时必须使用。
OpenSSL库
如果服务器不只是要支持HTTP,还需要在更安全的SSL协议上传输HTTP,那么需要拥有OpenSSL
使用./configure --help 能够查看nginx 相关模块状态
--with-xxxx 选项的功能或者模块默认都是关闭状态,使用with可以启用
--without-xxxx 选项的功能或者模块默认都是启用状态
测试也应该懂的Nginx

04


Nginx的组成



编译安装成功后,进入安装目录 /usr/local/nginx,几个重要文件。

nginx二进制可执行文件
sbin目录下,由各模块源码编译出的一个可执行文件,Nginx升级或添加模块时,可在编译成功后直接替换此文件,实现热部署
nginx.conf 配置文件
conf目录下的一个配置文件。这个是Nginx服务器的主配置文件。通过指令来配置这个文件能达到控制Nginx,实现反向代理,负载均衡等等
access.log 访问日志
logs目录下,记录每一条http请求信息
error.log 错误日志
logs目录下,错误日志信息,用于定位问题

05


Nginx命令行



nginx #不带参数,使用默认配置文件启动nginx
nginx -c #使用指定配置文件启动nginx
nginx -p dir #指定Nginx的安装目录
nginx -g directives #在配置文件之外设置全局指令
nginx -s #向主进程发送信号
nginx -s stop #强制关闭,暴力
nginx -s quit #优雅的关闭方式,退出前完成已经接受的连接请求
nginx -s reload #重新加载配置,使用心得配置后会启动新的worker,正常退出旧的worker
nginx -s reopen #重新打开日志文件也可以使用直接发送信号的方式来操作进程
kill -s SIGTERM pid #强制停止,同 nginx -s stop 效果一样
kill -s SIGINT pid #强制停止,同 nginx -s stop 效果一样
kill -s SIGQUIT pid #发送信号给master,优雅停止,同 nginx -s quit 效果一样
kill -s SIGWINCH pid #优雅停止worker,可以使用WINCH信号
kill -s SIGUSR1 pid #同 nginx -s reopen 效果一样
kill -s SIGUSR2 pid #平滑升级nginx时,此命令会将nginx.pid重命名为nginx.pid.oldbin
nginx -h #查看其他参数,使用 -h 查看

基于以上命令行了解,接下来我们一分钟快速入门 nginx 重载、热部署、日志切割

重载

修改配置文件后,可使用此命令进行重载
nginx -s reload
reload信号通知nginx重新载入配置文件,除了reload重载以外,nginx只会在启动时载入一次配置文件,之后配置文件的修改不会对已运行的nginx进程生效。当运行这个重载命令时,master进程会尝试读取配置文件,如果配置文件没有问题,master进程会启动新的worker进程来运行新的配置文件并处理请求,并且通知老的worker进程不再处理新的请求并在处理完当前任务后退出。如果配置文件存在问题不能执行,master进程会回退老配置文件继续工作,不会导致nginx进程整个异常退出。

热部署

热部署,即在不打断用户请求的情况下更新nginx版本
如我的安装目录 /usr/local/nginx

  1. 进入/usr/local/nginx/sbin 目录备份旧的nginx二进制文件。
    cp nginx nginx.old

  2. 将新的二进制文件导入(新版本或增加新的功能模块编译后得到的二进制文件),查看此时nginx进程,还是原来的旧进程。
    测试也应该懂的Nginx

  3. 使用kill -USR2 79545开始平滑升级,此时新旧进程会同时运行,但是旧的进程不再监听端口。
    测试也应该懂的Nginx

  4. 使用kill -WINCH 79545优雅的关闭旧的worker进程,此时两个主进程都还在,但是旧的worker进程已经关闭。
    测试也应该懂的Nginx
    以上已经成功完成热部署,超级开心。如果升级后发现新版本有问题,这时我们需要完成回退操作。

  5. 回退
    先用之前备份的 nginx.old 文件覆盖现有 nginx 文件,然后使用
    kill -HUP 79545命令拉起旧版本的worker进程。此时再次查看nginx进程,新旧版本master、worker进程同时运行着,再次使用kill -WINCH 79573 优雅的关闭新版本worker进程,回退完毕。
    测试也应该懂的Nginx

日志切割
  1. 创建用于日志切割的脚本,脚本内容大致如下:

#!/bin/bash
LOGS_PATH=/usr/local/nginx/logs/history
CURL_LOGS_PATH=/usr/local/nginx/logs/
#获取昨天的日期
YESTERDAY=$(date -d "yesterday" +%Y-%m-%d)
#分割日志
mv ${CURL_LOGS_PATH}/access.log ${LOG_PATH}/access_logs_${YESTERDAY}.log
mv ${CURL_LOGS_PATH}/error.log ${LOG_PATH}/error_logs_${YESTERDAY}.log
#向Nginx主进程发送USR1信号,重新打开日志文件
kill -USR1 $(cat /usr/local/nginx/logs/nginx.pid)
  1. 配置crontab 定时任务执行脚本,crontab的使用可以自行了解下。
    如 每天凌晨00:00定时执行这个脚本

#编辑定时任务
crontab -e
#增加下面内容保存即可
00 00 * * * /bin/bash /脚本存放目录/脚本名称.sh

这就实现了每天定时执行脚本,完成日志按天切割。

---------接下来是实战时间-------

06


一个简单的静态资源服务



直接来看nginx.conf配置:

测试也应该懂的Nginx
listen
监听端口
service_name
主机名称,一个HTTP请求时,Nginx会取出header头中的Host,与每个server中的 server_name进行匹配,以此决定到底由哪一个server块来处理这个请求
location
根据用户请求中的URI来匹配 uri,如果匹配,就选择 location{}块中的配置来处理用户请求。具体匹配方式此处不啰嗦
啰嗦一下 location 文件路径定义
以alias方式设置资源路径
alias会把location后面配置的路径丢弃掉,把当前匹配到的目录指向到指定的目录。如上图,如果访问 nginxdemo.wudi.pub:8080/test, 用户实际访问的是安装目录下的html/index.html.
以root方式设置资源路径
root则不然,它会根据完整的URI请求来映射如上图,如果访问 nginxdemo.wudi.pub:8080/test, 用户实际访问的是安装目录下的html/test/index.html.
index
接收到请求后,默认配置,Nginx首先会尝试访问 /index.html文件,如果可以访问,就直接返回文件内容结束请求。
autoindex
打开目录浏览功能,很多时候nginx服务就是为了用来下载文件的,网上很多下载服务都是这样共享静态资源
测试也应该懂的Nginx
以上就是一个简单的静态资源服务相关配置。顺便说一下,当静态资源比较大时,我们还可以通过 gzip来提高页面加载速度。如:

gzip on #开启或者关闭gzip模块
gzip_min_length 1 #大于1k压缩,默认0,不管页面多大都压缩
gzip_comp_level 2 #gzip压缩比/压缩级别,压缩级别 1-9,级别越高压缩率越大,当然压缩时间也就越长(传输快但比较消耗cpu)
gzip_types text/plain #设置压缩哪种类型文件

详细说明文档在这里:爱心文档(https://nginx.org/en/docs/http/ngx_http_gzip_module.html#gzip)

07


反向代理负载均衡服务



测试也应该懂的Nginx

什么是反向代理

反向代理指的是以代理服务器接收用户的的访问请求,代理用户向内部服务器重新发起请求,最后把内部服务器的响应信息返回给用户。

为什么使用反向代理
  1. 可以起到保护网站安全的作用,因为任何来自Internet的请求都必须先经过代理服务器。

  2. 通过缓存静态资源,加速Web请求。

  3. 实现负载均衡

负载均衡的功能

对用户的访问请求进行调度管理、压力分担
接下来我们配置一个最简单的负载均衡反向代理服务,直接来看nginx.conf配置:
测试也应该懂的Nginx

upstream 模块
负载均衡策略

在有些场景下,我们可能会希望来自某一个用户的请求始终落到固定的一台上游服务器中。例如,假设上游服务器会缓存一些信息。这时我们可以使用ip_hash。列举下Nginx的5种负载均衡策略:
1. 轮询(默认)
每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。

upstream backserver { 
server 192.168.0.1; 
server 192.168.0.2; 

2. 指定权重
指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。

upstream backserver { 
server 192.168.0.1 weight=2; 
server 192.168.0.2 weight=1; 

3. IP绑定 ip_hash
每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。

upstream backserver { 
ip_hash; 
server 192.168.0.1:88; 
server 192.168.0.2:80; 

4. fair(第三方)
按后端服务器的响应时间来分配请求,响应时间短的优先分配。

upstream backserver { 
server server1; 
server server2; 
fair; 

5. url_hash(第三方)
按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。

upstream backserver { 
server squid1:3128; 
server squid2:3128; 
hash $request_uri
hash_method crc32; 

反向代理配置
proxy_pass http://loca#此配置项将当前请求反向代理到URL参数指定的服务器上,我们使用了upstream配置负载均衡后,可以直接使用upstream块,如上图 http://local
proxy_set_header #生成发往上游的请求头
proxy_cache zone|off #off关闭proxy_cache功能,zone为用于存放缓存的内存区域名称
proxy_cache_key #指定对什么参数进行缓存
proxy_cache_valid #配置nginx cache中的缓存文件的缓存时间,上图表示,对于状态为200、304、302的缓存文件的缓存时间是1天
#还有个几个常用配置
proxy_method POST#转发时的协议方法名
proxy_set_body #生成发往上游的请求体
proxy_pass_request_body on|off #确定是否向上游服务器发送HTTP包体部分

以上就是一个简单的负载均衡反向代理配置,想了解更多的模块配置,可查阅 爱心文档(https://nginx.org/en/docs/http/ngx_http_proxy_module.html)

08


你可能还想了解的内容



nginx 请求流程 11 个阶段

序号 阶段 指令
1 POST_READ realip
2 SERVER_REWRITE rewrite
3 FIND_CONFIG
4 REWRITE rewrite
5 POST_REWRITE
6 PRE_ACCESS limit_conn, limit_req
7 ACCESS auth_basic, access, auth_request
8 POST_ACCESS
9 PRE_CONTENT try_files
10 CONTENT index, autoindex, concat
11 LOG access_log

第一个阶段 POST_READ 阶段。ngx_http_realip_module 模块就工作在这个阶段,它可以根据 X-Forwarded-For 等头部,提取出客户端的真实 IP。

第二个阶段 SERVER_REWRITE 阶段。ngx_http_rewrite_module 模块工作在这个阶段,用于实现 server{} 配置里的 url 重写功能。

第三个阶段 FIND_CONFIG 阶段。没有模块能够介入这个阶段。这个阶段的功能是根据请求的 url 查找到匹配的 locaiton{} 。

第四个阶段 REWRITE 阶段。ngx_http_rewrite_module 也工作在这个阶段,用于处理 location{} 配置里的 url 重写。

第五个阶段 POST_REWRITE 阶段。没有模块能够介入这个阶段。这个阶段完全是为了配合 ngx_http_rewrite_module 模块。

第六个阶段 PREACCESS 阶段。ngx_http_limit_conn_module 和 ngx_http_limit_req_module 模块工作在这个阶段,它们分别实现并发连接数限制和请求速率限制功能。可以通 limit_conn、limit_req配置并发请求连接数,请求速率。

第七个阶段 ACCESS 阶段。ngx_http_access_module 和 ngx_http_auth_basic_module 工作在这个阶段,它们分别实现访问控制和基本认证的功能。下面会有auth_basic、auth_request例子。

第八个阶段 POST_ACCESS 阶段。没有模块能够介入这个阶段。这个阶段是为了配合 ACCESS 阶段。

第九个阶段 PRECONTENT 阶段。ngx_http_mirror_module 工作在这个阶段,实现流量镜像的功能。

第十个阶段 CONTENT 阶段。这是最关键的一个阶段,也是外部模块最喜欢介入的阶段。ngx_http_index_module、ngx_http_static_module 工作在这个阶段实现返回静态页面的功能。后面分析代码,我们也是重点关注这个阶段。

最后一个 LOG 阶段。ngx_http_log_module 模块工作在这个阶段,实现输出访问日志的功能。

一些其他的模块
referer

防盗链referer模块。正常用户请求URL时,http请求的头部会通过referer头部,将网站当前页面的URL带上,告诉服务器请求是哪个页面发起的,而referer模块就是用来拒绝非正常的网站访问我们站点的资源.
Example:

location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
{
valid_referers none blocked nginxdemo.wudi.pub;
if ($invalid_referer) {
return 404;
}
expires 30d;
}

第1行是需要防止盗链的文件类型,或者其他location;
第3行是放行网站的域名,可以添加多个;
如果是盗链会直接返回404。

auth_basic

使用HTTP基本认证(HTTP Basic Authentication)协议的用户名密码验证。配置后用户访问URL会先弹出登陆验证。
首先使用 htpasswd -c /home/pwd/m_test.passwd username 生成一个密码文件。注:htpasswd指令用来创建和更新用于基本认证的用户认证密码文件,然后再进行配置

location / {
auth_basic "input you user name and password";
auth_basic_user_file /home/pwd/m_test;
}
auth_request

第三方认证,编译Nginx时需要添加该模块 --with-http_auth_request_module,该模块可以将客户端输入的用户名、密码 username:password 通过Base64编码后写入Request Headers中,然后通过第三方程序解码后跟数据库中用户名、密码进行比较,Nginx服务器通过 header的返回状态判断是否认证通过。
Example:

server {
...
auth_request /auth;
location /auth {
proxy_pass http://auth.server.com/HttpBasicAuthenticate.php;
#请求的验证服务器,配置此处略。。。
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
}
}
split_client

实现A/B测试的模块,有很多地方可以用得上。
功能:

  1. 基于已有变量创建新变量,可为实现AB测试提供更多的可能性。

  2. 对已有变量的值执行MurmurHash2算法得到32位整型哈希数字,记为hash。

  3. 32位无符号整型的最大数字2^32-1,记为max。

  4. 哈希数字与最大数字相除hash/max,可以得到百分比percent。

  5. 配置指令中设置了各个百分比构成的范围,如0-1%,1%-5%等,及范围对应的值。

  6. 当percent落在哪个范围里,新变量的值就对应着其后的参数。
    Example:

http {
split_clients "${remote_addr}" $variant {
0.5% .one;
2.0% .two;
* "";
}
server {
location / {
index index${variant}.html;
}
}

上面例子,就是使用remote_addr进行A/B测试,不同的 remote_addr会访问不同的链接,达到A/B测试的目的。

geo
geo $country {
default ZZ;
proxy 116.196.115.53;
127.0.0.0/24 US;
127.0.0.1/32 RU;
10.1.0.0/16 RU;
192.168.1.0/24 UK;
}
server {
listen 80;
server_name shop.com.cn
location /{
return 200 '$country\n';
}
}

通过上面配置,我们能够获取$country变量,我们就能够根据这个变量进行动态实现限速或其他用户访问控制。

geoip

使用变量获得用户的地理位置。如geoip_country, geoip_proxy等,我们能够通过这些变量进行区域访问限制,这里跳过,感兴趣的小伙伴可以深入了解下 爱心文档(https://nginx.org/en/docs/stream/ngx_stream_geoip_module.html)

End

以上就是关于nginx的入门级教程,想深入了解更多强大功能的小伙伴可以继续学习。测试不需要懂什么,测试啥都得懂~希望大家阅读后能有那么一丢丢收获~

送上更多精彩好文




点“在看”给我一朵小黄花测试也应该懂的Nginx