curl 工具的用法
HEAD请求:curl -I www.baidu.com
下载:curl -o 'filename' 'www,example.com/1.txt'
下载:curl -o 'filename' 'www,example.com/1.txt'
下载::curl -O 'www,example.com/1.txt'
代理:curl -xip:port 'https://www.google.com'
指定ip:curl -H 'Host:example.com' http://ip/1.txt
指定user-agent:curl -A 'Chrome/57' 'https://www.baidu.com'
指定referer:curl -e 'http://www.123.com 'https://www.baidu.com'
发送数据:curl -d 'a=1&b=2' -X POST http://www.example.com/aa
nginx配置文件详解
配置文件结构
全局配置(user,worker_processes、error_log、pid)
events(网络连接相关,worker_connections)
http(最重要部分,大部分功能都放在这里)
server(虚拟主机相关)
locationn(server里面)
配置结构
Nginx配置文件
#user nobody; #启动nginx的时候以那个user去运行Nginx
worker_processes 1; #启动后,多少个进程
#error_log logs/error.log cirt; #定义错误日志,cirt级别高,只记录重要的错误日志
#error_log logs/error.log notice; #定义错误日志notice级别。会记录很多不需要的东西,不建议占用磁盘空间
#error_log logs/error.log info; #定义错误日志info级别
#pid logs/nginx.pid; #定义nginx的pid存放位置
events { #网络连接部分
worker_connections 1024; #定义每个work_process同时开启的最大连接数,既允许最多只能有这么多连接。
accept_mutex on; #当某一个时刻只有一个网络连接请求服务器时,服务器上有多个睡眠的进程会被同时叫醒这样会损耗一定的服务器性能。NGINX中的accept_mutex设置为On。将会对多个Nginx(worker processer)接受连接时进行序列化,防止多个进程争抢资源,默认就是On
mulit_accept on; #nginx worker processer可以做到同时接收多个新到达的网络连接,前提是把该参数设置为on,默认为Off,既每个worker process一次只能接收一个新到达的网络连接
use epoll; #Nginx服务器提供了多个事件驱动器模型来处理网络消息,其支持的类型:select、poll kqueue、poll、kqueue、epoll、rtsing、/dev/poll
select:只能在windows下使用。这个事件建模不建议在高负载的系统使用
kqueue: 这种方式在FreeBSD 4.1+,OpenBSD2.9+,NetBSD 2.0和MacOS x系统中是最高效的
epoll: 这种方式是Linux 2.6+内核中最高效的方式
rtsig: 实时信号,可用在Linux 2.2.19的内核中,但不实用在高流量的系统中
/dev/poll:Solaris 7 11/99+,HP/UX 11.22+,IRIXX 6.5 15+,AND Tru64 UNIXX 5.1a+操作系统
eventport:Solaris 10 最高效的方式
}
http {
include mime.types; 定义Nginx能识别的网络资源媒体类型(如文本,html,js,css,流媒体等)
default_type application/octet-stream; 定义默认的type,如果不定义改行,默认为text/plain
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"'; 定义访问日志格式,main为日志格式的名字
#access_log logs/access.log main; 定义日志的路径以及采用的日志格式,此参数可以在server配置块中定义
sendfile on; 是否调用sendfile函数传输文件,默认为off,使用sendfile函数传输可以减少user mode和kernel mode的切换,从而提升服务器的性能,对于普通应用设为on,如果用来进行下载等应用磁盘IO重负载应用,可设置为off,以平衡磁盘与网络I/O处理速度,降低系统的 负载
sendfile_max_chunk 128k; 当sendfile设置为on时,此参数才会生效,该参数限定Nginx worker process 每次调用sendfile()函数传输数据的最大值,默认为0,如果设置为0则无限制。
#tcp_nopush on; 当tcp_nopush设置为On时,会调用tcp_cork方法进行数据传输。使用该方法会产生这样的效果:当应用程序产生数据时,内核不会立马封装包,而是当数据量积累到一定的量时才会封装,然后传输。这样有助于解决网络堵塞问题。
默认为on。举例:快递员收快递,包裹积累到一定量才会发,节约运输成本
#keepalive_timeout 0;
keepalive_timeout 65 60; 该参数有两个值,第一个值设置Nginx服务器与客户端会话结束后仍保持连接的最长时间,单位是秒,默认为75s
第二个值可以省略,它是针对客户端的浏览器来设置的,可以通过curl -I 看到header信息中有一项keep-Alive;timeout=60,如果不设置就没有这一项。
第二个数值设置后,浏览器就会根据这个数值决定何时主动关闭连接,Nginx服务器就不操心了。有的浏览器并不认可该参数。
send_timeout; 这个超时时间是发送响应的超时时间,既nginx服务器向客户端发送了数据包,但客户端一直没有去接收这个数据包。如果某个连接超过send_timeout定义的超时时间,那么Nginx将会关闭这个连接。
client_max_body_size 10m; 浏览器在发送含有较大HTTP包体的请求时,其头部会有一个content-Length字段,client_max_body_size是用来限制content-Length所示值的大小的。这个限制包体的配置不用等Nginx接收完所有的HTTP包体,就可以告诉用户请求请求过大不被接收,会返回413状态码。例如,用户试图上传一个1GB的文件,Nginx在收完包头后,发现content-Length超过client_max_body_size定义的值,就直接发送413(Request Entity Too Large)响应给客户端
#gzip on; 是否开启gzip压缩
gzip_min_length 1k; #设置允许压缩页面的最小字节数,页面字节数从header头得content-length中进行获取,默认值是20,建议设置成大于1k的字节数,小于1k可能会越压越大。
gzip_buffers 4 16k; 用来设置压缩的Buffer大小,设置系统获取几个单位的buffer用于存储gzip的压缩结果数据流。4 16k代表分配4个16k的buffer。在压缩完后会产生一个压缩完成的数据流,这个参数就是对这部分数据进行一个缓冲
4代表数量代表4个buffer,16k每个buffer有多大,一共完成多少Buffer,用4*16
gzip_http_version 1.1;用于识别Http协议的版本,早期的浏览器不支持Gzip压缩,用户会看到乱码,所以为了支持前期版本加上了这个选项。如果用了nginx反向代理并期望也启用了gzip压缩的话,由于末端通信是http/1.1,故设置为1.1
gzip_comp_level 6; gzip压缩比,1压缩比最小处理速度最快,9压缩比最大但处理速度最慢(传输快但比较消耗cpu)
gzip_types mime-type ...; 匹配mime类型进行压缩,无论是否指定text/html类型总是会被压缩的
示例:gzip_types text/plain application/x-javascript text/css text/html application/xml;
gzip_proxied any; Nginx作为反向代理的时候启用,决定开启或者关闭后端服务器返回的结果是否压缩,匹配的前提是后端服务器必须要返回via的header头。
以下可用的值:
off - 关闭所有的代理结果数据的压缩
expired - 启用压缩,如果header头 包含expired头信息
no-cache - 启用压缩,如果header头 包含 Cache-Control:no-cache 头信息
no-store - 启用压缩,如果header头 包含 Cache-Control: no-store 头信息
private - 启用压缩,如果header头 包含 Cache-Control: private 头信息
no_last_modified - 启用压缩,如果header头 不包含 last_modified 头信息
no_etag - 启用压缩,如果header头 不包含 'ETag' 头信息
auth - 启用压缩,如果header头中包含Authorization 头信息
any - 无条件启用压缩
gzip_vary on; 和http头有关系,会在响应头加个Vary:Accept-Encoding ,可以让前端的缓存经过gzip压缩的页面,例如,用Squid缓存经过Nginx压缩的数据。
server { #server端是包含在http内部的,每一个server都是一个虚拟主机
listen 80; 监听端口为80,也可以自定义其他端口
server_name localhost; //自定义网站域名,可以写多个,用空格分开
#charset koi8-r; 定义网站的字符集,一般不设置,而是网页代码中设置
#access_log logs/host.access.log main; 定义访问日志,可以针对每一个server(既每一个站点,设置它们自己的访问日志。
在server()里以后很多location配置段
location / {
root /data/blog/blog.123.com; 定义网站的根目录,目录可以是相对路径也可以是绝对路径
index index.html index.php; 定义站点的默认页
}
#error_page 404 /404.html; 定义404页面
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html; //当前状态码为500 502 503 504时则访问 50x.html
location = /50x.html {
root html; 定义50x.html所在路径
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1; proxy_pass后面指定要访问的URL路径,用proxy_pass实现代理
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
# location ~ \.php$ {
# root /data/blog/blog.123.com;
# fastcgi_pass 127.0.0.1:9000; 定义FastCGI服务器监听端口与地址,,支持两种形式,1 IP:Port 2 unix:/path/to/socket
# fastcgi_index index.php; 定义默认页
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; 定义SCRIPT_FILENAME变量,后面的路径/scripts为上面的root指定的目录
# include fastcgi_params; 引用prefix/conf/fastcgi_params文件,该文件定义了fastcgi相关的变量
# }
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht { 访问的url中,以/.ht开头的,如www.example.com/.htoccess会被拒绝,返回403状态码
# deny all; 这里的all 指的是所有的请求
#}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000; 监听的80端口
# listen somename:8080; 指定ip:port
# server_name somename alias another.alias; 指定多个server_name ,定义域名,别名
# location / {
# root html; 定义网站根目录
# index index.html index.htm; 定义网站访问到的索引页
# }
#}
# HTTPS server
#
#server {
# listen 443 ssl; 监听443端口,既ssl
# server_name localhost; 定于域名
# ssl_certificate cert.pem; 指定pem文件路径
# ssl_certificate_key cert.key; 指定Key文件路径
# ssl_session_cache shared:SSL:1m; 指定session cache大小
# ssl_session_timeout 5m; 指定session超时时间
# ssl——protocols TLSv1 TLSv1.1 TLSv1.2 //指定SSL协议
# ssl_ciphers HIGH:!aNULL:!MD5; 指定SSL算法
# ssl_prefer_server_ciphers on; 优先采取服务器算法
# location / {
# root html;
# index index.html index.htm;
# }
#}
Nginx架构分析
Nginx模块化
Nginx基于模块化设计每个模块是一个功能的实现,分布式开发,团队协作
核心模块、标准HTTP模块、可选HTTP模块、邮件模块、第三方模块
编译后的源码目录objs/ngx_modules.c
核心模块
核心模块是指Nginx服务器正常运行时必不可少的模块,它们提供了Nginx最基本最核心的服务,如进程管理、权限控制、错误日志记录、配置解析等
另一类是用于响应请求事件必需的功能,包括事件驱动机制,正则表达式解析等。
ngx_core_,pdule
ngx_errlohg_module
ngx_conf_module
ngx_regex_module
ngx_events_module
ngx_epoll_module
标准HTTP模块
标准HTTP模块是编译NGINX后台包含的模块,其支持Nginx服务器的标准HTTP功能
nginx_http_core :配置端口,URI分析,服务器响应错误处理,别名控制以及其他HTTP核心事务
ngx_http_access_module :基于IP地址的访问控制(允许/拒绝)
Ngx_http_auth_basic_module:基于HTTP的身份认证
ngx_http_autoindex_module :处理“/”结尾的请求并自动生成目录列表
ngx_http_browser_module:解析HTTP请求头中的"User-Agent"的域的值
ngx_http_charset_module:指定网页编码
ngx_http_empty_gif_module:从内存创建一个1 x 1的透明gif图片,可以快速调用
ngx_http_fastcgi_module :对FastCGI的支持
ngx_http_geo_module:对客户端请求中的参数转化为键值对变量
ngx_http_gzip_module :设置HTTP响应头
ngx_http:_index_module:处理以"/"结尾的请求,如果没有找到该目录下的index页,就将请求转给ngx_http_autoindex_module
ngx_http_limit_req_module:限制来自客户端的请求的响应和处理速率
Ngx_http_limit_conn_module:限制来自客户端的连接的响应和处理速率
ngx_http_log_module:自定义access日志
ngx_http_map_module::创建任意键值对变量
ngx_http_memcached_module:对Memacached的支持
ngx_http_proxy_module:支持代理事务
ngx_http_referer_module:对HTTP请求头中的referer进行过滤处理,比如,实现防盗链功能
ngx_http_rewrite_module:实现nginx的rewrite功能
ngx_http_scgi_module:对scgi的支持
ngx_http_ssl_module:对HTTPS的支持
ngx_http_upstream_module:定义一组服务器,可以接收代理,fastcgi、Memcached的重定向主要用于负载均衡
Nginx 的web请求机制
并行处理:多进程、多线程、异步
同步机制(通信机制)
同步机制,是指发送方发送请求后,需要等待接收方返回信息后,再继续发送下一个请求。
在同步机制中,所有的请求在服务器端得到听同步,既发送方和接受方对请求的处理步调是一致的。
异步机制(通信机制)
异步机制,是指发送方发出一个请求后,不等待接收方返回信息,就继续发送下一个请求
在异步机制中,所有来自发送方的请求形成一个队列,接收方处理完后再通知发送方。
举例:一家酒店前台,在旺季高峰时间段会预定酒席的电话
如果是同步机制情况下,前台每接一个电话后先不挂掉电话,而是去查询有无剩余酒席,查到结果,告诉客户
如果是异步机制情况下,前台每接一个预定电话直接回复客户,一会回复,此时前台把查询、恢复这些事情交给了另外的同事,该前台挂掉电话后,继续处理其他客户的事情,同事查到结果后,再给客户回复电话
阻塞(进程调用)
阻塞和非阻塞用来描述进程处理调用的方式,在网络通信 模型中,主要指的是socket的阻塞或非阻塞,而socket的本质就是I/O,阻塞方式下,进程(线程)在获取最终结果之前,被系统挂起了,也就是所谓的阻塞了,在阻塞过程中什么都干不了,比如在运行命令的时候一直没有结果一直在等着他直到有结果为止
非阻塞(进程调用)
非阻塞方式和阻塞相反,进程在获取最终结果之前,并没有进入被挂起的状态,而是该进程可以继续执行新的任务。
当有最终结果反馈给进程时,它再把结果交给客户端
举例:依然是酒店前台接待预定酒席电话的案例。
此时角色不再是前台,而是它的查询有无剩余酒席的同事,如果是阻塞方式,该同事在查询有无剩余酒席的过程中,需要傻傻地等待酒店管理系统给它返回结果,在此期间不能做其他事情
如果是非阻塞,该同事在等待酒店管理系统给它返回结果这段时间,可以做其他事情,比如可以打电话通知前面的客户剩余酒席的情况。
Nginx事件驱动模型
事件驱动模型是实现=异步非阻塞的一个手段,事件驱动模型中,一个进程(线程)就可以了
对于web服务器来说,客户端A的请求连接到服务端时,服务端的某个进程(Nginx process)会处理该请求,此进程在没有返回给客户端A结果时,它又去处理了客户端B的请求。服务端把客户端A以及客户端B发来的请求作为事件交给了事件收集器,而事件收集器,再把收集到的事件交给事件发送器,发送给事件处理器进行处理最后事件处理器处理完该事件后,通知服务端进程,服务端进程再把结果返回给客户端A,客户端B
在这个过程中,服务端进程做的事情属于用户级别的,而事件处理这部分工作属于内核级别的。也就是说这个事件驱动模型是需要操作系统内核来作为支撑的
Nginx的事件驱动模型,支持select、poll、epoll、rtsig、kqueue、/dev/poll、eventport等。
最常用的是前三种,其中kqueue模型用于支持BSD系列平台的事件驱动模型。kqueue是poll模型的一个变种,本质上和epoll意义。 /dev/polkl是Unix平台的事件驱动模型,其主要在Solaris7以及以上版本、HP/UX11.22以及以上版本、irix6.5.15以及以上版本Tru64 Unix 5.1A以及以上版本的平台使用。eventport是用于支持solaris10以及以上版本的事件驱动模型
select模型
Linux和windows都支持,使用select模型的步骤是:
1.创建所关注事件的描述符集合,对于一个描述符,可以关注其上面的读(Read)事件、写(write)事件以及异常发生(Exception)事件。在select模型中要创建这3类事件描述符集合
2.调用底层提供的select()函数,等待事件发生。
3.轮询所有事件描述符集合中的一个每一个事件描述符,检查是否有相应的事件发生,如果有就进行处理
poll模型
POll模型是Linux平台上的事件驱动模型,在Linux2.1.23中引入的,windows平台不支持该类型。
poll模型和select模型工作方式基本相同,区别在于select创建了3个描述符集合,而poll模型只创建一个描述符集合。
epoll模型
epoll模型属于Poll模型的变种,在Linux2.5.44中引入。epoll比poll更加高效,原因在于它不需要轮询整个描述符集合,而是Linux内核会关注事件集合,当有变动时,内核会发来通知
设计架构概念
Nginx基于模块化设计、基于事件驱动模型处理请求、主进程和工作进程
Nginx架构
nginx服务器使用master/worker,多进程模式。
主进程(Master process)启动后,会接收和处理外部信号;
主进程启动后通过fork()函数产生一个或多个子进程(work process),每个子进程会进行初始化,模块调用以及对事件的接收和处理工作
主进程
主要功能是和外界通信对内部其他进程进行管理,具体来说有以下几点:
读取nginx配置文件并验证有效性和正确性
建立、绑定和关闭socket
按照配置生成、管理工作进程
接收外界指令,比如重启、关闭、重载服务等指令
日志文件管理
子进程(worker process)
由主进程生成,生成数量可以在配置文件中定义,该进程主要工作
接收客户端请求
将请求以此送入各个功能模块进行过滤处理
IO调用,获取响应数据
与后端服务器通信,接收后端服务器处理结果
数据缓存,访问缓存索引,查询和调用缓存数据
发送请求结果,响应客户端请求
接收主进程,如重启、重载、退出等
Nginx的Rewrite配置
域名跳转(重定向)、URL重写(伪静态)、动静分离(跳转域名并接入CDN实现加速)
依赖PCRE库
模块:ngx_http_rewrite_module
Rwrite相关指令
if (条件判断) { 具体的rewrite规则}
条件举例
条件判断语句由Nginx内置变量、逻辑判断符号和目标字符串三部分组成。
其中,内置变量是Nginx固定的非自定义的变量。如$request_method,$request_uri等
逻辑判断符号,有=,!=,~,~*,!~,!~*
!表示相反的意思,~为匹配符号,它右侧为正则表达式,区分大小写,而~*为不区分大小写匹配
目标字符串可以是正则表达式,通常不用加引号,但表达式中有特殊符号时,比如空格花括号,分号等,需要用单引号引起来。
示例1
if ($request_metnod = POST) //当请求的方法为Post时,直接返回405状态码
{
return 405;//在该示例中并未用到rewrite规则,if中支持用return指令。
}
示例2
if ($http_user_agent ~ MSIE) //user_agent带有MSIE字符的请求,直接返回403状态码
{
return 403;
}
如果想同时限制多个user_agent,还可以写成这样
if($http_user_agent ~ "MSIE|firefox|spider")
{
return 403;
}
示例3
if(!-f $request_filename)//当请求的文件不存在,将会执行下面的rewrite规则
{
rewrite 语句
}
示例4
if ($request_uri ~* 'gid=\d{9,12/}' //\d表示数字,{9,12}表示数字出现次数时9到12次,如gid是123456789/就是符合条件的
{
rewrite 语句;
}
break和last
两个指令用法相同,但含义不同,需要放到rewrite规则的末尾,用来控制重写后的链接是否继续被Nginx配置执行(主要是rewrite,return指令)。
示例1 (连续两条rewrite规则):
server{
listen 80;
server_name test.com;
root /tmp/123.html
rewrite_log on; //开启rewrite日志,开启此日志,也要同时开启error_log的notice级别
rewrite /1.html /2.html;
rewrite /2.html /3.html;
}
当我们请求1.html时,最终访问到的是3.html,两条rewrite规则先后执行。
将last和break放在location里面
location / {
rewrite /1.html /2.html;
rewrite /2.html /3.html;
}
location /2.html
{
rewrite /2.html /a.html;
}
location /3.html
{
rewrite /3.html /b.html;
}
当请求/1.html,最终将会访问/b.html,连续执行location / 下的两次rewrite,跳转到3.html然后又匹配到location /3.html
location / {
rewrite /1.html /2.html break;
rewrite /2.html /3.html;
}
location /2.html
{
rewrite /2.html /a.html;
}
location /3.html
{
rewrite /3.html /b.html;
}
当请求1.html时最终会访问到2.html,匹配到2.html后因为有break就会跳出location这样结果就会匹配到2.html就不会继续向下执行了
location / {
rewrite /1.html /2.html last;
rewrite /2.html /3.html;
}
location /2.html
{
rewrite /2.html /a.html;
}
location /3.html
{
rewrite /3.html /b.html;
}
当请求/1.html,最终会访问a.html
在Location{}内部,遇到Last,本location{}内后续的指令不在执行,而重写后的url再次从头开始,从头到尾匹配一遍规则
结论
·当rewrite规则在Location{}外,break和last作用一样,遇到break或last后,其后续的rewrite/return语句不再执行
但后续还有location的话还会进一步执行Location{}里的语句。
当rewrite规则在location里,遇到break后,本location{}与其他location{}的所有rewrite/retuen都不会在执行
当rewrite规则在location里,遇到last后,本location{}的所有rewrite/retuen都不会在执行,但重写后的URI再次从头开始执行所有规则,那个匹配执行那个。
Nginx的return指令
该指令一般用于对请求的客户端直接返回响应的状态码,在该作用域内return后面所有Nginx的配置都是无效的
可以使用在server、location以及if配置中。
除了支持跟状态码,还可以跟字符串或者URL链接。
直接返回状态码
示例1
server {
listen 80;
server_name www.aming.com;
return 403;
rewrite /(.*) /abc/$1; //该行不会被执行。
}
**示例 **2
server {
listen 80;
server_name www.aming.com;
......
if ($request_uri ~ "\htpasswd|\.bak")
{
return 403;
rewrite /(.*) /aaa.txt; //该行配置不会被执行。
}
//如果下面还有其他配置,会被执行
......
}
返回字符串
示例3
server {
listen 80;
server_name www.aming.com;
return 200 "hello"
}
说明:如果要想返回字符串,必须要加上状态码,否则会报错
还可以支持Json数据
示例4
location ^~ /aming {
default_type application/json;
return 200 '{"name":"aming","id":"100"}';
}
也支持写一个变量
示例5
location /test {
return 200 "$host $request_uri";
}
示例5
server{
listen 80;
server_name www.aming.com;
return http://www.aminglinux.com/123.html
rewrite /(.*) /abc/$1 //该行不会被执行
}
注意:return后面的url必须是http://或者https://开头的
生成场景实战
背景:网站被黑,凡是在百度点击到本网站的请求,全部都跳转到了一个赌博网站。
通过Nginx解决
if ($http_referer ~ 'baidu.com')
{
return 200 "<html><script>windows.location.href='//$host$request_uri';</script></html>";
}
如果写成 return http://$host$request_uri;在浏览器中会提示”重定向的次数过多".
rewrite规则
rewrite regex replacement [flag]
rewrite配置可以在server、location以及if配置段生效
regex是用于匹配URI的正则表达式,其不会匹配到$host(域名)
replacement 是目标跳转的URI,可以以http://或者https://开头,也可以省略掉$host,直接写request_uri部分(既请求的链接)
flag:用来设置rewrite对URI的处理行为其中break,last,rediect,permanent,其中break,last在前面已经写过,rediect 和permanent的区别在于,前者为临时重定向(302),而后者是永久重定向(301),对于用户通过浏览器访问,这两者的效果是一致的。但是,对于搜索引擎蜘蛛爬虫来说就有区别了,使用301更有利于SEO,所以建议replacement 是以http://或者https://开头的flag使用permanent。
示例1
location / {
rewrite /(.*) http://www.aming.com;/$1 permanent ;
}
说明:"为正则表达式,用()括起来,在后面的URI中可以调用它,第一次出现的()用$1调用,第二次出现的()用$2调用,以此类推。"
示例2
location / {
rewrite /.* http://www.aming.com;/$request_uri permanent ;
}
说明:在replacement中,支持变量,这里的$request_uri就是客户端请求的链接
示例3
server{
listen 80;
server_name www.123.com;
root /tmp/123.com/
index index.html;
rewrite /(.*) /abc/$1 redirect;
}
说明:本例中的rewrite规则有问题,会造成连续循环,nginx有个最大50次限制,最终会失败,解决该问题有两个方案。
示例4
server{
listen 80;
server_name www.123.com;
root /tmp/123.com/
index index.html;
rewrite /(.*) /abc/$1 break;
}
说明:在rewrite中使用break,会避免循环。
示例5
server{
listen 80;
server_name www.123.com;
root/ /tmp/123.com;
index index.html;
if ($request_uri !~ '^/abc/')
{
rewrite /(.*) /abc/$1 redirect;
}
}
说明:加一个条件限制,也可以避免产生循环。
rewrite_log定义rewrite日志 rewrite_log on; 写到error_log notice级别
Nginx 常用全局变量
$args 请求中的参数,如www.123.com/1.php?a=1&b=2的args就是a=1&b=2
$content_length HTTP请求信息里的"Cotent-Length"
$conten_type HTTP请求信息里的"Content-Type"
$document_root nginx虚拟主机配置文件中的root参数对应
$document_uri 当前请求中不包括指令的URI,如www.123.com/1.php?a=1&b=2的document_uri就是1.php不包含后面的参数
$host 主机头,也就是域名
$http_user_agent 客户端的详细信息,也就是浏览器的标识,用Curl -A可以指定
$http_cookie 客户端的cookie信息
$limit_rate 如果nginx服务器使用limit_rate配置了显示网络速率,则显示0
$remote_addr 客户端的公网Ip
$remote_port 客户端的port
$remote_user 如果nginx有配置认证,该变量代表客户端认证的用户名
$request_body_file 做反向代理时发给后端服务器的本地资源的名称
$request_method 请求资源的方式,GET/PUT/DELLTE等
$request_filename 当前请求的资源文件路径名称,相当于$document_root $document_uri的组合
$request_uri 请求的链接,包括$document_uri 和 $args
$scheme 请求的协议,如ftp,http,https
$server_protocol 客户端请求资源的协议的版本,如http/1.0 ,HTTP/1.1,HTTP/1.2等
$server_addr 服务器ip地址
$server_name 服务器的主机名
$server_port 服务器的端口号
$uri 和$document_uri相同
$http_referer 客户端请求时的referer,通俗讲就是该请求是通过那个链接跳过来的。用curl -e 指定
rwrite实战
本部分内容nginx生产环境中使用的场景示例
域名跳转(域名重定向)
示例1(不带条件的)
server{
listen 80;
server_name www.aminglinux.com;
rewrite /(.*) http://www.aming.com/$1 permanent;
......
}
示例2(带条件的)
server{
listen 80;
server_name www.aminglinux.com aminglinux.com;
if ($host != 'www.aminglinux.com')
{
rewrite /(.*) http://www.aminglinux.com/$1 permanent
}
......
}
示例3(http跳转到https)
server {
listen 80;
server_name www.aminglinux.com;
rewrite /(.*) https://www.aminglinux.com/$1 permanent;
}
示例4(域名访问二级目录)
server{
listen 80;
server_name bbs.aminglinux.com;
rewrite /(.*) http://www.aminglinux.com/bbs/$1 last;
......
}
示例5 (静态请求分离)
server{
listen 80;
server_name www.aminglinux.com;
location ~* ^.+.(jpg|jpeg|gif|css|png|js)$
{
rewrite /(.*) http://img.aminglinux.com/$1 permanent;
}
防盗链
server{
listen 80;
server_name www.aminglinux.com;
location ~* ^.+.(jpg|jpeg|gif|css|png|js|rar|zip|fiv)$
{
valid_referers none blocked server_names *.aminglinux.com aminglinux.com *.aming.com aming.com;
if ($invalid_referer)
{
rewrite /(.*) http://img.aminglinux.com/images/forbidden.png;
}
}
......
}
说明:*这里是通配,跟正则里面的*不是一个意思,none指的是referer不存在的情况(curl-e测试),blocked指的是头部的值不以http://或者https://开头 (curl -e后面跟的referer不以http://或者https://开头),或者
location ~* ^.+.(jpg|jpeg|gif|css|png|js|rar|zip|fiv)$
{
valid_referers none blocked server_names *.aminglinux.com aminglinux.com *.aming.com aming.com;
}
if ($invaukd_referer)
{
return 403;
}
}
......
}
伪静态
示例7(discuz伪静态)
location / {
rewrite ^([^\.]*)/topic-(.+)\.html$ $1/portal.php?mod=topic$topic=$2 last;
rewrite ^([^\.]*)/forum-(\w+)-([0-9]+)\.html$ $1/forum.php?mod=forumdisplay&fid=$2&page=$3 last;
rewrite ^([^\.]*)/therad-([0-9]+)-([0-9]+)([0-9]+)\.html $1/forum.php?mod=viewthread&tid=$2&extra=page%3D$page%30%4&4&page=$3 last;
rewrite ^([^\.]*)/therad-([0-9]+)-([0-9]+)\.html$ $1/forum.php?mod=group&fid=$2&page=$3 last;
rewrite ^([^\.]*)/space-(username|uid)-(.+)\.html$ $1/home.php?mod=space&$2=$3 last;
rewrite ^([^\.]*)/(fid|tid)-([0-9]+)\.html$ $1/index.php?action=$2&value=$3 last;
}
~ 为区分大小写的匹配。
~* 不区分大小写的匹配(匹配firefox的正则同时匹配FireFox)。
!~ 不匹配的
!~* 不匹配的
. 匹配除换行符以外的任意字符
\w 匹配字母或数字或下划线或汉字
\s 匹配任意的空白符
\d 匹配数字
\b 匹配单词的开始或结束
^ 匹配字符串的开始
$ 匹配字符串的结束
*重复零次或更多次
+重复一次或更多次
? 重复零次或一次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次
*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复
\W 匹配任意不是字母,数字,下划线,汉字的字符
\S 匹配任意不是空白符的字符
\D 匹配任意非数字的字符
\B 匹配不是单词开头或结束的位置
[^x] 匹配除了x以外的任意字符
rewrite多个条件的并且
示例8:
location / {
set $rule 0;
}
if ($document_uri !~ '^/abc')
{
set $rule "${rule}2";
}
if ($rule = "012")
{
rewrite /(.*) /abc/$1 redirect;
}
}
Nginx的location配置
安装第三方模块echo-nginx-module
git clone https://github.com/openresty/echo-nginx-module.git
#注:echo-nginx-module 允许Nginx再配置文件里 使用echo
./configure --add-module=/path/to/echo-nginx-module
location语法优先级及案例
#location语法
location [=|^~|~|~*] /uri/ {......}
=:标识精确匹配
^~:标识区分大小写的正则匹配
~*:标识不区分大小写的正则匹配
/:通用匹配,任何请求都会匹配到
#location优先级及案例
#规则优先级
= 高于 ^~高于 ~*等于 ~ 高于 /
规则示例
location = "/12.jpg" {...}
如
www.aminglinux.com/12.jpg 匹配
www.aminglinux.com/abc/12.jpg 不匹配
location ^~ "/abc/" { ... }
如:
www.aminglinux.com/abc/123.com 匹配
www.aminglinux.com/a/abc/123.jpg 不匹配
location ~ "png" { ... }
如
www.aminglinux.com/aaa/bbb/ccc/123.png 匹配
www.aminglinux.com/aaa/png/123.html 匹配
location ~* "png" { ... }
如
www.aminglinux.com/aaa/bbb/ccc/123.PNG 匹配
www.aminglinux.com/aaa/png/123.html 匹配
location /admin/ { ... }
如
www.aminglinux.com/admin/aaa/1.php 匹配
www.aminglinux.com/123/admin/1.php 不匹配
小常识
有些资料介绍location 支持不匹配!~
如:location !~ 'png' { ... }
这是错误的,
如果有这样的需求,可以通过If实现
如:if ($uri !~ 'png') { ... }
注:location 优先级小于if
Nginx的代理
正向代理
示意图
1.PC无法直接访问WEB服务器,但代理服务器可以访问
2.代理服务器帮助PC请求页面并缓存到本地,并将页面返回给PC
注:PC只需要浏览器设置代理服务器IP和端口即可,PC知道代理服务器和WEB服务器的存在。
Nginx正向代理配置
Nginx正向代理使用场景并不多见
需求场景1
如果再机房中,只有一台机器可以联网,内网的机器想要使用yum安装包,再能联网机器上的机器上配置一个正向代理即可
Nginx正向代理配置文件
server {
listen 80 default_server;
resolver 119.29.29.29;
location /
{
proxy_pass http://$host$request_uri;
}
}
Nginx正向代理执行说明
resolver
语法:resolver address;
address为DNS服务器的地址,国内通用的DNS 119.29.29.29为dnspod公司提供。国际通用DNS8.8.8.8或者8.8.4.4为google提供。
示例:resolver 119.29.29.29
default_server:之所以要设置默认虚拟主机,是因为这样就不用设置server_name了,任何域名解析过来都可以正常访问。
proxy_pass:该指令用来设置要代理的目标URL,正向代理服务器设置就可以保持该固定值即可,关于该指令详细解释再反向代理配置中
反向代理
Nginx反向代理再生产环境中使用很多的
场景1:
域名没有备案,可以把域名解析到香港一台云主机上,在香港云主机做个代理而网站数据实在大陆服务器上
示例1
server
{
listen 80;
server_name aminglinux.com;
location /
{
proxy_pass http://123.23.13.11/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X_header X-forewarded-For $proxy_add_x_forwarded_for;
}
}
配置说明
1.proxy_pass
在正向代理中,已经使用过该指令。
格式很简单:proxy_pass URL;
其中URL包含:传输协议(http://,https://等)
示例如下:
proxy_pass http://www.aminglinux.com/;
proxy_pass http http://192.168.200.201:8080/uri;
proxy_pass unix:/tmp/www.sock;
对于proxy_pass的配置有几种情况需要注意。
示例2:
location /aming/
{
proxy_pass http://192.168.1.10;
...
}
示例3:
location /aming/
{
proxy_pass http://192.168.1.10/;
}
示例4:
location /aming/
{
proxy_pass http://192.168.1.10/linux/;
...
}
示例5:
location /aming/
{
proxy_pass http://192.168.1.10/linux;
...
}
假设server_name为www.aminglinux.com
当请求http://www.aminglinux.com/aming/a.html 的时候,以上示例2-5分钟分别访问的结果是
示例2:http://192.168.1.10/aming/a.html
示例3:http://192.168.1.10/a.html
示例4:http://192.168.1.10/linux/a.html
示例5:http://192.168.1.10/linuxa.html
2.proxy_set_header
proxy_set_header 用来设定被代理服务器收到的header信息。
语法:proxy_set_header field value;
field为要更改的项目,也可以理解为变量的名字比如host
value为变量的值
如果不设置proxy_set_header,则默认host的值为proxy_pass后面跟的那个域名或者IP(一般写IP),
比如示例4,请求到后端服务器上时,完整请求URI为:http://192.168.1.10/linux/a.html
如果设置Proxy_set_header,如Proxy_set_header host $host;
比如示例4.请求到后端的服务器完整URI为:http://www.aminglinux.com/linux/a.html
proxy_set_header X_Real-IP $remote_addr;和proxy_set_header X-Forwarded-For $proxy_add_xforwarded_for;用来设置被代理端接收到的远程客户端IP,如果不设置,则header信息并不会透传远程真是客户端的IP地址
可以用如下示例测试:
示例6(代理端)
server{
listen 8080;
server_name www.aminglinux.com;
root /tmp/123.com_8080;
index index.html;
location /linux/ {
echo "$host";
echo $remote_addr;
echo $proxy_add_X-Forwarded-For ;
}
}
示例7(代理服务器上)
server{
listen 80;
server_name http://192.168.1.10:8080/linux/;
location /aming/ {
proxy_pass http://192.168.1.10:8080/linux/;
proxy_set_header host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_X-Forwarded-For ;
}
}
3.proxy_redirect
该指令用来修改被代理服务器返回的响应头中的Location头域和"refresh"头域。
语法结构为:
proxy_redirect redirect replacement;
procy_redirect default;
proxy_redirect off;
示例8:
server {
listen 80;
server_nmae www.aminglinux.com;
index index.html
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header host $host;
proxy_redirect http://$host:8080/ /;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
当请求的链接为 http://www.aminglinux.com/aming
结果会返回301,定向到了http://www.aminglinux.com:8080/aming/
注意:返回301有几个先决条件
1. location后面必须是/;
2. proxy_pass后面的URL不能加uri,只能是IP或者IP:port结尾,并不能以/结尾;
3. 访问的uri必须是一个真实存在的目录,如,这里的aming必须是存在的
4. 访问的时候,不能以/结尾,只能是 www.xxx.com/aming
虽然,这4个条件挺苛刻,但确实会遇到类似的请求。解决方法是,加一行proxy_redirect http://$host:8080/ /;
4,nginx的proxy_buiffering和proxy_cache
两个都是Nginx代理中内存设置相关的参数
proxy_buffering设置
proxy_buffering主要是实现被代理服务器端的数据和客户端的请求异步
为了方便理解,我们定义三个角色,A发起请求到B,B再到C,C反馈的数据先到B的buffer上
然后B会根据proxy_buffer_size来决定什么时候开始把数据传输给A,在此过程中,如果所有的buffer被写满。
数据将会写入到temp_file中。
相反,如果proxy_buffering关闭,C反馈的数据实时地通过B传输给A
以下配置,都是针对每一个HTTP请求的。
1.proxy_buffering on;
该参数是否开启proxy的buffer功能,参数的值为On或者off.
如果这个设置为off,那么proxy_buffers和proxy_buffer_size这两个指令将会失效。
但是无论proxy_buffering是否开启,proxy_buffer_size都是生效的
2.proxy_buffer_size 4k;
该参数用来设置一个特殊的Buffer大小的。
从被代理服务器(C)上获取到的第一部分响应数据内容到代理服务器(B)上,通常是header信息,建议设置为4k。。
如果该参数设置太小,会出现502错误码,这是因为这部分Buffer不够存储header信息。建议设置为4k
3.proxy_buffrers 8 4k
这个参数设置存储被代理服务器上的数据所占用的Buffer的个数和每个buffer的大小。
所有buffer的大小为这两个数字的乘积
4.proxy_busy_buffer_size 16k;
在所有的buffer里,我们需要规定一部分Buffer把自己存的数据传给A,这部分buffer就叫做busy_buffer.
proxy_busy_buffer_size参数用来设施处于busy状态的buffer有多大。
对于B上buffer里的数据何时传输给A,我个人的理解是这样的
1)如果完整数据大小小于Busy_buffer大小,当数据传输完成后,马上传给A;
2)如果完整数据大小不少于busy_buffer大小,则装满busy_buffer后,马上传给A
5.proxy_temp_path
语法:proxy_temp_path
语法:proxy_temp_path path [level1 level2 level3]
定义proxy的临时文件存在目录以及目录层级
例:proxy_temp_path /usr/local/nginx/proxy_temp 1 2;
其中/usr/local//nginx/proxy_temp 为临时文件所在目录,1表示层级1的目录名为一个数字(0-9),2表示层级2目录名为2个数字(00-99)
6.proxy_max_temp_file_size
设置临时文件的总大小,例如:proxy_max_temp_file_size 100M;
7.proxy_temp_file_wirte_size
设置同时写入临时文件的数据量总大小,通常设置为8k或者16k.
proxy_buffer示例
server
{
listen 80;
server_name www.aminglinux.com;
proxy_buuffering on;
proxy_buffer_size 4k;
proxy_buffers 2 4k;
proxy_busy_buffers_size 4k;
pproxy_temp_path /tmp/nginx_proxy_tmp 1 2;
proxy_max_temp_file_size 20M;
proxy_temp_file_write_size 8kl;
location /
{
proxy_pass http://192.168.10.110:8080/;
proxy_set_header Host $host
proxy_set_header X-Real-IP $remote_addr
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
proxy_cache设置
proxy_cache 将从C上获取到的数据根据预设规则存放到B上(内存+磁盘)留着备用,A请求B时,B会把缓存的这些数据直接给A,而不需要再去向C获取。
proxy_cache相关功能生效的前提是,需要设置proxy_buffering on;
proxy_cache主要参数
1.proxy_cache
语法:proxy_cache zone|off
默认为Off,即关闭proxy_cache功能。zone为用于存放缓存的内存区域名称。
例如:proxy_cache my_zone;
从nginx 0.7.66版本开始,proxy_cache机制开启后会检测被代理端的http响应头中的:cache-control","Exire"头域,如,cache-Control为no-cache时,是不会缓存数据的。
2.proxy_cache_bypass
语法:proxy_cache_bypass string;
该参数设定,什么情况下的请求不读取cache而是从后端服务器上获取资源
这里的string通常为Nginx的一些变量。
例:proxy_cache_bypass $cookie_nocache $arg_nocache$arg_comment;
意思是如果 $cookie_nocache $arg_nocache$arg_comment;这些变量的值只要任何一个不为0或者不为空时,
则响应数据不从cache中获取,而是直接从后端服务器上获取
3.proxy_no_cache
语法:proxy_no_cache string;
该参数和proxy_cache_bypass类似,用来设定什么情况下不缓存。
例如proxy_no_cache $cookie_nocache $arg_nocache $arg_comment;
表示如果$cookie_nocache $arg_nocache $arg_comment;的值只要有一项不为0或者不为空时,不缓存数据。
4.proxy_cache_key
语法:proxy_cache_key string;
定义cache key,和proxy_cache_key $scheme $proxy_host $uri $is_arghs $args(该值为默认值,一般不用设置)
5.proxy_cache_path
语法:proxy_cache_path path [levels = levels ] keys_zone=name:size [inactive=time] [max_size=size]
Path:设置缓存数据存放的路径;
levels:设置目录层级,如levels=1:2,表示有两级子目录
keys_zone:设置内存zone的名字和大小,如keys_zone=my_zone:10m
inactive:设置缓存多长时间就失效,当硬盘上的缓存数据在该时间段访内没有被访问过,就会失效了,该数据就会被删除,默认为10s。
max_size:设置硬盘中最多可以缓存多少数据,当达到该数值时,nginx会删除最少访问的数据
例:proxy_cache_path /data/nginx_cache levels=1:2 keys_zone=my_zone:10m inactive=300s max_size=5g;
Nginx访问控制 -------- deny_allow
Nginx的deny和allow指令是由ngx_http_access_module模块提供,nginx安装默认内置了该模块。
除非在安装时有指定,--without-http_access_module.
语法
语法:allow/deny address | CIDR | unix | all
它表示,允许/拒绝某个ip或者一个Ip段访问,如果指定unix,那将允许socket的访问。
注意:unix在1.5.1中新加入的功能
在Nginx中allow和deny的规则时按顺序执行的。
示例
示例1
location /
{
allow 192.168.0.0/24;
allow 127.0.0.1;
deny all;
}
说明:这段配置值允许192.168.0.0/24网段和127.0.0.1的请求,其他来源IP全部拒绝。
示例2:
location ~ "admin"
{
allow 110.21.33.121;
deny all;
}
说明:访问的uri中包含admin的请求,只允许110.21.33.121这个ip的请求。
基于location的访问控制
在生产环境中,我们会对某某些特殊的请求进行限制,比如对网站的后台进行限制访问。
这就用到了location配置
示例1
location /aming/
{
deny all;
}
说明:针对/aming/目录,全部禁止访问,这里的deny all可以改为return 403.
示例2
location ~ “.bak|\.ht"
{
return 403;
}
说明:访问的uri中包含.bak字样的或者包含.ht的直接返回403代码。
测试举例
1.www.aminglinux.com/123.bak
2.www.aminglinux.com/aming/123/.htalskdjf
示例3
location ~ (data | cache | tmp | image |attachment).*\.php$
{
deny all;
}
说明:请求的uri中包含的data、cache、tmp、image、attachment 并且以.php结尾的,全部禁止访问
测试链接举例:
1.www.aminglinux.com/aming/cache/1.php
2.www.aminglinux.com/image/123.phps
3.www.aminglinux.com/aming/datas/1.php
Nginx基于$document_uri的访问控制
这就用到了变量$document_uri,根据前面所学的内容,该变量等价于$uri,其实也等价于location匹配。
示例1
if ($document_uri ~ "/aming/")
{
return 403;
}
说明:当请求的uri中包含/admin/时,直接返回403
if结构中不支持使用allow和deny
测试链接
1.www.aminglinux.com/123/admin/1.html 匹配
2.www.aminglinux.com/admin123/1.html 不匹配
3.www.aminglinux.com/admin.php 不匹配
示例2
if ($document_uri = /admin.php)
{
return 403'
}
说明:请求的uri为/admin.php时返回403状态码。
测试链接:
1.www.aminglinux.com/admin.php 匹配
2.www.aminglinux.com/123/admin.php 不匹配
示例3
if ($document_uri ~ '/data/|.cache/.*\.php$')
{
return 403;
}
说明:请求的Uri包含data或者cache目录,并且是php时,返回403状态码。
测试链接:
1.www.aminglinux.com/data/123/php 匹配
2.www.aminglinux.com/cache1/123.php 不匹配
Nginx访问控制-基于request_uri
主要是针对请求uri中的参数进行控制
示例
if ($request_uri ~ "gid=\d{9,12}")
{
return 403;
}
说明:\d{9,12}是正则表达式,表示9到12个数字,例如gid=1234567890就符号要求。
测试链接
1.www.aminglinux.com/index.php?gid=1234567890&pid=111 匹配
2.www.aminglinux.com/gid=123 不匹配
背景知识
曾经有一个客户的网站被cc攻击。对方发起太多类似这样的请求:/read/-123450515-1-1.html
实际上,这样的请求并不是正常的请求,网站会抛出一个节目,提示帖子不存在。
所以,可以直接针对这样的请求,return 403状态码
Nginx基于$user_agent的访问控制
$user_agent,可以简单理解成浏览器标识,包括一些蜘蛛爬虫都可以通过$user_agent来辨识。通过观察访问日志,可以发现一些搜索引擎的蜘蛛对网站访问特别频繁,它们并不友好。为了减少服务器压力。其实可以把除主流搜索引擎蜘蛛外其他蜘蛛爬虫全部封掉。
另外,一些cc攻击,我们可以通过它们的user_agent找到规律。
示例
if ($http_user_agent ~ 'YisouSpider | MJ12bot/v1.4.2|YoudaoBot|Tomato')
{
return 403;
}
说明:user_agent包含以上关键词的请求,全部返回403状态码。
测试
1.curl -A "123YisouSpider1.0"
2.curl -A "MJ12bot/v1.4.1"
Nginx基于$http_referer的访问控制
前面在rewrite做防盗链时用到过此变量
基于此变量,我们也可以做一些特殊需求
示例
背景,网站被挂马,搜索引擎的网页时有问题的,当通过搜索引擎点击到网站时,却显示一个博彩网站,由于查找木马需要时间,不能马上解决,为了不影响用户体验,可以针对此类请求做一个特殊操作。
比如,可以把从百度访问的链接直接返回404状态码,或者返回一段Html代码
if($http_referer ~ 'baidu.com')
{
return 404;
}
或者
if($http_referer ~ 'baidu,com')
{
return 200 "<html><script>window.location.href='//$host%request_uri';</script></html>";
}
Nginx的访问控制
Nginx基于$user_agent的访问控制
可以通过ngx_http_limit_module 和nex_http_limit_req_module模块来实现限速的功能。
ngx_http_limit_conn_module
该模块主要限制下载速度
1.并发限制
配置示例
{
...
limit_conn_zone $binary_remote_addr zone=aming:10m;
limit_conn _status 503; /针对错误错误返回状态码
limit_conn_log_leverl error; //针对错误日志级别
...
server
{
...
limit_conn aming 10;\
...
}
}
说明:首先用limit_conn_zone定义了一个内存区块索引aming,大小为10m,它以$binar_remote_addr作为key
该配置只能在http里面配置,不支持在server里配置。
limit_conn定义针对aming这个zone,并发连接为10个。在这里需要注意一下,这个10指的是单个ip的并发最多为10个
以ab命令进行并发测试访问控制
ab -n 5 -c 5 http//www.网站.com/uri
-n:连接5次
-c:并发5次
2.速度限制
location ~ /download/ {
...
limit_tate_after 512k;
limit_rate 150k;
...
}
说明:limit_rate_after定义当一个文件下载到指定大小(本例512k)之后开始限速;
limit_rate 定义下载速度为150k/s。
注意:这两个参数针对每个请求限速
ngx_http_limit_req_module
该模块主要用来限制连接数
1.limit_req_zone
语法:limit_req_zone $variable zone=name:size rate=rate;
默认值:none
配置段:http
设置一块共享内存限制域用来保存键值的状态参数。特别是保存了当前超出请求的数量。
键的值就是指定的变量(空值不会被计算)
如limit_req_zone $birnary_remote_addr zone=one:10m rate=1r/s;
说明:区域名称为one,大小为10m,平均处理的请求频率不能超过每秒一次,键值是客户端IP
使用$binary_remote_addr变量,可以将每条状态记录的大小减少到64字节,这样1M的内存可以保存大约1万6千个64字节的记录。
如果限制域的存储空间耗尽了,对于后续所有请求,服务器都会返回503(service Temporaily Unavailable)错误。
速度可以设置为每秒处理请求数和每分钟处理请求数,其值必须是整数,所以如果需要指定每秒处理少于1个的请求,2s处理一个请求,可以使用"30r/m"。
2.limit_req
语法:limit_req zone=name [burst=number] [nodelay];
默认值:-
配置段:http,server,location
设置对应的共享内存限制域和允许被处理的最大请求数阀值。
如果请求的频率限制域配置的值,请求处理会被延迟,所以所有的请求都是以定义的频率被处理的。
超过频率限制的请求会被延迟,直到被延迟的请求超过了定义的阈值,
这时这个请求会被终止,并返回503(service Temporaily Unavailable)错误
这个阀值的默认值为0.如
limit_req_zone $binary_remote_addr zone=aming:10m rate=1r/s
server {
location /upload/ {
limit_req zone=aming burst=5;
}
}
限制平均每秒不超过一个请求,同时允许超过频率限制的请求数不多于5个
如果不希望超过的请求被延迟,可以用nodelay参数,如
limit_req zone=aming burts=5 nodelay;
示例
http {
limit_req_zone $binary_remote_addr zone=aming:10m rate=1r/s;
server {
location ^~ /download/ {
limit_req zone = aming burts=5;
}
}
}
Nginx用户认证
安装httpd
yum -y install httpd
使用htpasswd生产密码文件
htpasswd -c /usr/local/nginx/conf/htpasswd aming //aming是用户名
option 说明
-c:create,创建的意思,如果在想往文件里增加用户就不要使用这个选项了因为会把原来的文件覆盖。
-h:查看帮助
-m:默认就是使用了这个选项,使用Md5加密
-d:使用crypt加密方式
-s:使用SHA加密认证方式
配置nginx用户认证
location /aming/
{
auth_basic "Auth"; //认证的名字
auth_basic_user_file /usr/local/nginx/conf/htpasswd //用户存的密码用户信息
}
测试
curl -uaming:passwd www.aminglinux.com/admin/1.html
Nginx的SSL配置
什么是证书?
证书,洋文叫digital certifocate或public key certifocate
它是用来证明某某东西确实是某某的东西,通俗的讲,证书就好比例子里面的公章,通过公章,可以证明该介绍信确实是对应的公司发出的
理论上,人人都可以找个证书工具,自己做一个证书。那如何防止坏人自己制作证书出来骗人呢?
什么是CA
CA是Certficate Authority的缩写,也叫”证书授权中心“。它是负责管理和签发证书的第三方机构,就好比例子里面的中介C公司。
一般来说,CA必须是所有行业和所有公众都信任的、认可的。因此它必须具有足够的权威性。
就好比A、B两公司都必须信任C公司,才会找C公司作为公章的中介
什么是CA证书?
CA证书,顾名思义,就是CA颁发的证书。
什么是证书之间的信任关系?
在开篇的例子,引入到中介后,业务员要同时带两个介绍信。第一个介绍信包含了两个公章,并注明,公章C信任公章A,证书间的信任关系,就和这个类似。就是用一个证书来证明另一个证书是真实可信的。
什么是证书信任链?
实际上,证书之间的信任关系,是可以嵌套的。
比如,C信任A1,A1信任A2,A2信任A3,。。。这个叫做证书的信任链。
只要你信任链上的头一个证书,那后续的证书,都是可以信任的。
什么是根证书?
根证书英文叫”root certificate“,为了说清楚根证书是咋回事,再来看个稍微复杂点的例子。
假设C证书信任A和B,任何A信任A1和A2;B信任B1和B2。则它们之间,构成如下的一个树形关系(一个倒立的树)。
处于最顶上的树根位置的那个证书,就是”根证书“。除了根证书,其他证书都要依靠上一级的证书,来证明自己。
那谁来证明"根证书"可靠呢?
实际上,根证书自己证明自己是可靠的(或者换句话说,根证书是不需要被证明的)
根证书是整个证书体系安全的根本。
所以某个证书体系中,根证书除了问题(不在可信了),那么所有被根证书所信任的其他证书,也就不可再信了。
证书有啥用?
CA证书的作用有很多,只列出常用的几个。
验证网站是否可信(针对HTTPS)
通常,我们如果访问某些敏感的网页(比如用户登录的页面),其协议都会使用https而不是http,因为http是明文的。一旦有坏人再偷窥你的网络通讯,就可以看到网络通讯的内容(比如你的密码、银行账号、等)但是不要以为有了HTTPS就安全了,如果有一个坏人,搞了一个假的网络站点,然后诱骗你上这个站点。你一不留神就把账号密码输入进去了,坏蛋阴谋就得逞了
为了防止坏人这么干,HTTPS协议除了有加密的机制 ,会验证该站点的CA证书(类似于验证介绍信的公章),如果浏览器发现该证书没有问题(证书被某个根证书信任、证书上绑定的域名和该网站的域名一致、证书没有过期),那么页面直接打开,否则的话,浏览器就会给出一个警告,告诉你该网站的证书存在问题,是否继续访问该站点。
SSL认证原理
要想弄明白SSL认证原理,首先要对CA有所了解,它在SSL认证过程中有非常重要的作用
说白了,CA就是一个组织,专门为网络服务器颁发证书的,国际知名的CA机构有VeriSign、Symantec,国内的有Globalsign。每一家CA都有自己的根证书,用来对它所签发的服务器端证书进行验证。
如果服务器提供方想为自己的服务器申请证书,它就需要想CA机构提出申请。
服务器提供方向CA提供自己的身份信息,CA判明申请者的身份后,就为它分配一个公钥。
并且CA将公钥和服务器身份绑在一起,并为之签字,这就形成了一个服务器证书。
如果一个用户想鉴别另一个证书的真伪,他就用CA的公钥对那个证书上的签字进行验证,一旦验证通过,该证书就被认为是有效的。
证书的内容包括:电子签证机关的信息,公钥用户信息,公钥,权威机构的签字和有效期等等。
目前,证书的格式和验证方法普遍遵循X.509国际标准
申请证书过程
首先要有一个CA根证书,然后用CA根证书来签发用户证书。
用户进行证书申请
1.先生成一个私钥
2.用私钥生成证书请求(证书请求里应含有公钥信息)
3.利用证书服务器的CA根证书来签发证书
这样最终拿到一个由CA根证书签发的证书,其实证书里仅有公钥,而私钥是在用户手中的
SSL工作流程(单向)
1.客户端say hello服务端
2.服务端将证书、公钥等发给客户端
3.客户端CA验证证书,成功继续、不成功弹出选择页面
4.客户端告知服务端所支持的加密算法
5.服务端选择最高级别加密算法明文通知客户端
6.客户端生成随机对称密钥KEY,使用服务端公钥加密发送给服务端
7.服务端使用私钥解密,获取对称密钥key。
8.后续客户端与服务端使用该密钥key进行加密通信
SSL工作流程(双向)
单项认证,仅仅是客户端需要检验服务端证书是否是正确的,而服务端不会检验客户端证书是否是正确的。
服务器端证书,而服务器也需要通过CA的公钥证书证书来验证客户端证书
双向验证的流程:
1.客户端say hello服务端
2.服务端将证书、公钥等发给客户端
3.客户端CA验证证书,成功继续、不成功弹出选择页面
4.客户端将自己的证书和公钥发给服务端
5.服务端验证客户端证书,如不通过直接断开连接
6.客户端告知服务端所支持的加密算法
7.服务端选择最高级别加密算法加密后发送给客户端
8.客户端生成随机对称密钥KEY,使用服务端公钥加密发送给服务端
9.服务端使用私钥解密,获取对称密钥key。
10.后续客户端与服务端使用该密钥key进行加密通信
生成CA根证书
mkdir /etc/pki/ca_test //创建CA根证书的目录
cd /etc/pki/ca_test
mkdir root server client newcerts 创建几个相关的目录
echo 01 > serial //定义序列号为01
echo 01 > crlnumber //创建crt号为01
touch index.txt //创建index.txt
cd ..
vi tls/openssl.cnf //改配置文件
default_ca = CA_default 改为 default_ca = CA_test
[ CA_default ] 改为 [ CA_test ]
dir = /etc/pki/CA 改为 dir = /etc/pki/cd_test
certificate = $dir/cacert.pem 改为certficate = $dir/root/ca.crt
private_key = $dir/private/cakey.pe 改为 private_key = $dir/root/ca.key
openssl genrsa -out /etc/pki/ca_test/root/ca.key //生成私钥
openssl req -new -key /etc/pki/ca_test/root/ca.key -out /etc/pki/ca_test/root/ca.csr
//生成请求文件,会让我们填写一些指标,这里要注意:如果在这一步填写了相应的指标
比如country Name、State or Province Name、hostname。
openssl x509 -req -days 3650 -in /etc/pki/ca_test/root/ca.csr -sinkey /etc/pki/ca_test/root/ca.key -out /etc/oki/ca_test/root/ca.crt
生成Server端证书
cd /etc/pki/ca_test/server
openssl grnrsa -out server.key //生成私钥文件
openssl req -new -key server.key -out server.csr //生成证书请求文件,填写信息需要和ca.csr中的organization.Name保持一致
openssl ca -in server.csr -cert /etc/pki/ca_test/root/ca.csr -keyfile /etc/pki/ca_test/root/ca.key -out server.csr
//用根证书签名server.csr,最后生成公钥文件server.crt,此步骤会有两个地方需要输入y
生成客户端证书
如果做ssl的双向认证,还需要给客户端生成一个证书,步骤和上面的基本一致
cd /etc/pki/ca_test/client
openssl genrsa -out client.key //生成私钥文件
openssl req -new -key client.key -out client.csr //生成请求文件,填写信息需要和ca.csr中的Organization Name保持一致
openssl ca -in client.csr -cert /etc/pki/ca_test/root/ca.crt -keyfile /etc/pki/ca_test/root/ca.key -out client.crt -days 3650
//签名client.csr, 生成client.crt,此步如果出现
failed to update database
TXT_DB error number 2
需执行
#sed -i 's/unique_subject = yes/unique_subject = no/' /etc/pki/ca_test/index.txt.attr
执行完,再次重复执行签名client.csr那个操作
Nginx单向认证
配置实例(单向)
cp /etc/pki/ca_test/server/server.* /usr/local/nginx/conf/
{
listen 443 ssl;
server_name www.aminglinux.com;
index index.html index.php;
root /data/wwwroot/aminglinux.com;
ssl on;
ssl_certificate server.crt;
ssl_certificate_key server.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ALL:!DH:!EXPORT:!RC4:+HIGH:+MEDIUM:!eNULL;
ssl_prefer_server_ciphers on;
...
}
配置说明
1. 443端口为ssl监听端口。
2. ssl on表示打开ssl支持。
3. ssl_certificate指定crt文件所在路径,如果写相对路径,必须把该文件和nginx.conf文件放到一个目录下。
4. ssl_certificate_key指定key文件所在路径。
5. ssl_protocols指定SSL协议。
6. ssl_ciphers配置ssl加密算法,多个算法用:分隔,ALL表示全部算法,!表示不启用该算法,+表示将该算法排到最后面去。
7. ssl_prefer_server_ciphers 如果不指定默认为off,当为on时,在使用SSLv3和TLS协议时,服务器加密算法将优于客户端加密算法。
Nginx双向认证
cp /etc/pki/ca_test/root/ca.crt /usr/local/nginx/conf/
配置示例:
{
listen 443 ssl;
server_name www.aminglinux.com;
index index.html index.php;
root /data/wwwroot/aminglinux.com;
ssl on;
ssl_certificate server.crt;
ssl_certificate_key server.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ALL:!DH:!EXPORT:!RC4:+HIGH:+MEDIUM:!eNULL;
ssl_prefer_server_ciphers on;
ssl_client_certificate ca.crt; //这里的ca.crt是根证书公钥文件
ssl_verify_client on;
...
}
配置说明
如果不进行以下操作,浏览器会出现400错误。400 Bad Request(No required SSL certificate was sent)
首先需要将client.key转换为pfx(p12)格式
# cd /etc/pki/ca_test/client
# openssl pkcs12 -export -inkey client.key -in client.crt -out client.pfx //这一步需要输入一个自定义密码,一会在windows上安装的时候要用到,需要记一下。
然后将client.pfx拷贝到windows下,双击即可安装。
也可以直接curl测试:
curl -k --cert /etc/pki/ca_test/client/client.crt --key /etc/pki/ca_test/client/client.key https://www.aminglinux.com/index.html
Nginx错误日志
Nginx错误日志平时不用太关注,但是一旦出了问题就需要借助错误日志来判断问题所在。
错误参数格式:error_log /path/to/log level;
Nginx错误日志级别
常见的错误日志级别有debug | info | notice | warn | error | crit | alert | emerg
级别越高记录的信息越少,如果不定义,默认的级别为errot
它可以配置在main、http、server、location段里。
如果在配置文件中定义了两个error_log,在同一个配置段里的话产生冲突,所以同一个段里只允许配置一个error_log.
但是在不同段里是可以的
Nginx错误日志示例
error_log /var/log/nginx/error_log crit
如果想彻底关闭error_log,需要这样配置
error_log /dev/null;
Nginx访问日志
Nginx访问日志可以设置自定义的格式,来满足特定的需求。
访问日志格式示例
示例1
log_format combined_realip '$remote_addr $http_x_forwarded_for [$time_local]'
'$host "$request_uri" $status'
'"$http_referer" "$http_user_agent"';
示例2
log_format main '$remote_addr [$time_local] '
'$host "$request_uri" $status "$request"'
'"$http_referer" "$http_user_agent" "$request_time"';
若不配置log_format或者不在access_log配置中指定log_format,则默认格式为:
'$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent";
常见变量
变量 说明
$time_local 通用日志格式下的本地时间;(服务器时间)
$remote_addr 客户端(用户)IP地址
$status 请求状态码,如200,404,301,302等
$body_bytes_sent 发送给客户端的字节数,不包括响应头的大小
$bytes_sent 发送给客户端的总字节数
$request_length 请求的长度(包括请求行,请求头和请求正文)
$request_time 请求处理时间,单位为秒,小数的形式
$upstream_addr 集群轮询地址
$upstream_response_time 指从Nginx向后端(php-cgi)建立连接开始到接受完数据然后关闭连接为止的时间
$remote_user 用来记录客户端用户名称
$request 请求方式(GET或者POST等)+URL(包含$request_method,$host,$request_uri)
$http_user_agent 用户浏览器标识
$http_host 请求的url地址(目标url地址)的host
$host 等同于$http_host
$http_referer 来源页面,即从哪个页面转到本页,如果直接在浏览器输入网址来访问,则referer为空
$uri 请求中的当前URI(不带请求参数,参数位于$args),不同于浏览器传递的$request_uri的值,它可以通过内部重定向,或者使用index指令进行修改。
$document_uri 等同于$uri
$request_uri 比$uri多了参数,即$uri+$args
$http_x_forwarded_for 如果使用了代理,这个参数会记录代理服务器的ip和客户端的ip ;
$server_port 请求的服务端口
Nginx访问日志过滤
一个网站,会包含很多元素,由其是有大量的图片、JS、CSS等静态元素。
这样的请求其实客户以不用记录日志。
配置示例、
location ~* ^.+\.(gif|jpg|png|css|js)$
{
access_log off;
}
或
location ~* ^.+\.(gif|jpg|png|css|js)$
{
access_log /dev/null;
}
Nginx访问日志切割
如果任由访问日志写下去,日志文件会变得越来越大,甚至是写满硬盘。所以,我们需要想办法把日志做切割,比如每天生成一个新的日志,旧的日志按规定时间删除即可。
实现日志切割可以通过写shell脚本或者系统的日志切割机制实现
shell脚本切割Nginx日志
#!/bin/bash
logdir=/var/log/nginx //定义日志路径
prefix=`date -d "-1 day" +%y%m%d` //定义切割后的日志前缀
cd $logdir
for f in `ls *.log`
do
mv $f $f-prefix //把日志改名
done
/bin/kill -USER1 $(cat /usr/local/nginx/logs/nginx.pid 2>/dev/null)生成新的日志,这个pid要修改为自己的nginx.pid的存放位置
bzip2 *$prefix //压缩日志
find . -type f -mtime +180 | xargs /bin/rm -f //删除超过180天的日志
我用以上脚本总是出错我自己写了一个脚本如下:
#!/bin/bash
logdir=/tmp/jiao/
prefix=$(date -d "-1 day" +%y%m%d)
cd $logdir
for f in *.log; do
if [ -f "$f" ]; then # 确保是一个文件
mv "$f" "${f}-${prefix}"
fi
done
pid=$(cat /mnt/shell/services/nginx/logs/nginx.pid 2>/dev/null)
if [ -n "$pid" ]; then # 确保 pid 不为空
/bin/kill -s USR1 "$pid"
fi
bzip2 *${prefix}*
find . -type f -name "*.${prefix}.bz2" -mtime +180 -print0 | xargs -0 /bin/rm -f
系统日志切割机制
在/etc/logrotate.d/下创建Nginx文件,内容为:
/data/logs/*log {
daily
rotate 30
missingok
notifempty
compress
sharedscripts
postrotate
/bin/kill $(cat /usr/local/nginx/logs/nginx.pid 2>/dev/null) 2 > /dev/null || : 生成新的日志,这个pid要修改为自己的nginx.pid的存放位置
endscript
}
说明:
1.nginx日志在/data/logs/目录下面,日志名字以log结尾
2.daily表示每天切割
3.rotate 30表示保留日志30天
4.missingok 表示忽略错误
5.notifempy 表示如果日志为空,不切割
6.compress 表示压缩
7.sharedscripts和endscript中间可以引用系统命令
8.postrotate表示切割之后要执行的命令
Nginx优化
Nginx作为高性能web服务器,即使不特意调整配置参数也可以处理大量的并发请求
worker进程
worker_processes
该进程表示启动几个工作进程,建议和本机cpu核数保持一致,每一核cpu处理一个进程。
worker_rlimit_nofile
它表示Nginx最大可用的文件描述符个数,需要配合系统的最大描述符,建议设置为102400.
#注:文件描述符是什么?在客户端访问服务器建立连接时就会生成一个文件描述符,每建立一次连接都会生成一个文件描述符
还需要在系统里执行ulimit -n 102400才可以。
也可以直接修改配置文件/etc/security/limits.conf修改
增加:
#* soft nofile 655350 (去掉前面的#)
#* hard nofile 655350 (去掉前面的#)
worker_connections
该参数用来配置每个Nginx worker进程最大处理的连接数,这个参数也决定了该Nginx服务器最多能处理多少客户端请求
(worker——processes * worker_connections),建议把该参数设置为10240,不建议太大。
http和tcp连接
use epoll
使用epoll模型的事件驱动模型,该模型为Linux系统下最优方式
multi_accept on
使每个worker进程可以同时处理多个客户端请求。
sendfile on
使用内核的FD文件传输功能,可以减少user mode和kernel mode 的切换,从而提升服务器性能。
tcp_nopush on
当tcp_nopush设置为on时,会调用tcp_cork方法进行数据传输。
使用该方法会产生这样的效果,当应用程序产生数据时,内核不会立马封装包,而是当数据量积累到一定量时才会封装,然后传输。
tcp_nodelay on
不缓存data-sends(关闭Nagle算法),这个能够提高高频发送小数据报文的实时性。
关于Nagle算法
假如需要频繁的发送一些小包数据,比如说1个字节,以IPV4为例的话,则每个包都要附带40字节的头
也就是说,总计41个字节的数据里,其中1个字节是我们需要的数据。
为了解决这个问题,,出现了 Nagle算法。
它规定,如果包的大小满足MSS,那么可以立即发送,否则数据会被放到缓冲区,等待已经发送的包被确认了之后才能继续发送。
通过这样的规定,可以降低网络里小包的的数量,从而提升网络性能。
keepalive_timeout
定义长连接的超时时间,建议30s,太短或者太长都不一定合适,当然,最好是根据业务自身的情况动态调整该参数
keepalive_requests
定义当客户端和服务端处于长连接情况下,每个客户端最多可以请求多少次,可以设置很大,比如50000
reset_timeout_connection on
设置为on的话,当客户端不再向服务端发送请求时,允许服务端关闭该连接。
client_body_timeout
客户端如果在该指定时间内没有加载完body数据,则断开连接,单位是秒,默认60,可以设置为10
send_timeout
这个超时时间是发送响应的超时时间,既Nginx服务器向客户端发送了数据包,但客户端一直没有去接收这个数据包。
这个某个连接超过send_timeout定义超时时间,那么Nginx将会关闭这个连接。单位是秒,可以设置为3
buffer和cache(以下配置都是针对单个请求)
client_body_buffer_size
当客户端以post方法提交一些数据到服务端会先写入到client_body_buffer_size中,如果buffer写满会写到临时文件里建议调整为128k
client_max_body_size
浏览器在发送含有较大HTTP body的请求时,其头部会有一个content-Length字段,client_max_body_size是用来限制content-length所示值的大小,这个限制body的配置不用等Nginx接收完所有的HTTP包体,就可以告诉用户请求过大不被接受。会返回413状态码。
就直接发送413(Request Enjtity Too Large)响应给客户端
将该数值设置为0,则禁用限制,建议设置为10m。
client_header_buffer_size
设置客户端header的buffer的大小,建议4k
large_client_header_buffers
对于比较大的header(超过client_header_buffer_size)将会使用该部分buffer,两个数值,第一个是个数,第二个是每个buffer的大小
建议设置为4 8k
open_file_cache
该参数对以下信息进行缓存:
打开文件描述符的文件大小和修改时间信息;
存在的目录信息;
搜索文件的错误信息(文件不存在无权限读取等信息)
格式:open_file_cache max=1020400 inactive=20s;
open_file_cache_valid
指多长时间检查一次缓存的有效信息,建议设置为30s。
open_file_cache_min_user
open_file_cache指令中的Inctive参数时间内文件的最少使用次数
如,将参数设置为1,则表示,如果文件在inactive时间内一次都没被使用,它将被移除。
建议设置为2.
压缩
对于纯文本的内容,Nginx是可以使用gzip压缩的,使用压缩技术可以减少对带宽的消耗
由bfx_http_gzip_module模块支持
配置如下:
gzip on; //开启gzip功能
gzip_min_length 1024; //设置请求资源超过该数值才进行压缩,单位字节
gzip_buffers 15 8k; //设置压缩使用buffer大小,第一个数字为数量,第二个为每个buffer的大小
gzip_comp_level 6; //设置压缩级别,范围1-9,9压缩级别最高,也最耗费cpu资源
gzip_types text/plain application/x-javascript text/css application/xml image/jpeg image/gif image/png //指定哪些类型的文件需要被压缩
gzip_idisable "MSIE 6\."; //IE6浏览器不启用压缩
测试
curl -I -H "Accept-Encoding: gzip,deflate" http://www.aminglinux.com/1.css
日志
错误日志级别调高,比如cirt级别,尽量少记录无关紧要的日志
对于访问日志,如果不要求记录日志,可以关闭
静态资源的访问日志关闭
静态文件过期
对于静态文件,需要设置一个过期时间,这样可以让这些资源缓存到客户端浏览器,在缓存未失效前,客户端不再向服务器请求相同的资源,从而节省带宽和资源消耗
配置示例如下:
location ~* ^.+\.(gif|jpg|png|css|js)$
{
expires id; //id表示1天,也可以用24h表示一天,
}
作为代理服务器
Nginx绝大多数情况下都是作为代理或者负载均衡的角色。
因为前面已经介绍过参数含义,一下只提供对应配置参数:
http
{
proxy_cache_path /data/nginx_cache/ levels=1:2 keys_zone=my_zone:10m inactive=300s max_size=5g;
...;
server
{
proxy_buffering on;
proxy_buffer_size 4k;
proxy)buffers 2 4k;
proxy_busy_buffers_size 4k;
proxy_temp_path /tmp/nginx_proxy_tmp 1 2;
proxy_max_temp_file_size 20M;
proxy_temp_file_write_size 8k;
location /
{
proxy_cache my_zone;
...;
}
}
}
说明: proxy_cache_path /data/nginx_cache/ 设置数据的存储目录,levels:定义生成的目录层级1:2表示有两层子目录,key_zone=my_zone:10m 定义一个zone,并分给他大小 inactive=300s
SSL优化
适当减少worker_processes数量,因为ssl功能需要使用cpu计算
使用长连接,因为每次建立ssl会话,都会耗费一定的资源(加密,解密)
开启ssl缓存,简化服务器和客户端的“握手”过程。
ssl_session_cache shared:SSL:10; //缓存为10m
ssl_session_timeout 10m; //会话超时时间为10分钟
调整Linux内核参数
作为高性能WEB服务器,只调整Linux本身的参数是不行的,因为Nginx服务依赖于高性能的操作系统。
以下为常见的几个Linux内核参数优化方法。
1.net.ipv4.tcp_max_tw_buckets
对于tcp连接,服务端和客户端通信完成后状态变为timewait,假如某个服务器非常忙,连接数特别多的话,那么这个timewait数量就会越来越大。
毕竟它也是会占用一定的资源,所以应该有一个最大值,当超过这个值,系统就会删除最早的连接,这样始终保持在一个数量级。
这个数值就是由net.ipv4.tcp_max_tw_buckets来决定的
centos7系统,你可以使用systel -a | grep tw_buckets来查看它的值,默认为32768
你可以适当把它调低,比如调整到8000,毕竟这个状态的连接太多也是会有消耗的。
但你不要把它调到几十、几百这样,因为这种状态的tcp连接也是有用的。
如果同样的客户端再次和服务端通信,就不用再次建立新的连接了,用这个旧的通道,省时省力。
2.net.ipv4.tcp_tw_reycle = 1
该参数的作用是快速回收timewait状态的连接,上面虽然提到系统会自动删除掉timewait状态的连接,但如果把这样的连接重新利用起来岂不是更好。
所以该参数设置为1就可以让timewait状态的连接快速回收,它需要和下面的参数配合一起使用
3.net_ipv4.tcp_tw_reuse = 1
该参数设置为1,将timewait状态的连接重新用于新的tcp连接,要结合上面的参数一起使用
4.net.ipv4.tcp_syncookies = 1
tcp三次握手中,客户端向服务端发起syn请求,服务端收到后,也会向客户端发起syn请求同时连带ack确认,这个重试的过程会持续一段时间(通常高于30s),当这种状态的连接数量非常大时,服务器会消耗很大的资源,从而造成瘫痪。
正常的连接进不来。这种恶意的半连接行为叫做syn flood攻击
设置1,是开启syn cookies,开启后可以避免发生上述的syn flood攻击。
设置该参数后,服务端接收客户端的ack后,在向客户端发送ack+syn之前会要求client在短时间内回应一个序号,如果客户端不能提供序号或者提供的序号不对则认为该客户端不合法,于是不会发ack+syn给客户端,更涉及不到重试
5.net.ipv4.tcp_max_syn_backlog
该参数定义系统能接受的最大半连接状态tcp连接数。客户端向服务端发送了syn包,服务端收到后,会记录一下,该参数决定最多能记录几个这样的连接,在centos7中,默认时256,当有syn flood攻击时,这个数值太小则很容易导致服务器瘫痪,
实际上此服务器并没有消耗太多资源(cpu,内存等),所以可以适当调大它,比如调整到30000。
6.net.ipv4.tcp_syn_retries
该参数适用于客户端,它定义发起syn的最大重试次数,默认为6,建议改为2
7.net.ipv4.tcp_synack_retries
该参数适用于服务端,它定义发起syn+ack的重试次数,默认为5,建议改为2,可以适当预防syn flood攻击
8.net.ipv4.ip_local_port_range
该参数定义端口范围,系统默认保留端口为1024及以下,以上部分为自定义端口。这个参数适用于客户端,当客户端和服务端建立连接时,比如说访问服务端的80端口,客户端随机开启了一个端口和服务端发起连接,这个参数定义随机端口的范围,默认为32768 61000建议调整为1025 61000
9.net.ipv4.tcp_fin_timeout
tcp连接状态中,客户端上有一个FIN-WAIT-2状态,它是状态变迁为timewait前一个状态。
该参数定义不属于任何进程的该连接状态的超时时间,默认为60,建议调整为6.
10.net.ipv4.tcp_keepalive_time
tcp连接状态里,有一个时established状态,只有这个状态下,客户端和服务端才能进行通信。正常情况下,当通信完毕,客户端或服务端会告诉对方要关闭连接,此时状态就会变为timewait,如果客户端没有告诉服务端,并且服务端也没有告诉客户端的话(例如,客户端断网了),此时需要该参数来判定
比如客户端已经断网了,但服务端本次连接的状态依然是established,服务端为了确认客户端是否断网就需要每隔一段时间去发送一个探测包去确认以下看看是否对方在线,这个时间就由该参数决定,它的默认值为7200秒,建议设置为30s
11 net,ipv4.tcp_keepalive_intvl
该参数和上面 的参数是一起的,服务端在短时间内发起了探测,查看是否在线,如果对方并没有确认,此时服务端还不能认定对方不在线,而是要尝试多次。该参数定义重新发送探测的时间,既第一次发现对方有问题后,过多久再次发起探测,默认为75秒,可以改为3s
12.net.ipv4.tcp_keepalive_probes
第10和第11参数规定了何时发起探测和探测后再过多久再次发起探测,但并没有定义一共探测几次才结束,该参数定义了发起探测的包的数量,默认为9,建议设置为2
以上参数仅供参考,具体要根据业务实际情况进行配置
配置目录
/etc/sysctl.conf
查看是否正确配置
sysctl -p
Nginx服务监控
系统级别监控
top
ps
netstat
ss
日志
nginx有一个内置的状态页,需要在编译的时候指定参数-with-http_stub_status_module参数方可打开。也就是说,该功能是由http_stub_status_module模块提供,默认没有加载
配置文件信息
配置Nginx状态信息
增加编译参数--with-http_stub_status_module
配置文件中增加
stub_status on;
localtion /status/ {
stub_status on;
access_log off;
allow 127.0.0.1;
allow Ip/24;
deny all;
}
stub_status on 既打开了状态页
access_log off 不记录日志
allow和deny只允许指定IP和IP段访问,因为这个页面需要保护起来,并不公开,当然也可以做用户认证
测试命令:curl -x127.0.0.1:80 www.amimglinux.com/status/
说明
active connections 活跃的连接数量
server accpts handled requests 总共处理的连接数、成功创建的握手次数、总共处理的请求次数,需要注意,一个连接可以有多次请求。
reading - 读取客户端的连接数。
writing - 响应数据到客户端的数量
waiting - 开启 keep-alive的情况下,这个值等于 active - (reading+writing).意思就是nginx已经处理完,正在等待下一次的请求驻留连接
Nginx支持HTTP/2
http/2是基于https的,所以必须要配置ssl
编译时增加编译参数--with-http_v2_module
关键配置
listen 443 ssl http2;
测试
curl -Ik https://域名.com
Nginx正向代理支持HTTPS
下载第三方模块ngx_http_proxy_connect_module,不同的Nginx版本需要下载不同的patch包
编译时增加编译参数–add-dynamic-module=/path/to/ngx_http_proxy_connect_module
关键配置
proxy_connect
procy_connect_allow 80 443 3000 9070 9074;
proxy_connect_connect_timeout 10s;
proxy_connect_read_timeout 10s;
proxy_connect_send_timeout 10s;
Nginx正向代理配置
Nginx正向代理使用场景并不多见。
需求场景1:
如果在机房中,只有一台机器可以联网,其他机器只有内网,内网的机器想用使用yum安装软件包,在能能联网的机器上配置一个正向代理即可。
Nginx正向代理配置文件
说明: 以下配置文件为nginx官方提供,该方法只能实现针对http的网站的访问,如果是https就会有问题。要想实现https的正向代理,可以使用一个三方模块,后面介绍。
server {
listen 80 default_server;
resolver 119.29.29.29;
location /
{
proxy_pass http://$host$request_uri;
}
}
Nginx正向代理配置执行说明
- resolver
语法:resolver address;`
address为DNS服务器的地址,国内通用的DNS 119.29.29.29为dnspod公司提供。 国际通用DNS 8.8.8.8或者8.8.4.4为google提供。
其他可以参考 http://dns.lisect.com/
示例:resolver 119.29.29.29;
- default_server
之所以要设置为默认虚拟主机,是因为这样就不用设置server_name了,任何域名解析过来都可以正常访问。
- proxy_pass
该指令用来设置要代理的目标url,正向代理服务器设置就保持该固定值即可。关于该指令的详细解释在反向代理配置中。
Nginx正向代理支持https
下载三方模块ngx_http_proxy_connect_module,github地址:https://github.com/chobits/ngx_http_proxy_connect_module
注意,不同的Nginx版本,还需要下载不同的patch包。
下面的例子,以1.9.12为例
wget http://nginx.org/download/nginx-1.9.12.tar.gz
tar -xzvf nginx-1.9.12.tar.gz
cd nginx-1.9.12/
patch -p1 < /path/to/ngx_http_proxy_connect_module/patch/proxy_connect.patch
./configure --add-dynamic-module=/path/to/ngx_http_proxy_connect_module
make && make install
Nginx正向代理配置文件
server {
listen 3128;
# dns resolver used by forward proxying
resolver 119.29.29.29;
# forward proxy for CONNECT request
proxy_connect;
proxy_connect_allow 80 443 3000 9070 9074;
proxy_connect_connect_timeout 10s;
proxy_connect_read_timeout 10s;
proxy_connect_send_timeout 10s;
# forward proxy for non-CONNECT request
location / {
proxy_pass http://$host;
proxy_set_header Host $host;
}
}
测试示例
$ curl https://github.com/ -v -x 127.0.0.1:3128
* Trying 127.0.0.1... -.
* Connected to 127.0.0.1 (127.0.0.1) port 3128 (#0) | curl creates TCP connection with nginx (with proxy_connect module).
* Establish HTTP proxy tunnel to github.com:443 -'
> CONNECT github.com:443 HTTP/1.1 -.
> Host: github.com:443 (1) | curl sends CONNECT request to create tunnel.
> User-Agent: curl/7.43.0 |
> Proxy-Connection: Keep-Alive -'
>
< HTTP/1.0 200 Connection Established .- nginx replies 200 that tunnel is established.
< Proxy-agent: nginx (2)| (The client is now being proxied to the remote host. Any data sent
< '- to nginx is now forwarded, unmodified, to the remote host)
* Proxy replied OK to CONNECT request
* TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 -.
* Server certificate: github.com |
* Server certificate: DigiCert SHA2 Extended Validation Server CA | curl sends "https://github.com" request via tunnel,
* Server certificate: DigiCert High Assurance EV Root CA | proxy_connect module will proxy data to remote host (github.com).
> GET / HTTP/1.1 |
> Host: github.com (3) |
> User-Agent: curl/7.43.0 |
> Accept: */* -'
>
< HTTP/1.1 200 OK .-
< Date: Fri, 11 Aug 2017 04:13:57 GMT |
< Content-Type: text/html; charset=utf-8 | Any data received from remote host will be sent to client
< Transfer-Encoding: chunked | by proxy_connect module.
< Server: GitHub.com (4)|
< Status: 200 OK |
< Cache-Control: no-cache |
< Vary: X-PJAX |
... |
... <other response headers & response body> ... |
...
linux机器上配置全局代理
在 /etc/profile 文件中增加如下三项。
export proxy="http://{proxy_server_ip}:3128"
export http_proxy=$proxy
export https_proxy=$proxy
使配置生效
source /etc/profile
对那些没有域名解析通过绑定hosts文件来访问的域名,不让其走http/https代理,需要额外增加环境变量:
export no_proxy='a.test.com,127.0.0.1,2.2.2.2'
Nginx四层代理服务
nginx 从1.9.0版本开始支持四层代理,但做四层代理时 编译需要添加 --with-stream模块
nginx配置文件示例
http {
......
}
## stram模块 和http模块是一同等级
stream {
upstream app_server{
server 192.168.0.14:8028;
#server 192.168.0.15:8028;
}
server {
listen 8028; #8028端口将以4层TCP协议方式转发至后端app_sever;
proxy_pass app_server; #paoxy_pass定义的名字需要与upstream定义的是相同的
}
}
UDP示例
stream {
upstream dns {
server 192.168.0.13:53; 53是真实的端口
}
server {
listen 5353 udp; 5353端口是转发的端口,别人可以通过5353访问到53端口
proxy_pass dns;
proxy_timeout 5s;
}
}