推荐阅读

  1. SpringBoot 整合篇

  2. 手写一套迷你版HTTP服务器

  3. 记住:永远不要在MySQL中使用UTF-8

  4. Springboot启动原理解析

最近花了一些时间来总结nginx常用的技能知识点,通过一些常用的实际案例来将nginx的众多小知识点串联起来。

首先是进入nginx目录进行脚本启动,准备初始化环境:


[root@idea-centos nginx]# cd ./sbin/
[root@idea-centos sbin]# ll
total 3528
-rwxr-xr-x. 1 root root 3611160 Dec 26 16:43 nginx
[root@idea-centos sbin]# ps -ef|grep nginx
root       7270   7194  0 10:42 pts/1    00:00:00 grep --color=auto nginx
[root@idea-centos sbin]# ./nginx 
[root@idea-centos sbin]# ps -ef|grep nginx
root       7272      1  0 10:42 ?        00:00:00 nginx: master process ./nginx
nobody     7273   7272  0 10:42 ?        00:00:00 nginx: master process ./nginx
root       7275   7194  0 10:42 pts/1    00:00:00 grep --color=auto nginx

启动成功之后,通过curl可以进行访问测试

首先通过 ip add来查看地址信息(本人使用的是虚拟机):

[root@idea-centos sbin]# ip add
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:0c:29:ea:63:63 brd ff:ff:ff:ff:ff:ff
    inet 192.168.43.235/24 brd 192.168.43.255 scope global noprefixroute dynamic ens33
       valid_lft 2922sec preferred_lft 2922sec
    inet6 2409:8955:3048:50d5:e7e8:636d:bdf1:7033/64 scope global noprefixroute dynamic 
       valid_lft 2964sec preferred_lft 2964sec
    inet6 fe80::8a65:608b:505e:fe4/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever
			 

然后通过curl在本虚拟机上边进行测试:


[root@idea-centos sbin]# curl http://192.168.43.235/
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
Welcome to nginx!
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a rel="nofollow" href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a rel="nofollow" href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

如果访问失败,请先查看防火墙是否开启,如果开启了,可以先进行关闭,方便展开后续的demo练习:

[root@idea-centos sbin]# firewall-cmd --state
running
[root@idea-centos sbin]# systemctl stop firewalld.service
[root@idea-centos sbin]# firewall-cmd --state
not running

访问成功之后,会看到nginx的首页:

nginx常用的基础命令总结


#默认方式启动:
./sbin/nginx 

#指定配置文件启动 
./sbing/nginx -c /tmp/nginx.conf 

#指定nginx程序目录启动
./sbin/nginx -p /usr/local/nginx/

#快速停止
./sbin/nginx -s stop

#优雅停止 (会等当前的请求先处理完再杀死)
./sbin/nginx -s quit

# 热装载配置文件 
./sbin/nginx -s reload
# 重新打开日志文件
./sbin/nginx -s reopen

# 检测配置是否正确
./sbin/nginx -t

启动nginx之后,可以通过日志来查看进程的相关信息


[root@idea-centos nginx]# ./sbin/nginx
[root@idea-centos nginx]# ps -ef|grep nginx
root      11059      1  0 16:34 ?        00:00:00 nginx: master process ./sbin/nginx
nobody    11060  11059  0 16:34 ?        00:00:00 nginx: worker process
root      11062   8193  0 16:34 pts/1    00:00:00 grep --color=auto nginx

master process 主要是负责日志的更新,热装载 主进程 worker process 工作进程 处理客户端的连接,处理请求

nginx的日志

在nginx的log文件夹底下,我们在重新访问服务器之后,相应的access日志就会增加新的属性内容。

这里面的日志寻找,实际上是根据对象句柄来进行查找指定的文件。

关于nginx处理机制的理解 nginx里的高性能主要归咎于其相应特有的io多路复用。每当有请求发送过来的时候,请求的都会被放入到一个select队列里面,专门存储客户端的http链接信息,然后通过一个专门的选择器来处理轮询,监听客户端是否有发送请求的数据(一直轮询,直到有数据发送过来的时候才会发生堵塞),一旦当接收到发送的数据时,selector就会做出指定的处理。

当我们启动了nginx之后,会有两类型进程出现,分别是master进程和worker进程,而客户端发送的请求通常只是和master进程进行通信,master进程负责接收这些请求,然后分发给不同的worker进程进行处理。

其实本质上,worker进程是从master进程这边fork出来的,在master的进程里面,需要先建立好被监听的socket之后,再fork出多个worker进程,多个worker进程(多核cpu环境下)会争抢accept_mutex互斥锁,抢锁成功的进程就会注册相应的事件对任务进行处理。这种独立进程处理事件的好处在于避免了原本的加锁开销,同时当进程中出现了bug异常的时候,不会影响其他的进程,降低了风险。

nginx处理高并发的方式又是如何的呢?当采用多个worker来处理请求的时候,每个worker里面都只有一个主线程,但是这样处理并发数非常有限,那当并发数量大的时候,是否又需要创建成千上万的线程来实现请求的处理呢?

nginx的设计高明之处就在于了这一点上了,它的机制有点类似于linux里面的epoll这样的系统调度。处理的响应线程(暂命名为A)只有一个,当请求过来的事件处理完毕的时候,会主动通知A , 不会对堵塞的请求等待操作。这样的设计避免了A主动去轮询和等待事件处理的所带来的事件消耗。

相比于传统的apache服务器,每一个连接,apache就会创建一个进程,每个进程内单线程,apache最多能创建256个进程。对于一个负载相对较高的网站来说,256的进程,也就是256个线程,因为线程处理请求时,是同步阻塞模式,接收请求之后,会一直等待该请求读取程序文件(IO)(同步),执行业务逻辑,返回客户端,所有操作完成之后才能处理下一个请求(阻塞)如果服务器已经达到256的极限,那么接下去的访问就需要排队这也就是为什么某些服务器负载不高的原因了。

小结

每个worker进程都是单线程的模式

每个io链接都是基于异步非堵塞的模型建立的。

worker进程的数目结合服务器的cpu数量来进行设置,可以达到调优的效果 ** nginx里面的基本组成模块**

nginx在处理请求的时候,更多的是会把这些请求组成一条请求链,链上的各个模块都会进行不同功能的处理。例如说对于请求的解压和压缩处理,对于ssl的处理模块等。总的来说,nginx的基本模块我总结为了以下几项:

event module 事件处理机制模块,提供了各种事件的具体处理框架。nginx具体采用何种事件来处理,依赖于操作系统和编译设置项,例如ngx_event_core_module和ngx_epoll_module。

phase handler 主要负责处理客户端请求并产生待响应内容,ngx_http_static_module就是其中之一,该module主要是将nginx请求转往读取一些磁盘上边的数据,然后做出输出响应,例如说:uri 是以 / 结尾,则会交由 index 模块来链接上完整的路径名然后通过内部调用的方式来调用本模块。

output filter 该模块可以对响应的输出内容做出指定的修改,例如对页面的某些特定url进行替换操作。

upstream 这一模块相信大家都很熟悉,该模块主要是做反向代理的工作。

load-balancer 此模块主要是负责负载均衡功能,在集群中选择任意一台服务器作为请求的处理。

nginx的常用配置案例讲解 首先来查看一份最为基础的nginx基本配置:


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;  #标识使用零拷贝


    keepalive_timeout  65;  #标识采用长连接

    server {
        listen       80;
        server_name  localhost;

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


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

    }
}

ps:这份配置里面分成了很多份配置块,每个配置块和大括号之间都需要有空格进行标明,否则会识别失效

配置location的重定向 例如说我们在/usr/local/www/文件夹底下创建了一份html页面,然后需要通过nginx来进行访问,nginx在匹配server的location路径的时候,会先按照全路径,再从左往右,再从右往左匹配的顺序。


    server {
        listen       80;
        server_name  www.idea.com  *.idea.com  idea.*;

        location / {
            root  /usr/local/www/;
        }
    }
		

注意,这里面的root配置是存在继承关系的,location里面的root会继承location外边的root信息。如果location里面有写root,就会按照location里面的root来判断。server外>server内>location内。

配置动静分离 所谓的动静分离实质上是指我们对于nginx配置里面的动态请求和静态文件都做了一定的分离。例如以下的配置信息:

server {
        listen       80;
        server_name  www.idea.com  *.idea.com  idea.*;
        root /usr/local/www;    
    #这里面添加映射static的记录
        location /static {
            root /usr/local/static/;
        }

        location / {
            root  /usr/local/www/;
            index idea.html;
        }
    }

ps:这里面的配置内容,我引入了host文件的修改

192.168.43.235 www.idea.com 
192.168.43.235 test.idea.com 
192.168.43.235 idea.test

图片的实际存储位置是:


/usr/local/static/img/logo.jpg

按照上述的配置来讲,访问的方式是:

http://www.idea.com/static/img/logo.jpg

但是这种情况下,我们通过nginx来访问图片的方式是不会成功的,原因是这个地址会被nginx处理成为:

前往root地址+static的最终地址进行查询:


/usr/local/static/static

为了避免这种情况,通常会用别名alias来进行匹配,具体配置如下:


server {
        listen       80;
        server_name  www.idea.com  *.idea.com  idea.*;
        root /usr/local/www;    
        location /static {
            alias /usr/local/static/;
        }

        location / {
            root  /usr/local/www/;
            index idea.html;
        }
    }

这个时候,我们再去访问 http://www.idea.com/static/img/logo.jpg 就会访问成功了。

同样对于别名我们可以进行更加复杂一些的逻辑操作案例:

假设我们需要访问static底下的文件内容,这个时候不需要携带相应的名称,直接通过访问 http://www.idea.com/static/css/test.css 就可以访问到 /usr/local/www/static/css/ 底下的内容了

那么,假如说有很多种类型的静态文件需要进行映射该如何配置呢?这个时候可以引入正则表达式的配置:

server {
		listen       80;
		server_name  www.idea.com  *.idea.com  idea.*;
		root /usr/local/www;    
		location /static {
				alias /usr/local/static/;
		}

#这里的~是指忽略大小写



        location  ~* \.(gif|png|jpg|css|js) {
            root /usr/local/static/;
        }

        location / {
            root  /usr/local/www/;
            index  idea.html;
        }
    }

加入了这段正则表达式的配置之后(~* .(gif|png|css|js)$) 通过访问该路径: http://www.idea.com/css/test.css 我们就可以访问到/usr/local/static/css/底下的文件内容了。

代理访问机制 nginx还提供了一个非常灵活的代理访问机制,供我们通过代理的方式来进行访问location

通过nginx配置完全匹配代理进行页面跳转:

server {
		listen       80;
		server_name  www.idea.com  *.idea.com  idea.*;
		root /usr/local/www;    
		location /static {
				alias /usr/local/static/;
		}

		location ~* \.(gif|png|jpg|css|js) {
				alias /usr/local/static/;
		}

		location  =/idea-serach {
				proxy_pass  http://www.baidu.com
		}

		location / {
				root  /usr/local/www/;
				index idea.html;
		}
}

通过proxy_pass配置来提供代理的请求转发,当我们访问http://www.idea.com/idea-serach的时候,就会匹配到访问百度的页面了。

小结 nginx对于访问的常见配置支持以下几种:

  1. 通过全路径访问location

  2. 通过关键字static匹配来访问location

  3. 通过正则表达式来访问location

  4. 通过反向代理进行访问location

防盗链匹配 设置某些链接只允许在固定的网站站点进行访问,防止某些特殊的域名进行ip访问之后盗取本网站的资源文件,因此可以设置这个防盗链的功能。具体配置如下:


server {
        listen       80;
        server_name  www.idea.com  *.idea.com  idea.*;
        root /usr/local/www/;   
        location /static {
            alias /usr/local/static/;
        }
        # 防盗链配置
        location ~* \.(gif|png|jpg|css|js) {
            valid_referers none blocked *.idea.com;
            if ($invalid_referer) {
                return 403;
            }
            root /usr/local/static/;

        }

        location =/idea-serach {
            proxy_pass http://www.baidu.com;
        }

        location / {
            root  /usr/local/www/;
}
}

网站黑名单 黑名单的配置比较简单,只需要先创建好黑名单文件,然后在http块里面引入就好了

# 创建黑名单文件
echo 'deny 192.168.0.132;' >> balck.ip
#http 配置块中引入 黑名单文件
include       black.ip;

记得配置成功之后要让nginx reload一下,同时请求的时候查看是否是203,如果是的话说明是缓存请求。

nginx的日志配置 当我们需要对于客户端发送的数据进行一些详细信息的查看时候,需要对nginx进行日志的记录,相应的可选参数有以下几点:

log_format格式变量:

$remote_addr  #记录访问网站的客户端地址
$remote_user  #远程客户端用户名
$time_local  #记录访问时间与时区
$request  #用户的http请求起始行信息
$status  #http状态码,记录请求返回的状态码,例如:200、301、404等
$body_bytes_sent  #服务器发送给客户端的响应body字节数
$http_referer  #记录此次请求是从哪个连接访问过来的,可以根据该参数进行防盗链设置。
$http_user_agent  #记录客户端访问信息,例如:浏览器、手机客户端等
$http_x_forwarded_for  #当前端有代理服务器时,设置web节点记录客户端地址的配置,此参数生效的前提是代理服务器也要进行相关的x_forwarded_for设置

将日志配置放在http块里面即可。

通常我们会将日志的配置放置于http模块当中,例如下边的这组案例:


events {
    worker_connections  1024;
}

http {
    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"';
    access_log  logs/access.log  main;
    sendfile        on;
    keepalive_timeout  65;

    server {
        listen       80;
        server_name  localhost;
        #charset koi8-r;
        #access_log  logs/host.access.log  main;

        location / {
            root   html;
            index  index.html index.htm;
        }
        #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;
        } 
    }
    }
		

ngixn里面的代理配置 正向代理

客户机和服务器之间加入一层proxy代理机器,当发生请求的时候,由代理服务器去请求server端。最常见的正向代理案例有:vpn,局域网里面的上网客户端工具等。

举个栗子来讲:

当我们需要通过路径匹配到 $host: $port /baidu.html的时候,被代理请求百度页面,可以进行以下的配置:


location =/baidu.html {
           proxy_pass http://www.baidu.com;
    }

反向代理

当客户端请求服务端的时候,实际上在服务端的接入层有一个代理机器进行请求的转发,而这一层请求的转发相对于客户端而言是透明的。

反向代理请求内部的服务器,虽然配置相似,但是功能不同,配置案例如下:

location =/baidu.html {
           proxy_pass http:/127.0.0.1:8088;
    }
		

代理相关参数


proxy_pass           # 代理服务
proxy_redirect off;   # 是否允许重定向
proxy_set_header Host $host; # 传 header 参数至后端服务
proxy_set_header X-Forwarded-For $remote_addr; # 设置request header 即客户端IP 地址
proxy_connect_timeout 90; # 连接代理服务超时时间
proxy_send_timeout 90; # 请求发送最大时间
proxy_read_timeout 90;  # 读取最大时间
proxy_buffer_size 4k; 
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k; 
proxy_temp_file_write_size 64k;
ngixn里面的负载均衡

nginx里面有个叫做upstream的模块,专门用于配置负载均衡的内容,upstream里面提供有以下的相关参数:

service 反向服务地址 加端口 weight 权重 max_fails 失败多少次 认为主机已挂掉则,踢出 fail_timeout 移除server之后重新请求的时间 当服务挂了之后,这段时间内重新连接 backup 备用服务 (当服务全部都挂了,那么就会请求这里的服务) max_conns 允许最大连接数 slow_start 当节点恢复,不立即加入,而是等待 slow_start 后加入服务对列。

相应参数的具体配置如下:

  upstream backend {
       server 192.168.43.191:8080 weight=5 fail_timeout=10s;
       server 192.168.43.191:8089 weight=5 fail_timeout=10s;
    }
		
	

ngixn里面默认支持的负载均衡策略是轮询加权重的方式,除此之外,nginx自身还支持额外的多种负载均衡策略:

ll+weight:轮询加权重 (默认) 容易出现失重的情况,例如说某一台机器的访问过慢,容易导致请求堆积。

ip_hash : 基于Hash 计算 ,常用于保持session 一至性 基于hash计算的时候,可以根据ip进行hash计算请求到指定的服务器。 (通常session一致性在分布式中最好的处理手段是将session存储在第三方的存储中心)

首先对ip进行hash计算之后,将该值和服务器个数进行取模运算。

url_hash: 静态资源缓存,节约存储,加快速度 可以根据图片的url请求到指定的服务器,比较好理解。

least_conn : 最小链接数 每次请求都只会请求到最少客户端连接数的那台服务器去。

least_time :最小的响应时间 计算节点平均响应时间,然后取响应最快的那个,分配权重更高

配置案例 通过使用ip进行哈希计算的方式来请求后端服务器

  upstream backend {
       ip_hash;
       server 192.168.43.191:8080 weight=1;
       server 192.168.43.191:8089 weight=8 fail_timeout=10s;
}

  location / {
            #root   html;
            index  index.html index.htm;
            proxy_pass http://backend;
   }
	

当然nginx除了这些常用功能以外,还提供有非常丰富的其他功能配置,具体配置可以参考nginx的官方文档配置信息http://nginx.org/en/docs/