Nginx基础回顾

img

  • nginx 到底是什么?
    Nginx 是⼀个⾼性能的HTTP和反向代理web服务器,核⼼特点是占有内存少,并发能⼒强
  • Nginx ⼜能做什么事情(应⽤场景)

  • Http服务器(Web服务器)
    性能⾮常⾼,⾮常注重效率,能够经受⾼负载的考验。
    ⽀持50000个并发连接数,不仅如此, CPU和内存的占⽤也⾮常的低, 10000个没有活动的连
    接才占⽤2.5M的内存。

  • 反向代理服务器

  • 正向代理

在浏览器中配置代理服务器的相关信息,通过代理服务器访问⽬标⽹站,代理服务器收
到⽬标⽹站的响应之后,会把响应信息返回给我们⾃⼰的浏览器客户端
img

  • 反向代理
    浏览器客户端发送请求到反向代理服务器(⽐如Nginx),由反向代理服务器选择原始
    服务器提供服务获取结果响应,最终再返回给客户端浏览器

img

  • 负载均衡服务器
    负载均衡,当⼀个请求到来的时候(结合上图), Nginx反向代理服务器根据请求去找到⼀个
    原始服务器来处理当前请求,那么这叫做反向代理。那么,如果⽬标服务器有多台(⽐如上
    图中的tomcat1, tomcat2, tomcat3…),找哪⼀个⽬标服务器来处理当前请求呢,这样⼀
    个寻找确定的过程就叫做负载均衡。
    ⽣活中也有很多这样的例⼦,⽐如,我们去银⾏,可以处理业务的窗⼝有多个,那么我们会
    被分配到哪个窗⼝呢到底,这样的⼀个过程就叫做负载均衡。

负载均衡就是为了解决⾼负载的问题。

  • 动静分离
    img
    Nginx 的特点
  • 跨平台: Nginx可以在⼤多数类unix操作系统上编译运⾏,⽽且也有windows版本

  • Nginx的上⼿⾮常容易,配置也⽐较简单

  • ⾼并发,性能好

  • 稳定性也特别好,宕机概率很低

Nginx的安装

  • 上传nginx安装包到linux服务器, nginx安装包(.tar⽂件)下载地址: http://nginx.org
  • 安装Nginx依赖, pcre、 openssl、 gcc、 zlib(推荐使⽤yum源⾃动安装)
    yum -y install gcc zlib zlib-devel pcre-devel openssl openssl-devel

  • 解包Nginx软件包
    tar -xvf nginx-1.17.8.tar

  • 进⼊解压之后的⽬录 nginx-1.17.8
    cd nginx-1.17.8

  • 命令⾏执⾏./configure

  • 命令⾏执⾏ make

  • 命令⾏执⾏ make install,完毕之后在/usr/local/下会产⽣⼀个nginx⽬录
    img

进⼊sbin⽬录中,执⾏启动nginx命令

cd nginx/sbin
./nginx

然后访问服务器的80端⼝(nginx默认监听80端⼝)

img

  • Nginx主要命令

  • ./nginx 启动nginx

  • ./nginx -s stop 终⽌nginx(当然也可以找到nginx进程号,然后使⽤kill -9 杀掉nginx进程)

  • ./nginx -s reload (重新加载nginx.conf配置⽂件)

Nginx核⼼配置⽂件解读

Nginx的核⼼配置⽂件conf/nginx.conf包含三块内容:全局块、 events块、 http块
全局块
从配置⽂件开始到events块之间的内容,此处的配置影响nginx服务器整体的运⾏,⽐如worker进
程的数量、错误⽇志的位置等

# 运行用户
#user  nobody;
# worker进程数量通常设置为和cpu数量相等
worker_processes  1;
# 全局错误日志以及pid文件位置
#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;

events块
events块主要影响nginx服务器与⽤户的⽹络连接,⽐如worker_connections 1024,标识每个
workderprocess⽀持的最⼤连接数为1024

events {
   # 使用epoll的I/O模型(如果你不知道Nginx该使用哪种轮询方法,会自动选择一个最适合你操作系统的)
    use epoll;
   # 每个进程允许最大并发数
    worker_connections  1024;
}

http块
http块是配置最频繁的部分,虚拟主机的配置,监听端⼝的配置,请求转发、反向代理、负载均衡

http {
  	# 引入mime类型定义文件
    include       mime.types;
    default_type  application/octet-stream;
		# 设置日志格式
    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';
		 # Nginx访问日志存放位置
    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;
		# 设置连接超时时间
    #keepalive_timeout  0;
    keepalive_timeout  65;
		# 开启gzip压缩
    #gzip  on;

    server {
    		# 监听端口
        listen       80;
    		# 定义使用localhost访问
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;
				# 默认请求
        location / {
            root   html; # 默认的网站根目录
            index  index.html index.htm; # 索引页欢迎页
      			deny 172.168.22.11;   # 禁止访问的ip地址,可以为all
    				allow 172.168.33.44; # 允许访问的ip地址,可以为all
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
    		# 错误提示页面
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }


    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}


    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}

}

Nginx配置语法

就跟前面文件作用讲解的图所示,Nginx 的主配置文件是 /etc/nginx/nginx.conf,你可以使用 cat -n nginx.conf 来查看配置。

nginx.conf 结构图可以这样概括:

main        # 全局配置,对全局生效
├── events  # 配置影响 Nginx 服务器或与用户的网络连接
├── http    # 配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置
│   ├── upstream # 配置后端服务器具体地址,负载均衡配置不可或缺的部分
│   ├── server   # 配置虚拟主机的相关参数,一个 http 块中可以有多个 server 块
│   ├── server
│   │   ├── location  # server 块可以包含多个 location 块,location 指令用于匹配 uri
│   │   ├── location
│   │   └── ...
│   └── ...
└── ...

一个 Nginx 配置文件的结构就像 nginx.conf 显示的那样,配置文件的语法规则:

  1. 配置文件由指令与指令块构成;
  2. 每条指令以 ; 分号结尾,指令与参数间以空格符号分隔;

  3. 指令块以 {} 大括号将多条指令组织在一起;

  4. include 语句允许组合多个配置文件以提升可维护性;

  5. 使用 # 符号添加注释,提高可读性;

  6. 使用 $ 符号使用变量;

  7. 部分指令的参数支持正则表达式;

全局变量

全局变量名 功能
$host 请求信息中的 Host,如果请求中没有 Host 行,则等于设置的服务器名,不包含端口
$request_method 客户端请求类型,如 GET、POST
$remote_addr 客户端的 IP 地址
$args 请求中的参数
$arg_PARAMETER GET 请求中变量名 PARAMETER 参数的值,例如:$http_user_agent(Uaer-Agent 值), $http_referer
$content_length 请求头中的 Content-length 字段
$http_user_agent 客户端agent信息
$http_cookie 客户端cookie信息
$remote_addr 客户端的IP地址
$remote_port 客户端的端口
$http_user_agent 客户端agent信息
$server_protocol 请求使用的协议,如 HTTP/1.0、HTTP/1.1
$server_addr 服务器地址
$server_name 服务器名称
$server_port 服务器的端口号
$scheme HTTP 方法(如http,https)

gzip压缩

gzip 是一种常用的网页压缩技术,传输的网页经过 gzip 压缩之后大小通常可以变为原来的一半甚至更小(官网原话),更小的网页体积也就意味着带宽的节约和传输速度的提升,特别是对于访问量巨大大型网站来说,每一个静态资源体积的减小,都会带来可观的流量与带宽的节省。

百度可以找到很多检测站点来查看目标网页有没有开启 gzip 压缩,在下随便找了一个 <网页GZIP压缩检测> 输入掘金 jd.com 来偷窥下掘金有没有开启 gzip。

img

使用 gzip 不仅需要 Nginx 配置,浏览器端也需要配合,需要在请求消息头中包含 Accept-Encoding: gzip(IE5 之后所有的浏览器都支持了,是现代浏览器的默认设置)。一般在请求 html 和 css 等静态资源的时候,支持的浏览器在 request 请求静态资源的时候,会加上 Accept-Encoding: gzip 这个 header,表示自己支持 gzip 的压缩方式,Nginx 在拿到这个请求的时候,如果有相应配置,就会返回经过 gzip 压缩过的文件给浏览器,并在 response 相应的时候加上 content-encoding: gzip 来告诉浏览器自己采用的压缩方式(因为浏览器在传给服务器的时候一般还告诉服务器自己支持好几种压缩方式),浏览器拿到压缩的文件后,根据自己的解压方式进行解析。

先来看看 Nginx 怎么进行 gzip 配置,和之前的配置一样,为了方便管理,还是在 /etc/nginx/conf.d/ 文件夹中新建配置文件 gzip.conf。

# /etc/nginx/conf.d/gzip.conf

gzip on; # 默认off,是否开启gzip
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;

# 上面两个开启基本就能跑起了,下面的愿意折腾就了解一下
gzip_static on;
gzip_proxied any;
gzip_vary on;
gzip_comp_level 6;
gzip_buffers 16 8k;
# gzip_min_length 1k;
gzip_http_version 1.1;
  • gzip_types:要采用 gzip 压缩的 MIME 文件类型,其中 text/html 被系统强制启用;
  • gzip_static:默认 off,该模块启用后,Nginx 首先检查是否存在请求静态文件的 gz 结尾的文件,如果有则直接返回该 .gz 文件内容;

  • gzip_proxied:默认 off,nginx做为反向代理时启用,用于设置启用或禁用从代理服务器上收到相应内容 gzip 压缩;

  • gzip_vary:用于在响应消息头中添加 Vary:Accept-Encoding,使代理服务器根据请求头中的 Accept-Encoding 识别是否启用 gzip 压缩;

  • gzip_comp_level:gzip 压缩比,压缩级别是 1-9,1 压缩级别最低,9 最高,级别越高压缩率越大,压缩时间越长,建议 4-6;

  • gzip_buffers:获取多少内存用于缓存压缩结果,16 8k 表示以 8k*16 为单位获得;

  • gzip_min_length:允许压缩的页面最小字节数,页面字节数从header头中的 Content-Length 中进行获取。默认值是 0,不管页面多大都压缩。建议设置成大于 1k 的字节数,小于 1k 可能会越压越大;

  • gzip_http_version:默认 1.1,启用 gzip 所需的 HTTP 最低版本;

配置负载均衡

主要配置如下:

http {
  upstream myserver {
  	# ip_hash;  # ip_hash 方式
    # fair;   # fair 方式
    server 127.0.0.1:8081;  # 负载均衡目的服务地址
    server 127.0.0.1:8080;
    server 127.0.0.1:8082 weight=10;  # weight 方式,不写默认为 1
  }
 
  server {
    location / {
    	proxy_pass http://myserver;
      proxy_connect_timeout 10;
    }
  }
}

Nginx 提供了好几种分配方式,默认为轮询,就是轮流来。有以下几种分配方式:

  1. 轮询,默认方式,每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务挂了,能自动剔除;
  2. weight,权重分配,指定轮询几率,权重越高,在被访问的概率越大,用于后端服务器性能不均的情况;

  3. ip_hash,每个请求按访问 IP 的 hash 结果分配,这样每个访客固定访问一个后端服务器,可以解决动态网页 session 共享问题。负载均衡每次请求都会重新定位到服务器集群中的某一个,那么已经登录某一个服务器的用户再重新定位到另一个服务器,其登录信息将会丢失,这样显然是不妥的;

  4. fair(第三方),按后端服务器的响应时间分配,响应时间短的优先分配,依赖第三方插件 nginx-upstream-fair,需要先安装;

配置动静分离

动静分离在之前也介绍过了,就是把动态和静态的请求分开。方式主要有两种,一种 是纯粹把静态文件独立成单独的域名,放在独立的服务器上,也是目前主流推崇的方案。另外一种方法就是动态跟静态文件混合在一起发布, 通过 Nginx 配置来分开。

通过 location 指定不同的后缀名实现不同的请求转发。通过 expires 参数设置,可以使浏览器缓存过期时间,减少与服务器之前的请求和流量。具体 expires 定义:是给一个资源设定一个过期时间,也就是说无需去服务端验证,直接通过浏览器自身确认是否过期即可,所以不会产生额外的流量。此种方法非常适合不经常变动的资源。(如果经常更新的文件,不建议使用 expires 来缓存),我这里设置 3d,表示在这 3 天之内访问这个URL,发送一个请求,比对服务器该文件最后更新时间没有变化。则不会从服务器抓取,返回状态码 304,如果有修改,则直接从服务器重新下载,返回状态码 200。

server {
  location /www/ {
  	root /data/;
    index index.html index.htm;
  }
  
  location /image/ {
  	root /data/;
    autoindex on;
  }
}

设置二级域名虚拟主机

在某某云 ☁️ 上购买了域名之后,就可以配置虚拟主机了,一般配置的路径在 域名管理 -> 解析 -> 添加记录 中添加二级域名,配置后某某云会把二级域名也解析到我们配置的服务器 IP 上,然后我们在 Nginx 上配置一下虚拟主机的访问监听,就可以拿到从这个二级域名过来的请求了。

img

现在我自己的服务器上配置了一个 abc 的二级域名,也就是说在外网访问 abc.lvxueyang.vip 的时候,也可以访问到我们的服务器了。

img

配置反向代理

反向代理是工作中最常用的服务器功能,经常被用来解决跨域问题,下面简单介绍一下如何实现反向代理。

首先进入 Nginx 的主配置文件:

然后我们去 http 模块的 server 块中的 location /,增加一行将默认网址重定向到最大学习网站 Bilibili 的 proxy_pass 配置 :

server {
	listen:80;
  server_name:lvxueyang.vip;
  location / {
  	proxy_pass http://www.bilibili.com;	
  }
}

比如我们监听 9001 端口,然后把访问不同路径的请求进行反向代理:

  1. 把访问 http://127.0.0.1:9001/edu 的请求转发到 http://127.0.0.1:8080
  2. 把访问 http://127.0.0.1:9001/vod 的请求转发到 http://127.0.0.1:8081

这种要怎么配置呢,首先同样打开主配置文件,然后在 http 模块下增加一个 server 块:

server {
  listen 9001;
  server_name *.sherlocked93.club;

  location ~ /edu/ {
    proxy_pass http://127.0.0.1:8080;
  }
  
  location ~ /vod/ {
    proxy_pass http://127.0.0.1:8081;
  }
}

反向代理还有一些其他的指令,可以了解一下:

  1. proxy_set_header:在将客户端请求发送给后端服务器之前,更改来自客户端的请求头信息。
  2. proxy_connect_timeout:配置Nginx与后端代理服务器尝试建立连接的超时时间。

  3. proxy_read_timeout:配置Nginx向后端服务器组发出read请求后,等待相应的超时时间。

  4. proxy_send_timeout:配置Nginx向后端服务器组发出write请求后,等待相应的超时时间。

  5. proxy_redirect:用于修改后端服务器返回的响应头中的Location和Refresh。

跨域 CORS 配置

跨域的定义

同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。通常不允许不同源间的读操作。

同源的定义

如果两个页面的协议,端口(如果有指定)和域名都相同,则两个页面具有相同的源。

img

使用反向代理解决跨域

在前端服务地址为 abc.lvxueyang.vip 的页面请求 def.lvxueyang.vip 的后端服务导致的跨域,可以这样配置:

server {
  listen 9001;
  server_name abc.lvxueyang.vip;

  location / {
    proxy_pass def.lvxueyang.vip;
  }
}

这样就将对前一个域名 abc.lvxueyang.vip的请求全都代理到了 def.lvxueyang.vip,前端的请求都被我们用服务器代理到了后端地址下,绕过了跨域。

这里对静态文件的请求和后端服务的请求都以 abc.lvxueyang.vip开始,不易区分,所以为了实现对后端服务请求的统一转发,通常我们会约定对后端服务的请求加上 /apis/ 前缀或者其他的 path 来和对静态资源的请求加以区分,此时我们可以这样配置:

# 请求跨域,约定代理后端服务请求path以/apis/开头
location ^~/apis/ {
    # 这里重写了请求,将正则匹配中的第一个分组的path拼接到真正的请求后面,并用break停止后续匹配
    rewrite ^/apis/(.*)$ /$1 break;
    proxy_pass def.lvxueyang.vip;
  
    # 两个域名之间cookie的传递与回写
    proxy_cookie_domain abc.lvxueyang.vip def.lvxueyang.vip;
}

这样,静态资源我们使用 abc.lvxueyang.vip/xx.html,动态资源我们使用 abc.lvxueyang.vip/apis/getAwo,浏览器页面看起来仍然访问的前端服务器,绕过了浏览器的同源策略,毕竟我们看起来并没有跨域。

配置 header 解决跨域

当浏览器在访问跨源的服务器时,也可以在跨域的服务器上直接设置 Nginx,从而前端就可以无感地开发,不用把实际上访问后端的地址改成前端服务的地址,这样可适性更高。

比如前端站点是 abc.lvxueyang.vip,这个地址下的前端页面请求def.lvxueyang.vip下的资源,比如前者的 abc.lvxueyang.vip/index.html 内容是这样的:

<html>
<body>
    <h1>welcome fe.sherlocked93.club!!<h1>
    <script type='text/javascript'>
    var xmlhttp = new XMLHttpRequest()
    xmlhttp.open("GET", "http://def.lvxueyang.vip/index.html", true);
    xmlhttp.send();
    </script>
</body>
</html>

很明显这里是跨域请求,在浏览器中直接访问 http://abc.lvxueyang.vip/index.html 是可以访问到的,但是在 def.lvxueyang.vip 的 html 页面访问就会出现跨域。

在 /etc/nginx/conf.d/ 文件夹中新建一个配置文件,对应二级域名 def.lvxueyang.vip :

# /etc/nginx/conf.d/def.lvxueyang.vip.conf

server {
  listen       80;
  server_name  def.lvxueyang.vip;
  
	add_header 'Access-Control-Allow-Origin' $http_origin;   # 全局变量获得当前请求origin,带cookie的请求不支持*
	add_header 'Access-Control-Allow-Credentials' 'true';    # 为 true 可带上 cookie
	add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';  # 允许请求方法
	add_header 'Access-Control-Allow-Headers' $http_access_control_request_headers;  # 允许请求的 header,可以为 *
	add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
	
  if ($request_method = 'OPTIONS') {
		add_header 'Access-Control-Max-Age' 1728000;   # OPTIONS 请求的有效期,在有效期内不用发出另一条预检请求
		add_header 'Content-Type' 'text/plain; charset=utf-8';
		add_header 'Content-Length' 0;
    
		return 204;                  # 200 也可以
	}
  
	location / {
		root  /usr/share/nginx/html/def;
		index index.html;
	}
}

然后 nginx -s reload 重新加载配置。这时再访问 fe.sherlocked93.club/index.html 结果如下,请求中出现了我们刚刚配置的 Header。

地址重写

有的时候我们的网站更换了域名,但还有用户在使用老的域名访问,这时可以通过nginx的地址重写来让用户跳转到新的域名进行访问。

比如说原来我们用的www.lvxueyang.com这个域名不用了,现在改成www.lvxueyang.vip了来访问文档项目了;

server {
    listen       80;
    server_name  www.lvxueyang.com; 
    
    rewrite "^/(.*)$" http://www.lvxueyang.vip/$1; #地址重写到新地址

    location / {
        root   /usr/share/nginx/html/docs; 
        index  index.html index.htm;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

}

此时访问旧域名www.lvxueyang.com会直接跳转到www.lvxueyang.vip去。

适配PC或移动设备

现在很多网站都是有了PC端和H5站点的,因为这样就可以根据客户设备的不同,显示出体验更好的,不同的页面了。

这样的需求有人说拿自适应就可以搞定,比如我们常说的bootstrap和24格布局法,这些确实是非常好的方案,但是无论是复杂性和易用性上面还是不如分开编写的好,比如我们常见的淘宝、京东……这些大型网站就都没有采用自适应,而是用分开制作的方式。

$http_user_agent的使用:

Nginx通过内置变量$http_user_agent,可以获取到请求客户端的userAgent,就可以用户目前处于移动端还是PC端,进而展示不同的页面给用户。

server{
        listen 80;
        server_name nginx2.jspang.com;
        location / {
         root /usr/share/nginx/pc;
         if ($http_user_agent ~* '(Android|webOS|iPhone|iPod|BlackBerry)') {
            root /usr/share/nginx/mobile;
         }
         index index.html;
        }
}

配置高可用集群(双机热备)

当主 Nginx 服务器宕机之后,切换到备份 Nginx 服务器

img

首先安装 keepalived,

yum install keepalived -y

然后编辑 /etc/keepalived/keepalived.conf 配置文件,并在配置文件中增加 vrrp_script 定义一个外围检测机制,并在 vrrp_instance 中通过定义 track_script 来追踪脚本执行过程,实现节点转移:

global_defs{
   notification_email {
        acassen@firewall.loc
   }
   notification_email_from Alexandre@firewall.loc
   smtp_server 127.0.0.1
   smtp_connect_timeout 30 // 上面都是邮件配置,没卵用
   router_id LVS_DEVEL     // 当前服务器名字,用hostname命令来查看
}
vrrp_script chk_maintainace { // 检测机制的脚本名称为chk_maintainace
    script "[[ -e/etc/keepalived/down ]] && exit 1 || exit 0" // 可以是脚本路径或脚本命令
    // script "/etc/keepalived/nginx_check.sh"    // 比如这样的脚本路径
    interval 2  // 每隔2秒检测一次
    weight -20  // 当脚本执行成立,那么把当前服务器优先级改为-20
}
vrrp_instanceVI_1 {   // 每一个vrrp_instance就是定义一个虚拟路由器
    state MASTER      // 主机为MASTER,备用机为BACKUP
    interface eth0    // 网卡名字,可以从ifconfig中查找
    virtual_router_id 51 // 虚拟路由的id号,一般小于255,主备机id需要一样
    priority 100      // 优先级,master的优先级比backup的大
    advert_int 1      // 默认心跳间隔
    authentication {  // 认证机制
        auth_type PASS
        auth_pass 1111   // 密码
    }
    virtual_ipaddress {  // 虚拟地址vip
       172.16.2.8
    }
}

其中检测脚本 nginx_check.sh,这里提供一个:

#!/bin/bash
A=`ps -C nginx --no-header | wc -l`
if [ $A -eq 0 ];then
    /usr/sbin/nginx # 尝试重新启动nginx
    sleep 2         # 睡眠2秒
    if [ `ps -C nginx --no-header | wc -l` -eq 0 ];then
        killall keepalived # 启动失败,将keepalived服务杀死。将vip漂移到其它备份节点
    fi
fi

复制一份到备份服务器,备份 Nginx 的配置要将 state 后改为 BACKUP,priority 改为比主机小。

设置完毕后各自 service keepalived start 启动,经过访问成功之后,可以把 Master 机的 keepalived 停掉,此时 Master 机就不再是主机了 service keepalived stop,看访问虚拟 IP 时是否能够自动切换到备机 ip addr。

再次启动 Master 的 keepalived,此时 vip 又变到了主机上。

配置 HTTPS

下载证书的压缩文件,里面有个 nginx 文件夹,把 xxx.crt 和 xxx.key 文件拷贝到服务器目录,再配置下:

server {
  listen 443 ssl http2 default_server;   # SSL 访问端口号为 443
  server_name lvxueyang.vip;         # 填写绑定证书的域名

  ssl_certificate /etc/nginx/https/1_lvxueyang.vip_bundle.crt;   # 证书文件地址
  ssl_certificate_key /etc/nginx/https/2_lvxueyang.vip.key;      # 私钥文件地址
  ssl_session_timeout 10m;

  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;      #请按照以下协议配置
  ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; 
  ssl_prefer_server_ciphers on;
  
  location / {
    root         /usr/share/nginx/html;
    index        index.html index.htm;
  }
}

写完 nginx -t -q 校验一下,没问题就 nginx -s reload,现在去访问 https://lvxueyang.vip/ 就能访问 HTTPS 版的网站了。

一般还可以加上几个增强安全性的命令:

add_header X-Frame-Options DENY;           # 减少点击劫持
add_header X-Content-Type-Options nosniff; # 禁止服务器自动解析资源类型
add_header X-Xss-Protection 1;             # 防XSS攻击

静态服务

server {
  listen       80;
  server_name  static.lvxueyang.vip;
  charset utf-8;    # 防止中文文件名乱码

  location /download {
    alias	          /usr/share/nginx/html/static;  # 静态资源目录
    
    autoindex               on;    # 开启静态资源列目录
    autoindex_exact_size    off;   # on(默认)显示文件的确切大小,单位是byte;off显示文件大概大小,单位KB、MB、GB
    autoindex_localtime     off;   # off(默认)时显示的文件时间为GMT时间;on显示的文件时间为服务器时间
  }
}

图片防盗链

server {
  listen       80;        
  server_name  *.lvxueyang.vip;
  
  # 图片防盗链
  location ~* \.(gif|jpg|jpeg|png|bmp|swf)$ {
    valid_referers none blocked server_names ~\.google\. ~\.baidu\. *.qq.com;  # 只允许本机 IP 外链引用,感谢 @木法传 的提醒,将百度和谷歌也加入白名单
    if ($invalid_referer){
      return 403;
    }
  }
}

请求过滤

# 非指定请求全返回 403
if ( $request_method !~ ^(GET|POST|HEAD)$ ) {
  return 403;
}

location / {
  # IP访问限制(只允许IP是 192.168.0.2 机器访问)
  allow 192.168.0.2;
  deny all;
  # 限制用户通过某一个连接向Nginx服务器发起请求的次数
  keepalive_requests 5;
  root   html;
  index  index.html index.htm;
}

单页面项目 history 路由配置

server {
  listen       80;
  server_name  lvxueyang.vip;
  
  location / {
    root       /usr/share/nginx/html/dist;  # vue 打包后的文件夹
    index      index.html index.htm;
    try_files  $uri $uri/ /index.html @rewrites;  
    
    expires -1;                          # 首页一般没有强制缓存
    add_header Cache-Control no-cache;
  }
  
  
  location @rewrites {
    rewrite ^(.+)$ /index.html break;
  }
}

HTTP 请求转发到 HTTPS

配置完 HTTPS 后,浏览器还是可以访问 HTTP 的地址 http://www.lvxueyang.vip/ 的,可以做一个 301 跳转,把对应域名的 HTTP 请求重定向到 HTTPS 上

server {
    listen      80;
    server_name www.lvxueyang.vip;

    # 单域名重定向
    if ($host = 'lvxueyang.vip'){
        return 301 https://www.lvxueyang.vip;
    }
    # 全局非 https 协议时重定向
    if ($scheme != 'https') {
        return 301 https://$server_name$request_uri;
    }

    # 或者全部重定向
    return 301 https://$server_name$request_uri;

    # 以上配置选择自己需要的即可,不用全部加
}

泛域名路径分离

这是一个非常实用的技能,经常有时候我们可能需要配置一些二级或者三级域名,希望通过 Nginx 自动指向对应目录,比如:

  1. test1.lvxueyang.vip 自动指向 /usr/share/nginx/html/doc/test1 服务器地址;
  2. test2.lvxueyang.vip 自动指向 /usr/share/nginx/html/doc/test2 服务器地址;
server {
    listen       80;
    server_name  ~^([\w-]+)\.lvxueyang\.vip$;

    root /usr/share/nginx/html/doc/$1;
}

泛域名转发

和之前的功能类似,有时候我们希望把二级或者三级域名链接重写到我们希望的路径,让后端就可以根据路由解析不同的规则:

  • test1.lvxueyang.vip/api?name=a 自动转发到 127.0.0.1:8080/test1/api?name=a ;
  • test2.lvxueyang.vip/api?name=a 自动转发到 127.0.0.1:8080/test2/api?name=a ;
server {
    listen       80;
    server_name ~^([\w-]+)\.lvxueyang\.vip$;

    location / {
        proxy_set_header        X-Real-IP $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header        Host $http_host;
        proxy_set_header        X-NginX-Proxy true;
        proxy_pass              http://127.0.0.1:8080/$1$request_uri;
    }
}

Nginx缓存

为了提升网站的整体性能,我们一般会采用缓存,从宏观层面来说,会采用浏览器缓存和后端缓存,Nginx处于Web 网站的服务最外层,而且支持浏览器缓存配置和后端数据缓存,用它来做部分数据缓存,效率更高。 Web缓存是可以自动保存常见文档副本的HTTP 设备。当Web请求抵达缓存时,如果本地有“已缓存的”副本,就可以 从本地设备而不是服务器中提取这个文 档。

OpenRestry安装

OpenResty® 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及 大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、 Web 服务和动态网关。 OpenResty 通过lua脚本扩展 nginx 功能,可提供负载均衡、请求路由、安全认证、服务鉴权、流量控制与日志监 控等服务。 OpenResty® 通过汇聚各种设计精良的 Nginx 模块(主要由 OpenResty 团队自主开发),从而将 Nginx 有效地 变成一个强大的通用 Web 应用平台。这样, Web 开发人员和系统工程师可以使用 Lua 脚本语言调动 Nginx 支持 的各种 C 以及 Lua 模块,快速构造出足以胜任 10K 乃至 1000K 以上单机并发连接的高性能 Web 应用系统。

安装依赖库

yum install wget libtermcap-devel ncurses-devel libevent-devel readline-devel pcre-devel gcc
openssl openssl-devel per

下载安装包

wget https://openresty.org/download/openresty-1.11.2.5.tar.gz

解压安装

tar -xf openresty-1.11.2.5.tar.gz
cd openresty-1.11.2.5
./configure --prefix=/usr/local/openresty --with-luajit --without-http_redis2_module --withhttp_stub_status_module --with-http_v2_module --with-http_gzip_static_module --withhttp_sub_module --add-module=/usr/local/server/ngx_cache_purge-2.3/
make && make install

安装完成后,在 /usr/local/openrestry/nginx 目录下是安装好的nginx。

浏览器缓存

客户端侧缓存一般指的是浏览器缓存、app缓存等等,目的就是加速各种静态资源的访问,降低服务器压力。 我们通过配置Nginx设置网页缓存信息,从而降低用户对服务器频繁访问造成的巨大压力。我们先配置一个案例,再 基于案例去讲解Nginx缓存。

Nginx Web缓存配置

nginx 提供了 expires 、 etag 、 if-modified-since 指令来进行浏览器缓存控制。我们使用 expires 来配置 Nginx对网页的缓存。

语法: expires [modified] time;
默认值: expires off;
上下文: http, server, location, if in location

1)上传html

将1.html上传到服务器的 /usr/local/server/html 目录下。

2)配置nginx 修改 /usr/local/openrestry/nginx/conf/nginx.conf 文件,配置如下:

server {
  listen 80;
  server_name localhost;
  location / {
    #静态文件路径
    root /usr/local/server/html;
    #缓存10秒
    expires 10s;
  }
}

第一次请求 http://192.168.211.141/1.html

img

第二次请求 http://192.168.211.141/1.html

img

Http缓存控制头

img
参数说明:

HTTP 中最基本的缓存机制,涉及到的 HTTP 头字段,包括 Cache-Control, Last-Modified, 
If-ModifiedSince, Etag,If-None-Match 等。

Last-Modified/If-Modified-Since
Etag是服务端的一个资源的标识,在 HTTP 响应头中将其传送到客户端。所谓的服务端资源可以是一个Web页面,也可以是
JSON或XML等。服务器单独负责判断记号是什么及其含义,并在HTTP响应头中将其传送到客户端。比如,浏览器第一次请求一
个资源的时候,服务端给予返回,并且返回了ETag: "50b1c1d4f775c61:df3" 这样的字样给浏览器,当浏览器再次请求这
个资源的时候,浏览器会将If-None-Match: W/"50b1c1d4f775c61:df3" 传输给服务端,服务端拿到该ETAG,对比资源
是否发生变化,如果资源未发生改变,则返回304HTTP状态码,不返回具体的资源。

Last-Modified :标示这个响应资源的最后修改时间。web服务器在响应请求时,告诉浏览器资源的最后修改时间。
If-Modified-Since :当资源过期时(使用Cache-Control标识的max-age),发现资源具有 Last-Modified 声明,
则再次向web服务器请求时带上头。

If-Modified-Since ,表示请求时间。web服务器收到请求后发现有头 If-Modified-Since 则与被请求资源的最后修
改时间进行比对。若最后修改时间较新,说明资源有被改动过,则响应整片资源内容(写在响应消息包体内),HTTP 200;若
最后修改时间较旧,说明资源无新修改,则响应 HTTP 304 (无需包体,节省浏览),告知浏览器继续使用所保存的 cache
。

Pragma行是为了兼容 HTTP1.0 ,作用与 Cache-Control: no-cache 是一样的
Etag/If-None-Match
Etag :web服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识(生成规则由服务器决定),如果给定URL中的资源修
改,则一定要生成新的Etag值。

If-None-Match :当资源过期时(使用Cache-Control标识的max-age),发现资源具有Etage声明,则再次向web服务
器请求时带上头 If-None-Match (Etag的值)。web服务器收到请求后发现有头 If-None-Match 则与被请求资源的相
应校验串进行比对,决定返回200或304。

Etag:
Last-Modified 标注的最后修改只能精确到秒级,如果某些文件在1秒钟以内,被修改多次的话,它将不能准确标注文件的
修改时间,如果某些文件会被定期生成,当有时内容并没有任何变化,但 Last-Modified 却改变了,导致文件没法使用缓
存有可能存在服务器没有准确获取文件修改时间,或者与代理服务器时间不一致等情形 Etag是服务器自动生成或者由开发者
生成的对应资源在服务器端的唯一标识符,能够更加准确的控制缓存。 Last-Modified 与 ETag 是可以一起使用的,服务
器会优先验证 ETag ,一致的情况下,才会继续比对 Last-Modified ,最后才决定是否返回304。

代理缓存

用户如果请求获取的数据不是需要后端服务器处理返回,如果我们需要对数据做缓存来提高服务器的处理能力,我们
可以按照如下步骤实现:
1、请求Nginx,Nginx将请求路由给后端服务
2、后端服务查询Redis或者MySQL,再将返回结果给Nginx
3、Nginx将结果存入到Nginx缓存,并将结果返回给用户
4、用户下次执行同样请求,直接在Nginx中获取缓存数据

proxy_cache

proxy_cache 是用于 proxy 模式(一般也可称为反代)的缓存功能,proxy_cache 在 Nginx 配置的 http 段、server段(location 段)中分别写入不同的配置。http 段中的配置用于定义 proxy_cache 空间,server 段中的配置用于调用 http 段中的定义,启用对 server 的缓存功能。
属性使用说明
proxy_cache_path:

proxy_cache_path /usr/local/openresty/nginx/cache levels=1:2
keys_zone=openresty_cache:10m max_size=10g inactive=60m use_temp_path=off;
【作用】指定缓存存储的路径,缓存存储在/usr/local/openresty/nginx/cache目录
【levels=1:2】设置一个两级目录层次结构存储缓存,在单个目录中包含大量文件会降低文件访问速度,因此我们建议对大
多数部署使用两级目录层次结构。如果 levels 未包含该参数,Nginx 会将所有文件放在同一目录中。
【keys_zone=openresty_cache:10m】设置共享内存区域,用于存储缓存键和元数据,例如使用计时器。拥有内存中的密
钥副本,Nginx 可以快速确定请求是否是一个 HIT 或 MISS 不必转到磁盘,从而大大加快了检查速度。1 MB 区域可以存
储大约 8,000 个密钥的数据,因此示例中配置的 10 MB 区域可以存储大约 80,000 个密钥的数据。
【max_size=10g】设置缓存大小的上限。它是可选的; 不指定值允许缓存增长以使用所有可用磁盘空间。当缓存大小达到限
制时,一个称为缓存管理器的进程将删除最近最少使用的缓存,将大小恢复到限制之下的文件。
【inactive=60m】指定项目在未被访问的情况下可以保留在缓存中的时间长度。在此示例中,缓存管理器进程会自动从缓存
中删除 60 分钟未请求的文件,无论其是否已过期。默认值为 10 分钟(10m)。非活动内容与过期内容不同。Nginx 不会
自动删除缓存 header 定义为已过期内容(例如 Cache-Control:max-age=120)。过期(陈旧)内容仅在指定时间内未
被访问时被删除。访问过期内容时,Nginx 会从原始服务器刷新它并重置 inactive 计时器。
【use_temp_path=off】表示NGINX会将临时文件保存在缓存数据的同一目录中。这是为了避免在更新缓存时,磁盘之间互
相复制响应数据,我们一般关闭该功能。

proxy_cache:

设置是否开启对后端响应的缓存,如果开启的话,参数值就是zone的名称,比如:proxy_cache openresty_cache;
proxy_cache_valid:

对不同的response code设定不同的缓存时间,如果不设置code,默认为200,301,302,也可以用any指定所有code
【proxy_cache_valid 200 304 10s;】所有200/304响应的数据都缓存10秒。
【proxy_cache_valid any 1m;】所有请求响应的值都缓存1分钟
proxy_cache_min_uses:
指定在多少次请求之后才缓存响应内容,这里表示将缓存内容写入到磁盘。
【proxy_cache_min_uses 3;】同一个请求达到了3次,才将缓存写入磁盘。
proxy_cache_lock:
默认不开启,开启的话则每次只能有一个请求更新相同的缓存,其他请求要么等待缓存有数据要么限时等待锁释放;nginx1.1.12才开始有。
proxy_cache_key:
缓存文件的唯一key,可以根据它实现对缓存文件的清理操作

缓存操作

我们在 nginx.conf 中添加如下配置:

#缓存配置
proxy_cache_path /usr/local/openresty/nginx/cache levels=1:2 keys_zone=openresty_cache:10m
max_size=10g inactive=60m use_temp_path=off;
server {
	listen 80;
	server_name localhost;
	#html配置
	location ~ \.html {
	# 静态文件路径
	root /usr/local/server/html;
	#缓存10秒
	expires 10s;
} 
  #非html配置
location / {
  #启用缓存openresty_cache
  proxy_cache openresty_cache;
  #针对指定请求缓存
  #proxy_cache_methods GET;
  #设置指定请求会缓存
  proxy_cache_valid 200 304 10s;
  #最少请求1次才会缓存
  proxy_cache_min_uses 3;
  #如果并发请求,只有第1个请求会去服务器获取数据
  #proxy_cache_lock on;
      #唯一的key
  proxy_cache_key $host$uri$is_args$args;
  proxy_pass http://myip:18081;
	}
}

此时 /usr/local/openresty/nginx/cache 目录下只有1个temp文件夹。
我们执行3次请求 http://192.168.211.141/user/wangwu,可以发现此时多了一些其他目录,这些目录就是存
储每个请求对应的缓存。
img

Nginx底层进程机制剖析

Nginx启动后,以daemon多进程⽅式在后台运⾏,包括⼀个Master进程和多个Worker进程, Master
进程是领导,是⽼⼤, Worker进程是⼲活的⼩弟。
img

master进程
主要是管理worker进程,⽐如:
接收外界信号向各worker进程发送信号(./nginx -s reload)
监控worker进程的运⾏状态,当worker进程异常退出后Master进程会⾃动重新启动新的
worker进程等
worker进程
worker进程具体处理⽹络请求。多个worker进程之间是对等的,他们同等竞争来⾃客户端的请
求, 各进程互相之间是独⽴的。⼀个请求,只可能在⼀个worker进程中处理,⼀个worker进程,
不可能处理其它进程的请求。 worker进程的个数是可以设置的,⼀般设置与机器cpu核数⼀致。

Nginx进程模型示意图如下
img

以 ./nginx -s reload 来说明nginx信号处理这部分
1) master进程对配置⽂件进⾏语法检查
2)尝试配置(⽐如修改了监听端⼝,那就尝试分配新的监听端⼝)
3)尝试成功则使⽤新的配置,新建worker进程
4)新建成功,给旧的worker进程发送关闭消息
5)旧的worker进程收到信号会继续服务,直到把当前进程接收到的请求处理完毕后关闭
所以reload之后worker进程pid是发⽣了变化的

img

worker进程处理请求部分的说明
例如,我们监听9003端⼝,⼀个请求到来时,如果有多个worker进程,那么每个worker进程都有
可能处理这个链接。

  • master进程创建之后,会建⽴好需要监听的的socket,然后从master进程再fork出多个
    worker进程。所以,所有worker进程的监听描述符listenfd在新连接到来时都变得可读。
  • nginx使⽤互斥锁来保证只有⼀个workder进程能够处理请求,拿到互斥锁的那个进程注册
    listenfd读事件,在读事件⾥调⽤accept接受该连接,然后解析、处理、返回客户端
    nginx多进程模型好处

  • 每个worker进程都是独⽴的,不需要加锁,节省开销

  • 每个worker进程都是独⽴的,互不影响,⼀个异常结束,其他的照样能提供服务

  • 多进程模型为reload热部署机制提供了⽀撑

惊群效应

Nginx为了充分发挥性能,使用了多个worker子进程监听相同端口的设计,这样多个子进程在accept建立新连接时会有争抢,这会带来著名的“惊群”问题,子进程数量越多越明显,这会造成系统性能的下降。

nginx 通过加锁ngx_accept_mutex 解决了惊群效应。

限流

  • Nginx限流就是限制用户请求速度,防止服务器受不了
  • 限流有3种
  1. 正常限制访问频率(正常流量)
  2. 突发限制访问频率(突发流量)

  3. 限制并发连接数

  • Nginx的限流都是基于漏桶流算法,底下会说道什么是桶铜流

实现三种限流算法

正常限制访问频率(正常流量):
  • 限制一个用户发送的请求,我Nginx多久接收一个请求。
  • Nginx中使用ngx_http_limit_req_module模块来限制的访问频率,限制的原理实质是基于漏桶算法原理来实现的。在nginx.conf配置文件中可以使用limit_req_zone命令及limit_req命令限制单个IP的请求处理频率。
#定义限流维度,一个用户一分钟一个请求进来,多余的全部漏掉
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/m;

#绑定限流维度
server{
	
	location/seckill.html{
		limit_req zone=zone;	
		proxy_pass http://lj_seckill;
	}

}
  • 1r/s代表1秒一个请求,1r/m一分钟接收一个请求, 如果Nginx这时还有别人的请求没有处理完,Nginx就会拒绝处理该用户请求。
突发限制访问频率(突发流量):
  • 限制一个用户发送的请求,我Nginx多久接收一个。
  • 上面的配置一定程度可以限制访问频率,但是也存在着一个问题:如果突发流量超出请求被拒绝处理,无法处理活动时候的突发流量,这时候应该如何进一步处理呢?Nginx提供burst参数结合nodelay参数可以解决流量突发的问题,可以设置能处理的超过设置的请求数外能额外处理的请求数。我们可以将之前的例子添加burst参数以及nodelay参数:
#定义限流维度,一个用户一分钟一个请求进来,多余的全部漏掉
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/m;

#绑定限流维度
server{
	
	location/seckill.html{
		limit_req zone=zone burst=5 nodelay;
		proxy_pass http://lj_seckill;
	}

}
  • 为什么就多了一个 burst=5 nodelay; 呢,多了这个可以代表Nginx对于一个用户的请求会立即处理前五个,多余的就慢慢来落,没有其他用户的请求我就处理你的,有其他的请求的话我Nginx就漏掉不接受你的请求
限制并发连接数
  • Nginx中的ngx_http_limit_conn_module模块提供了限制并发连接数的功能,可以使用limit_conn_zone指令以及limit_conn执行进行配置。接下来我们可以通过一个简单的例子来看下:

上面配置了单个IP同时并发连接数最多只能10个连接,并且设置了整个虚拟服务器同时最大并发数最多只能100个链接。当然,只有当请求的header被服务器处理后,虚拟服务器的连接数才会计数。刚才有提到过Nginx是基于漏桶算法原理实现的,实际上限流一般都是基于漏桶算法和令牌桶算法实现的。