willson-wang / Blog

随笔
https://blog.willson-wang.com/
MIT License
70 stars 10 forks source link

Nginx基础 #81

Open willson-wang opened 4 years ago

willson-wang commented 4 years ago

mac下安装nginx

使用brew安装nginx

brew install nginx

安装完成之后,通过brew list nginx可以查看nginx下载之后被放置目录

/usr/local/Cellar/nginx/1.15.2/.bottle/etc/ (15 files)
/usr/local/Cellar/nginx/1.15.2/bin/nginx
/usr/local/Cellar/nginx/1.15.2/homebrew.mxcl.nginx.plist
/usr/local/Cellar/nginx/1.15.2/html -> ../../../var/www // 注意这里将html目录链接到/usr/local/var/www,这样我们直接将文件放置到www目录即可访问
/usr/local/Cellar/nginx/1.15.2/share/man/man8/nginx.8

同时可以通过brew info nginx查看更详细的信息,如下所示

image

nginx常用操作命令

启动nginx

nginx

查看nginx配置是否正确

nginx -t 

重新加载配置|重启|停止|退出 nginx

nginx -s reload|reopen|stop|quit

分析配置文件

以默认的nginx.conf文件为例

#user  nobody; // user指令,表示以哪个用户运行nginx,nobody是一个权限比较低的用户
worker_processes  1; // 启动进程数,一般设置为cpu的核数,通过

# mac下的brew安装的nginx日志目录/usr/local/var/log/nginx
#error_log  logs/error.log; // 全局错误日志 类型包括debug | info | notice | warn | error | crit 从左至右debug最详细,crit最少
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid; // 记录当前启用nginx的进程id

# 工作模式及连接数上线
events {
    use epoll; # epoll是多路复用io中的一种方式,但是仅用于linux2.6以上内核,可以大大提高nginx的性能
    worker_connections  1024; # 单个后台worker process进程的最大并发链接数
}

# http服务
http {
    # 设定mine类型,类型由mime.type文件定义
    include       mime.types;
    # 默认输出content-type类型,当请求的文件类型在mime.types文件中找不到时,返回default_type定义的默认类型,供浏览器处理
    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;

    # 指令指定 nginx 是否调用 sendfile 函数(zero copy方式)来输出文件,对于普通应用,必须设为on,如果用来进行下载等应用磁盘IO重负载应用,可设置为 off,以平衡磁盘与网络I/O处理速度,降低系统的uptime.
    sendfile        on;
    #tcp_nopush     on;

    # 连接超时时间秒
    #keepalive_timeout  0;
    keepalive_timeout  65;

    # 开启gzip压缩 ,压缩有利于提升网络的传输速度,但非常小的文件 不需要压缩,压缩反而会影响速度
    #gzip  on;

    #设定反向代理服务器列表
    #weigth参数表示权值,权值越高被分配到的几率越大,反向代理服务器列表,表示访问myserver请求,会挑选服务器列表中的一个服务器进行转发请求,从而达到负载均衡分解请求压力效果;如何挑选服务器,有随机规则、权重规则或ip_hash规则等
    upstream myserver {
        server 192.168.1.2 weight=5;
        server 192.168.1.3 weight=1;
        server 192.168.1.4 weight=6;
    }

    # 虚拟主机,可以有多个虚拟主机
    server {
        # 监听8080端口
        listen       8080;

        # 定义使用此服务的访问域名或ip
        server_name  localhost;

        # 设置编码
        #charset koi8-r;

        # 设定本虚拟主机的访问日志
        #access_log  logs/host.access.log  main;

        # 路由规则模块,location表示路由规则,也就是用户请求的url地址,nginx有一套针对url的路由规则
        location / {
            root   html; # 定义服务器的默认网站根目录位置
            index  index.html index.htm; # 定义首页索引文件的名称
            proxy_pass http://myserver ;# 请求转向myserver定义的服务器列表
        }

        #error_page  404              /404.html;

        # 出现500,502,503,504错误 就路由请求 /50x.html
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

一个最简单的nginx.conf,即最少配置信息

events {
    worker_connections 1024;
}

http {
    server {
        server_name: test.com;
        location / {
            root html;
            index index.html index.htm;
        }
    }
}

虚拟主机

虚拟主机使用的是特殊的软硬件技术,它把一台运行在因特网上的服务器主机分成一台台“虚拟”的主机,每台虚拟主机都可以是一个独立的网站,可以具有独立的域名,具有完整的Intemet服务器功能(WWW、FTP、Email等),同一台主机上的虚拟主机之间是完全独立的。从网站访问者来看,每一台虚拟主机和一台独立的主机完全一样。

利用虚拟主机,不用为每个要运行的网站提供一台单独的Nginx服务器或单独运行一组Nginx进程。虚拟主机提供了在同一台服务器、同一组Nginx进程上运行多个网站的功能。

配置虚拟主机:虚拟主机由两部分组件server_name+listen;二者组成必须是唯一的;当server_name一样时,端口号必须不同;当端口号相同时,server_name必须不同;因为只有二者组成是唯一的,这样才可以区分是哪个虚拟主机

端口相同,server_name匹配优先级

完全匹配与通配符匹配对比

server {
    listen 8080;
    server_name *.1;
    location / {
        root /Users/wangks/Documents/f/my-nginx/server1;
        index index.html index.htm;
    }
}

server {
    listen 8080;
    server_name qwer.1;
    location / {
        root /Users/wangks/Documents/f/my-nginx/server2;
        index index.html index.htm;
    }
}
curl qwer.1:8080 输出结果 <div>server2</div>
curl test.1:8080 输出结果 <div>server1</div>

完全匹配比通配符匹配优先级高

通配符前与通配符后比对

server {
    listen 8080;
    server_name qwer.*;
    location / {
        root /Users/wangks/Documents/f/my-nginx/server3;
        index index.html index.htm;
    }
}

server {
    listen 8080;
    server_name *.1;
    location / {
        root /Users/wangks/Documents/f/my-nginx/server1;
        index index.html index.htm;
    }
}
curl qwer.1:8080 <div>server1</div>
curl qwer.2:8080 <div>server3</div>

通配符前优先级比通配符后优先级高

通配符后与正则表达式比对

server {
    listen 8080;
    server_name ~^\w+.1$;
    location / {
        root /Users/wangks/Documents/f/my-nginx/server4;
        index index.html index.htm;
    }
}

server {
    listen 8080;
    server_name qwer.*;
    location / {
        root /Users/wangks/Documents/f/my-nginx/server3;
        index index.html index.htm;
    }
}
curl qwer.1:8080 <div>server3</div>
curl test.1:8080 <div>server4</div>

通配符后优先级比正则表达式优化级高

server {
    listen 8080;
    server_name tty.com;
    location / {
        root /Users/wangks/Documents/f/my-nginx/server5;
        index index.html index.htm;
    }
}

server {
    listen 8080 default;
    server_name kfc.com ;
    location / {
        root /Users/wangks/Documents/f/my-nginx/server6;
        index index.html index.htm;
    }
}

server {
    listen 8080;
    server_name ~^\w+.1$;
    location / {
        root /Users/wangks/Documents/f/my-nginx/server4;
        index index.html index.htm;
    }
}
curl test.1:8080 <div>server4</div>
curl test.2:8080 <div>server6</div>

正则匹配优先级高于default,default只能作用于匹配不到的情况下,而且nginx.conf不可以配多个default,否则nginx重启时候会提示错误

server {
    listen 8080;
    server_name tty.com;
    location / {
        root /Users/wangks/Documents/f/my-nginx/server5;
        index index.html index.htm;
    }
}

server {
    listen 8080;
    server_name kfc.com ;
    location / {
        root /Users/wangks/Documents/f/my-nginx/server6;
        index index.html index.htm;
    }
}
curl test1.com:8080 <div>server5</div>

没有default的场景,默认匹配第一个server

端口相同时server_name匹配优先级结论如下:

完全匹配 >> 通配符在前匹配 >> 通配符在后匹配 >> 正则匹配 >> default >> 顺序上的第一个server

location匹配规则

location配置语法:location [=|~|~*|^~] /uri/ { … }

location匹配方式分为普通匹配和正则匹配

用~和~* 为前缀的匹配是正则匹配,用=和^~或没有前缀为普通匹配

因为一个server内支持多个location,所以location匹配规则存在优先级

对比一

server {
    listen 80;
    server_name ttl.com;
    root /Users/wangks/Documents/f/my-nginx/location-test;
    charset utf-8;

    # 普通匹配
    location /api {
        default_type 'text/html';
        return 200 '规则/api/';
    }

    # 全等匹配
    location = /api {
        default_type 'text/html';
        return 200 '规则=/api';
    }
}
curl ttl.com/api 返回结果 规则=/api

对比二

server {
    listen 80;
    server_name ttl.com;
    root /Users/wangks/Documents/f/my-nginx/location-test;
    charset utf-8;

    # 正则匹配
    location ~ ^/api {
        default_type 'text/html';
        return 200 '规则^~ /api';
    }

    # 前缀匹配
    location ^~ /api {
        default_type 'text/html';
        return 200 '规则^~ /api';
    }
}
curl ttl.com/api 返回结果 规则^~ /api

对比三

server {
    listen 80;
    server_name ttl.com;
    root /Users/wangks/Documents/f/my-nginx/location-test;
    charset utf-8;

    # 普通匹配
    location /api {
        default_type 'text/html';
        return 200 '规则 /api';
    }

    # 正则匹配
    location ~ ^/api {
        default_type 'text/html';
        return 200 '规则~ ^/api';
    }
}
curl ttl.com/api 返回结果 规则~ ^/api

对比四

server {
    listen 80;
    server_name ttl.com;
    root /Users/wangks/Documents/f/my-nginx/location-test;
    charset utf-8;

    # 正则匹配区分大小写
    location ~ ^/api {
        default_type 'text/html';
        return 200 '规则~ /api';
    }

    # 正则匹配,不区分大小写
    location ~* ^/api {
        default_type 'text/html';
        return 200 '规则~* /api';
    }
}
curl ttl.com/api 返回结果 规则~ /api

对比五

server {
    listen 80;
    server_name ttl.com;
    root /Users/wangks/Documents/f/my-nginx/location-test;
    charset utf-8;

    # 正则匹配,不区分大小写
    location ~* ^/api {
        default_type 'text/html';
        return 200 '规则~* /api';
    }

    # 正则匹配,区分大小写
    location ~ ^/api {
        default_type 'text/html';
        return 200 '规则~ /api';
    }
}
curl ttl.com/api 返回结果 规则~* /api

对比六

server {
    listen 80;
    server_name ttl.com;
    root /Users/wangks/Documents/f/my-nginx/location-test;
    charset utf-8;

    # 普通匹配
    location /api {
        default_type 'text/html';
        return 200 '规则 /api';
    }

    # 普通匹配
    location /api/b/ {
        default_type 'text/html';
        return 200 '规则/api/b/';
    }
}
curl ttl.com/api/a.html 返回结果 规则 /api
curl ttl.com/api/b/a.html 返回结果 规则/api/b/

从上可知匹配优先级

全等匹配 >> 前缀匹配 >> 正则匹配 >> 普通匹配

正则匹配之间顺序优先

同种匹配之间匹配长度优先

接口转发

场景一 proxy_pass 相对路径

server {
    listen 80;
    server_name ttl.com;
    root /Users/wangks/Documents/f/my-nginx/location-test;
    charset utf-8;

    # 前缀匹配
    location /api {
        default_type 'text/html';
        return 200 '规则 /api';
    }

    # 正则匹配
    location /api/ {
        default_type 'text/html';
        return 200 '规则/api/';
    }

    location / {
        default_type 'text/html';
        return 200 '规则/ $uri $request_uri';
    }
}

server {
    listen 80;
    server_name ttp.com;

    location / {
        proxy_pass http://ttl.com;
    }

    location /api {
        proxy_pass http://ttl.com;
    }

    location /api/ {
        proxy_pass http://ttl.com;
    }
}
curl ttp.com 返回结果 规则/ / /
curl ttp.com/api  返回结果  规则/api /api --- /api
curl ttp.com/api/a  返回结果 规则/api/ /api/a --- /api/a

可以看出来location 匹配的完整路径将直接透传给 proxy_pass 的uri

场景二 proxy_pass 绝对路径

server {
    listen 80;
    server_name ttl.com;
    root /Users/wangks/Documents/f/my-nginx/location-test;
    charset utf-8;

    # 前缀匹配
    location /v1 {
        default_type 'text/html';
        return 200 '规则 /v1 $uri --- $request_uri';
    }

    # 正则匹配
    location /v1/ {
        default_type 'text/html';
        return 200 '规则/v1/ $uri --- $request_uri';
    }
}

server {
    listen 80;
    server_name ttp.com;

    location / {
        if ($uri ~* ^/apq/) {
            proxy_pass http://ttl.com/; 
        } else {
            proxy_pass http://ttl.com/;
        }
    }

    location /api {
        proxy_pass http://ttl.com/;
    }

    location /api/ {
        proxy_pass http://ttl.com/;
    }

    location /abc {
        proxy_pass http://ttl.com/v1;
    }

    location /abc/ {
        proxy_pass http://ttl.com/v1;
    }

    location /apc {
        proxy_pass http://ttl.com/v1/;
    }

    location /apc/ {
        proxy_pass http://ttl.com/v1/;
    }
}
curl ttp.com  返回结果 规则/ / /  => ttl.com/
curl ttp.com/api   返回结果 规则/ / / => ttl.com/
curl ttp.com/api/b   返回结果  规则/ /b /b => ttl.com/b
curl ttp.com/abc   返回结果 规则 /v1 /v1 --- /v1 => ttl.com/v1
curl ttp.com/abc/b  返回结果  规则 /v1 /v1b --- /v1b => ttl.com/v1b
curl ttp.com/apc  返回结果  规则/v1/ /v1/ --- /v1/ => ttl.com/v1/
curl ttp.com/apc/b 返回结果 规则/v1/ /v1/b --- /v1/b => ttl.com/v1/b

场景三 正则表达式

location ~* ^/apq/ {
    proxy_pass http://ttl.com/;
}

nginx: [emerg] "proxy_pass" cannot have URI part in location given by regular expression, or inside named location, or inside "if" statement, or inside "limit_except" block in /usr/local/etc/nginx/nginx.conf:107

不能使用绝对路径

场景四 rewrite 重写

location /name/ {
    rewrite /name/([^/]+) /api?name=$1 break;
    proxy_pass http://ttl.com;
}

location /age/ {
    rewrite /age/([^/]+) /api?age=$1 break;
    proxy_pass http://ttl.com/;
}

location ^~/user/ {
    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_set_header X-NginX-Proxy true;

    rewrite ^/user/(.*)$ /$1 break;
    proxy_pass http://user;
}

location ^~/order/ {
    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_set_header X-NginX-Proxy true;

    rewrite ^/order/(.*)$ /$1 break;
    proxy_pass http://order;
}
curl ttp.com/name/xiaoming 返回结果  规则/api /api --- /api?name=xiaoming
curl ttp.com/age/18  返回结果 规则/api /api --- /api?age=18

rewrite之后相当于相对路径的透传方式

资源缓存

location ~* \.(js|css)$ {
    expires 7d;
    #expires -1;
    #add_header Cache-Control 'public';
    #add_header Cache-Control 'no-cache';
    #add_header Cache-Control 'no-store';
    #add_header Cache-Control 'max-age=86400';
}

设置资源缓存方式

通过expires设置
expires 日期; 表示资源的缓存日期

expires -1; 表示资源不缓存 相当于add_header Cache-Control 'no-cache';

image

通过add_header来添加Cache-Control
add_header Cache-Control 'public';

Cache-Control常用值

public:客户端和代理服务器可以缓存
private:客户端可以缓存
max-age:缓存的内容将在xxx秒后失效,意思就是在客户端收到信息后,信息会缓存xxx秒;过了xxx秒客户端必须重新获取信息
no-store:不缓存请求的任何返回内容,不缓存请求返回的任何内容
no-cache:强制向服务器端再验证一次

image

no-cache和no-store的区别就是,no-cache会缓存请求返回的内容,而no-store不缓存;但no-cache时,在下次用缓存的内容时,需要向服务器验证一下,缓存到底能不能用

private与public的区别是,priviate不允许代理服务器缓存,一般我们的项目会用nginx做代理服务器,所以priviate不允许nginx等这类代理服务器缓存

ETag验证

ETag:资源唯一标识 nginx默认返回etag

一般会把请求的内容做md5加密,返回唯一的标识;会把ETag的值一起返回给浏览器;浏览器会把ETag存储下来。如W/"5ea904c2-30e0b"

下一次请求时将Etag一并带过去给服务器,服务器只需要比较客户端传来的ETag跟自己服务器上该资源的ETag是否一致,就能很好地判断资源相对客户端而言是否被修改过了。

如果服务器发现ETag匹配不上,那么直接以常规GET 200状态码形式将新的资源(当然也包括了新的ETag)发给客户端;如果ETag是一致的,则直接返回304状态码客户端直接使用本地缓存即可。

那么客户端是如何把标记在资源上的ETag传回给服务器的呢?请求报文中有两个首部字段可以带上ETag值:

If-None-Match: ETag-value

If-None-Match: W/"5ea904c2-30e0b",告诉服务端如果ETag没匹配上需要重发资源数据,否则直接回送304和响应报头即可

当前各浏览器均是使用的该请求首部来向服务器传递保存的ETag值。

If-Match: ETag-value

告诉服务器如果没有匹配到ETag,或者收到了*值而当前并没有该资源实体,则应当返回412(Precondition Failed)状态码给客户端。否则服务器直接忽略该字段

ETag优点:

缺点:

Last-Modified验证

服务器将资源传递给客户端时,会将资源最后更改的时间以“Last-Modified: GMT”的形式加在实体首部上一起返回给客户端。

Last-Modified: Wed, 29 Apr 2020 04:38:26 GMT

客户端会为资源标记上该信息,下次再次请求时,会把该信息附带在请求头中一并带给服务器去做检查,若传递的时间值与服务器上该资源最终修改时间是一致的,则说明该资源没有被修改过,直接返回304状态码,内容为空。

如果两个时间不一致,则服务器会发回该资源并返回200状态码,和第一次请求时类似。这样保证不向客户端重复发出资源,也保证当服务器有变化时,客户端能够得到最新的资源

也类似ETag,客户端请求报文头,两种参数

If-Modified-Since: Last-Modified-value

If-Modified-Since: Wed, 29 Apr 2020 04:38:26 GMT

该请求首部告诉服务器如果客户端传来的最后修改时间与服务器上的一致,则直接回送304 和响应报头即可。

当前各浏览器均是使用的该请求参数来向服务器传递保存的 Last-Modified 值。

If-Unmodified-Since: Last-Modified-value

该值告诉服务器,若Last-Modified没有匹配上(资源在服务端的最后更新时间改变了),则应当返回412(Precondition Failed) 状态码给客户端。

Last-Modified 存在一个问题,如果在服务器上,一个资源被修改了,但其实际内容根本没发生改变,但修改时间变化了,会导致Last-Modified时间匹配不上而返回了整个实体给客户端(跟客户端缓存里有个一模一样的资源);就是错误的判断内容改变了。

负载均衡

nginx实现负载均衡原理,用户访问首先访问到nginx服务器,然后nginx服务器再从应用服务器集群中选择压力比较小的服务器,然后将该访问请求引向该服务器。如应用服务器集群中某一台服务器崩溃,那么从待选择服务器列表中将该服务器删除,也就是说一个服务器崩溃了,那么nginx服务器不会把请求引向到该服务器

upstream myrule { #myrule代表此负载均衡的规则名称
    server ttu.com:8080; #服务器1的ip地址以及端口
    server ttu.com:8081; #服务器2的ip地址以及端口
}

server {
    listen 80;
    server_name ttk.com;

    location / {
        proxy_pass http://myrule;
    }
}

server {
    listen 8080;
    server_name ttu.com;
    charset utf-8;

    location / {
        default_type 'text/html';m 
        return 200 'ttu.com:8080 $uri';
    }
}

server {
    listen 8081;
    server_name ttu.com;
    charset utf-8;

    location / {
        default_type 'text/html';
        return 200 'ttu.com:8081 $uri';
    }
}

上面的配置代表,用户访问服务器ttk.com,端口80时,nginx会把请求传发给myrule的负载均衡规则去处理;nginx会随机取一个myrule下的服务器来处理用户请求。

随机选取

curl ttk.com   ttu.com:8081 /
curl ttk.com   ttu.com:8080 /
curl ttk.com   ttu.com:8081 /
curl ttk.com   ttu.com:8080 /

权重选取

upstream myrule {
    server ttu.com:8080 weight=1; 
    server ttu.com:8081 weight=5;
}
curl ttk.com  ttu.com:8081 /
curl ttk.com  ttu.com:8081 /
curl ttk.com  ttu.com:8080 /
curl ttk.com  ttu.com:8081 /
curl ttk.com  ttu.com:8081 /
curl ttk.com  ttu.com:8081 /
curl ttk.com  ttu.com:8080 /

优先访问权重大的服务器

ip_hash选取

upstream myrule {
    ip_hash;
    server ttu.com:8080; 
    server ttu.com:8081;
}
curl ttk.com  ttu.com:8080 / 127.0.0.1
curl ttk.com  ttu.com:8080 / 127.0.0.1
curl ttk.com  ttu.com:8080 / 127.0.0.1
curl ttk.com  ttu.com:8080 / 127.0.0.1
curl ttk.com  ttu.com:8080 / 127.0.0.1
curl ttk.com  ttu.com:8080 / 127.0.0.1

将ip固定访问某个服务器,ip_hash这种方式在很多场景会用到,如:session会话,状态转发服务等业务

gzip压缩

gzip  on;
gzip_min_length 1k;
gzip_http_version 1.1;
gzip_vary on;
gzip_types text/plain application/javascript application/x-javascript text/css application/xml;
gzip_buffers 4 16k;

主要一定要保证返回的content-type在gzip_types值内包含

image

日志切割

在了解nginx日志之前我们需要现了解下nginx一些常用的内置变量

server {
    listen 80;
    server_name ttr.com;
    charset utf-8;

    location / {
        default_type 'text/html';
        return 200 'remote_addr => $remote_addr, time_local => $time_local, request => $request, http_host => $http_host, status => $status, body_bytes_sent => $body_bytes_sent, http_referer => $http_referer, http_user_agent => $http_user_agent, request_time => $request_time, host => $host, request_method  => $request_method, args => $args, content_length => $content_length, http_cookie => $http_cookie, remote_port => $remote_port, server_protocol => $server_protocol, server_addr => $server_addr, server_name => $server_name, server_port => $server_port, scheme => $scheme';
    }
}
key 说明 示例
$remote_addr 客户端ip地址 127.0.0.1
$time_local 访问时间和时区 04/May/2020:14:27:46 +0800
$request http请求首部第一行,包含请求方法、URI及http协议版本 GET /?name=xiaoming HTTP/1.1
$http_host 请求地址,即浏览器中你输入的地址(IP或域名) ttr.com
$status HTTP请求状态 200
$body_bytes_sent 发送给客户端文件内容大小 0
$http_referer url跳转来源
$http_user_agent 浏览器user_agent Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36
$request_time 整个请求的总时间 0.000
$host 请求信息中的 Host,如果请求中没有 Host 行,则等于设置的服务器名,不包含端口 ttr.com
$request_method 客户端请求方法 GET
$args 请求中的参数 name=xiaoming&age=18
$content_length 请求头中的 Content-length 字段
$http_cookie 客户群请求携带cookie a=123; key=aaa
$remote_port 客户端的端口 64966
$server_protocol 请求使用的协议 HTTP/1.1
$server_addr 服务器地址 127.0.0.1
$server_name 服务器名称 ttr.com
$server_port 服务器的端口号 80
$scheme HTTP 方法 http
$http_x_forwarded_for 真实客户端访问ip 127.0.0.1

设置access_log

访问日志主要用于记录客户端的请求。客户端向 nginx 服务器发起的每一次请求都会被记录到 access_log 中

定义日志输出格式
log_format <NAME> <Strin---g>;
关键字      格式标签   日志格式

默认log_format格式
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    <FILE>    <NAME>;
关键字         日志文件   格式标签

默认日志文件

access_log logs/access_log.log main;

设置error_log

error_log file [level];
关键字   日志文件 错误等级

默认值 error_log logs/error.log error;

level 可以是:debug、info、notice、warn、error、crit、alert、emerg 中的任意值。只有日志的错误级别大于等于level 指定的值才会被写入错误日志中,默认值是 error
# nginx内统一的错误日志输出
error_log logs/error.log error;

http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                  '$status $body_bytes_sent "$http_referer" '
                 '"$http_user_agent" "$http_x_forwarded_for"';
    # http内所有虚拟主机统一access_log,使用的日志格式标签是main
    access_log logs/access_log.log main;

    server {
        listen 80;
        server_name ttr.com;

        location / {
            index index.html;
        }

        # 该虚拟主机内单独使用的日志,使用的日志格式标签是main
        access_log logs/access_ttr_log.log main;

        # 该虚拟主机内单独使用的日志,使用的日志的错误等级是error
        error_log logs/error_ttr.log error;
    }
}

定义输出日志文件可以在http内定定义,这个可以记录整个http内的所有server nginx访问日志,也可以定义在单独的某个server内,针对这个server进行单独记录,如果某个server内有进行单独配置,则不会记录到http内的acess_log内

按日期格式输出access_log

if ($time_iso8601 ~ "^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})")
{
    set $year $1;
    set $month $2;
    set $day $3;
    set $hour $4;
    set $minutes $5;
    set $seconds $6;
}

access_log  /logs/access_$year-$month-$day-$hour.log  main;
127.0.0.1 - - [04/May/2020:15:26:08 +0800] "GET / HTTP/1.1" 200 920 "-" "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1" "-"
127.0.0.1 - - [04/May/2020:15:26:09 +0800] "GET /static/css/main.ecedf1df.css HTTP/1.1" 200 36589 "http://ttreact.com/" "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1" "-"
127.0.0.1 - - [04/May/2020:15:26:09 +0800] "GET /static/js/main.5503e124.js HTTP/1.1" 200 133167 "http://ttreact.com/" "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1" "-"

常用场景

部署vue项目、不使用cdn,且publicPath = '/',hash路由,然后把dist目录下的内容拷贝到访问目录vue-nginx下即可

server {
    listen 80;
    server_name tty.com;

    # 匹配html
    location / {
        # 配置跟目录
        root /Users/wangks/Documents/f/my-nginx/vue-nginx/;
        index index.html index.htm;
    }
}

举个例子index.html内的 实际访问的时候会是http://tty.com/vue-nginx/static/js/vendor.1c529e7d1ddeab46c70e.js,只要确保将http://tty.com/替换成/Users/wangks/Documents/f/my-nginx/vue-nginx/之后,地址/Users/wangks/Documents/f/my-nginx/vue-nginx/static/js/vendor.1c529e7d1ddeab46c70e.js是正确访问路径就是ok,如果路径不对则会404这时需要根据静态文件重新配置root

部署vue项目、不使用cdn,且publicPath = '/vue-nginx',hash路由, 然后把dist目录下的内容拷贝到访问目录vue-nginx下即可

server {
    listen 80;
    server_name tty.com;

    # 匹配html
    location / {
        # 配置跟目录
        root /Users/wangks/Documents/f/my-nginx/vue-nginx/;
        index index.html index.htm;
    }

    # 匹配静态资源
    location ^~ /vue-nginx/ {
        root /Users/wangks/Documents/f/my-nginx/;
    }

    # 或者匹配文件
    location ~* \.(gif|jpg|jpeg|png|css|js|ico)$ {
        root /Users/wangks/Documents/f/my-nginx/;
    }
}

部署vue项目、不使用cdn,且publicPath = '/',history路由,然后把dist目录下的内容拷贝到访问目录vue-nginx下即可

server {
    listen 80;
    server_name tto.com;

    # 匹配html
    location / {
        # 配置跟目录
        root /Users/wangks/Documents/f/my-nginx/vue-nginx-history/;
        # 支持history路由
        try_files $uri $uri/ /index.html /index.htm;
    }
}

部署react项目、不使用cdn,且publicPath = '/',history路由

server {
    listen 80;
    server_name ttreact.com;
    location / {
        root /Users/wangks/Documents/f/my-nginx/react-nginx-history/;
        try_files $uri $uri/ /index.html /index.htm;
    }
}

补充知识

MIME互联网媒体类型(Internet media type,也称为MIME类型(MIME type)或内容类型(content type))是给互联网上传输的内容赋予的分类类型。一份内容的互联网媒体类型是由其文件格式与内容决定的。互联网媒体类型与文件拓展名相对应,因此计算机系统常常通过拓展名来确定一个文件的媒体类型并决定与其相关联的软件。互联网媒体类型的分类标准由互联网号码分配局(IANA)发布。1996年十一月,媒体类型在RFC 2045中被最初定义,当时仅被使用在SMTP协议的电子邮件中。现在其他的协议(比如HTTP或者SIP)也都常使用MIME类型。 一个MIME类型至少包括两个部分:一个类型(type)和一个子类型(subtype)。此外,它还可能包括一个或多个可选参数(optional parameter)。比如,HTML文件的互联网媒体类型可能是text/html; charset = UTF-8

在这个例子中,文件类型为text,子类型为html,而charset是一个可选参数,其值为UTF-8。

MIME与HTTP协议

HTTP服务器在发送一份报文主体时,在HTTP报文头部插入解释自身数据类型的MIME头部信息(Content-Type)。

MIME-type和Content-Type的关系: 当web服务器收到静态的资源文件请求时,依据请求文件的后缀名在服务器的MIME配置文件中找到对应的MIME Type,再根据MIME Type设置HTTP Response的Content-Type,然后客户端如浏览器根据Content-Type的值处理文件。

独立类型表明了对文件的分类

类型 描述 典型示例
text 表明文件是普通文本,理论上是人类可读 text/plain, text/html, text/css, text/javascript
image 表明是某种图像。不包括视频,但是动态图(比如动态gif)也使用image类型 image/gif, image/png, image/jpeg, image/bmp, image/webp, image/x-icon, image/vnd.microsoft.icon
audio 表明是某种音频文件 audio/midi, audio/mpeg, audio/webm, audio/ogg, audio/wav
video 表明是某种视频文件 video/webm, video/ogg
application 表明是某种二进制数据 application/octet-stream, application/pkcs12, application/vnd.mspowerpoint, application/xhtml+xml, application/xml, application/pdf

对于text文件类型若没有特定的subtype,就使用 text/plain。类似的,二进制文件没有特定或已知的 subtype,即使用 application/octet-stream。

重要的MIME类型

application/octet-stream 这是应用程序文件的默认值。意思是 未知的应用程序文件 ,浏览器一般不会自动执行或询问执行。浏览器会像对待 设置了HTTP头Content-Disposition 值为 attachment 的文件一样来对待这类文件。

text/plain 文本文件默认值。即使它意味着未知的文本文件,但浏览器认为是可以直接展示的。

text/css 在网页中要被解析为CSS的任何CSS文件必须指定MIME为text/css。通常,服务器不识别以.css为后缀的文件的MIME类型,而是将其以MIME为text/plain 或 application/octet-stream 来发送给浏览器:在这种情况下,大多数浏览器不识别其为CSS文件,直接忽略掉。特别要注意为CSS文件提供正确的MIME类型

text/html

所有的HTML内容都应该使用这种类型。XHTML的其他MIME类型(如application/xml+html)现在基本不再使用(HTML5统一了这些格式)。

在nginx目录下查看mime.types

types {
    text/html                                        html htm shtml;
    text/css                                         css;
    text/xml                                         xml;
    image/gif                                        gif;
    image/jpeg                                       jpeg jpg;
    application/javascript                           js;
    application/atom+xml                             atom;
    application/rss+xml                              rss;

    text/mathml                                      mml;
    text/plain                                       txt;
    text/vnd.sun.j2me.app-descriptor                 jad;
    text/vnd.wap.wml                                 wml;
    text/x-component                                 htc;

    image/png                                        png;
    image/svg+xml                                    svg svgz;
    image/tiff                                       tif tiff;
    image/vnd.wap.wbmp                               wbmp;
    image/webp                                       webp;
    image/x-icon                                     ico;
    image/x-jng                                      jng;
    image/x-ms-bmp                                   bmp;

    ...
}

错误

1、nginx -s reload报错 nginx: [error] invalid PID number "" in "/usr/local/var/run/nginx/nginx.pid"

原因: nginx根本就没有启动过,所以pid文件的值为空没法平滑启动,先启动了才能平滑启动

2、chrome浏览器下css不生效 Resource interpreted as Stylesheet but transferred with MIME type text/plain: "http://ttreact.com/static/css/main.ecedf1df.css".

在nginx内加入include及default_type配置 include mime.types; default_type application/octet-stream;

未添加前 image

添加后 image

参考链接: https://www.cnblogs.com/wangzhisdu/p/7839109.html https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Basics_of_HTTP/MIME_types https://www.cnblogs.com/woshimrf/p/nginx-proxy-rewrite-url.html https://blog.csdn.net/zzhongcy/article/details/86303204

supccc commented 3 years ago

mac下安装nginx

使用brew安装nginx

brew install nginx

安装完成之后,通过brew list nginx可以查看nginx下载之后被放置目录

/usr/local/Cellar/nginx/1.15.2/.bottle/etc/ (15 files)
/usr/local/Cellar/nginx/1.15.2/bin/nginx
/usr/local/Cellar/nginx/1.15.2/homebrew.mxcl.nginx.plist
/usr/local/Cellar/nginx/1.15.2/html -> ../../../var/www // 注意这里将html目录链接到/usr/local/var/www,这样我们直接将文件放置到www目录即可访问
/usr/local/Cellar/nginx/1.15.2/share/man/man8/nginx.8

同时可以通过brew info nginx查看更详细的信息,如下所示

image

nginx常用操作命令

启动nginx

nginx

查看nginx配置是否正确

nginx -t 

重新加载配置|重启|停止|退出 nginx

nginx -s reload|reopen|stop|quit

分析配置文件

以默认的nginx.conf文件为例

#user  nobody; // user指令,表示以哪个用户运行nginx,nobody是一个权限比较低的用户
worker_processes  1; // 启动进程数,一般设置为cpu的核数,通过

# mac下的brew安装的nginx日志目录/usr/local/var/log/nginx
#error_log  logs/error.log; // 全局错误日志 类型包括debug | info | notice | warn | error | crit 从左至右debug最详细,crit最少
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid; // 记录当前启用nginx的进程id

# 工作模式及连接数上线
events {
  use epoll; # epoll是多路复用io中的一种方式,但是仅用于linux2.6以上内核,可以大大提高nginx的性能
    worker_connections  1024; # 单个后台worker process进程的最大并发链接数
}

# http服务
http {
  # 设定mine类型,类型由mime.type文件定义
    include       mime.types;
    # 默认输出content-type类型,当请求的文件类型在mime.types文件中找不到时,返回default_type定义的默认类型,供浏览器处理
    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;

  # 指令指定 nginx 是否调用 sendfile 函数(zero copy方式)来输出文件,对于普通应用,必须设为on,如果用来进行下载等应用磁盘IO重负载应用,可设置为 off,以平衡磁盘与网络I/O处理速度,降低系统的uptime.
    sendfile        on;
    #tcp_nopush     on;

  # 连接超时时间秒
    #keepalive_timeout  0;
    keepalive_timeout  65;

  # 开启gzip压缩 ,压缩有利于提升网络的传输速度,但非常小的文件 不需要压缩,压缩反而会影响速度
    #gzip  on;

    #设定反向代理服务器列表
  #weigth参数表示权值,权值越高被分配到的几率越大,反向代理服务器列表,表示访问myserver请求,会挑选服务器列表中的一个服务器进行转发请求,从而达到负载均衡分解请求压力效果;如何挑选服务器,有随机规则、权重规则或ip_hash规则等
  upstream myserver {
      server 192.168.1.2 weight=5;
      server 192.168.1.3 weight=1;
      server 192.168.1.4 weight=6;
  }

  # 虚拟主机,可以有多个虚拟主机
    server {
      # 监听8080端口
        listen       8080;

        # 定义使用此服务的访问域名或ip
        server_name  localhost;

      # 设置编码
        #charset koi8-r;

      # 设定本虚拟主机的访问日志
        #access_log  logs/host.access.log  main;

      # 路由规则模块,location表示路由规则,也就是用户请求的url地址,nginx有一套针对url的路由规则
        location / {
            root   html; # 定义服务器的默认网站根目录位置
            index  index.html index.htm; # 定义首页索引文件的名称
            proxy_pass http://myserver ;# 请求转向myserver定义的服务器列表
        }

        #error_page  404              /404.html;

        # 出现500,502,503,504错误 就路由请求 /50x.html
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

一个最简单的nginx.conf,即最少配置信息

events {
  worker_connections 1024;
}

http {
  server {
      server_name: test.com;
      location / {
          root html;
          index index.html index.htm;
      }
  }
}

虚拟主机

虚拟主机使用的是特殊的软硬件技术,它把一台运行在因特网上的服务器主机分成一台台“虚拟”的主机,每台虚拟主机都可以是一个独立的网站,可以具有独立的域名,具有完整的Intemet服务器功能(WWW、FTP、Email等),同一台主机上的虚拟主机之间是完全独立的。从网站访问者来看,每一台虚拟主机和一台独立的主机完全一样。

利用虚拟主机,不用为每个要运行的网站提供一台单独的Nginx服务器或单独运行一组Nginx进程。虚拟主机提供了在同一台服务器、同一组Nginx进程上运行多个网站的功能。

配置虚拟主机:虚拟主机由两部分组件server_name+listen;二者组成必须是唯一的;当server_name一样时,端口号必须不同;当端口号相同时,server_name必须不同;因为只有二者组成是唯一的,这样才可以区分是哪个虚拟主机

端口相同,server_name匹配优先级

完全匹配与通配符匹配对比

server {
  listen 8080;
  server_name *.1;
  location / {
      root /Users/wangks/Documents/f/my-nginx/server1;
      index index.html index.htm;
  }
}

server {
  listen 8080;
  server_name qwer.1;
  location / {
      root /Users/wangks/Documents/f/my-nginx/server2;
      index index.html index.htm;
  }
}
curl qwer.1:8080 输出结果 <div>server2</div>
curl test.1:8080 输出结果 <div>server1</div>

完全匹配比通配符匹配优先级高

通配符前与通配符后比对

server {
  listen 8080;
  server_name qwer.*;
  location / {
      root /Users/wangks/Documents/f/my-nginx/server3;
      index index.html index.htm;
  }
}

server {
  listen 8080;
  server_name *.1;
  location / {
      root /Users/wangks/Documents/f/my-nginx/server1;
      index index.html index.htm;
  }
}
curl qwer.1:8080 <div>server1</div>
curl qwer.2:8080 <div>server3</div>

通配符前优先级比通配符后优先级高

通配符后与正则表达式比对

server {
  listen 8080;
  server_name ~^\w+.1$;
  location / {
      root /Users/wangks/Documents/f/my-nginx/server4;
      index index.html index.htm;
  }
}

server {
  listen 8080;
  server_name qwer.*;
  location / {
      root /Users/wangks/Documents/f/my-nginx/server3;
      index index.html index.htm;
  }
}
curl qwer.1:8080 <div>server3</div>
curl test.1:8080 <div>server4</div>

通配符后优先级比正则表达式优化级高

server {
  listen 8080;
  server_name tty.com;
  location / {
      root /Users/wangks/Documents/f/my-nginx/server5;
      index index.html index.htm;
  }
}

server {
  listen 8080 default;
  server_name kfc.com ;
  location / {
      root /Users/wangks/Documents/f/my-nginx/server6;
      index index.html index.htm;
  }
}

server {
  listen 8080;
  server_name ~^\w+.1$;
  location / {
      root /Users/wangks/Documents/f/my-nginx/server4;
      index index.html index.htm;
  }
}
curl test.1:8080 <div>server4</div>
curl test.2:8080 <div>server6</div>

正则匹配优先级高于default,default只能作用于匹配不到的情况下,而且nginx.conf不可以配多个default,否则nginx重启时候会提示错误

server {
  listen 8080;
  server_name tty.com;
  location / {
      root /Users/wangks/Documents/f/my-nginx/server5;
      index index.html index.htm;
  }
}

server {
  listen 8080;
  server_name kfc.com ;
  location / {
      root /Users/wangks/Documents/f/my-nginx/server6;
      index index.html index.htm;
  }
}
curl test1.com:8080 <div>server5</div>

没有default的场景,默认匹配第一个server

端口相同时server_name匹配优先级结论如下:

完全匹配 >> 通配符在前匹配 >> 通配符在后匹配 >> 正则匹配 >> default >> 顺序上的第一个server

location匹配规则

location配置语法:location [=|~|~*|^~] /uri/ { … }

location匹配方式分为普通匹配和正则匹配

用~和~* 为前缀的匹配是正则匹配,用=和^~或没有前缀为普通匹配

  • ~ 前缀表示区分大小写的正则匹配
  • ~* 前缀表示不区分大小写的正则匹配
  • = 前缀表示精确匹配
  • ^~ 前缀表示uri以某个常规字符串开头,可以理解为url的普通匹配
  • / 默认匹配,当其它匹配都匹配不上的时候匹配/

因为一个server内支持多个location,所以location匹配规则存在优先级

对比一

server {
  listen 80;
  server_name ttl.com;
  root /Users/wangks/Documents/f/my-nginx/location-test;
  charset utf-8;

  # 普通匹配
  location /api {
      default_type 'text/html';
      return 200 '规则/api/';
  }

  # 全等匹配
  location = /api {
      default_type 'text/html';
      return 200 '规则=/api';
  }
}
curl ttl.com/api 返回结果 规则=/api

对比二

server {
  listen 80;
  server_name ttl.com;
  root /Users/wangks/Documents/f/my-nginx/location-test;
  charset utf-8;

  # 正则匹配
  location ~ ^/api {
      default_type 'text/html';
      return 200 '规则^~ /api';
  }

  # 前缀匹配
  location ^~ /api {
      default_type 'text/html';
      return 200 '规则^~ /api';
  }
}
curl ttl.com/api 返回结果 规则^~ /api

对比三

server {
  listen 80;
  server_name ttl.com;
  root /Users/wangks/Documents/f/my-nginx/location-test;
  charset utf-8;

  # 普通匹配
  location /api {
      default_type 'text/html';
      return 200 '规则 /api';
  }

  # 正则匹配
  location ~ ^/api {
      default_type 'text/html';
      return 200 '规则~ ^/api';
  }
}
curl ttl.com/api 返回结果 规则~ ^/api

对比四

server {
  listen 80;
  server_name ttl.com;
  root /Users/wangks/Documents/f/my-nginx/location-test;
  charset utf-8;

  # 正则匹配区分大小写
  location ~ ^/api {
      default_type 'text/html';
      return 200 '规则~ /api';
  }

  # 正则匹配,不区分大小写
  location ~* ^/api {
      default_type 'text/html';
      return 200 '规则~* /api';
  }
}
curl ttl.com/api 返回结果 规则~ /api

对比五

server {
  listen 80;
  server_name ttl.com;
  root /Users/wangks/Documents/f/my-nginx/location-test;
  charset utf-8;

  # 正则匹配,不区分大小写
  location ~* ^/api {
      default_type 'text/html';
      return 200 '规则~* /api';
  }

  # 正则匹配,区分大小写
  location ~ ^/api {
      default_type 'text/html';
      return 200 '规则~ /api';
  }
}
curl ttl.com/api 返回结果 规则~* /api

对比六

server {
  listen 80;
  server_name ttl.com;
  root /Users/wangks/Documents/f/my-nginx/location-test;
  charset utf-8;

  # 普通匹配
  location /api {
      default_type 'text/html';
      return 200 '规则 /api';
  }

  # 普通匹配
  location /api/b/ {
      default_type 'text/html';
      return 200 '规则/api/b/';
  }
}
curl ttl.com/api/a.html 返回结果 规则 /api
curl ttl.com/api/b/a.html 返回结果 规则/api/b/

从上可知匹配优先级

全等匹配 >> 前缀匹配 >> 正则匹配 >> 普通匹配

正则匹配之间顺序优先

同种匹配之间匹配长度优先

接口转发

场景一 proxy_pass 相对路径

server {
  listen 80;
  server_name ttl.com;
  root /Users/wangks/Documents/f/my-nginx/location-test;
  charset utf-8;

  # 前缀匹配
  location /api {
      default_type 'text/html';
      return 200 '规则 /api';
  }

  # 正则匹配
  location /api/ {
      default_type 'text/html';
      return 200 '规则/api/';
  }

  location / {
      default_type 'text/html';
      return 200 '规则/ $uri $request_uri';
  }
}

server {
  listen 80;
  server_name ttp.com;

  location / {
      proxy_pass http://ttl.com;
  }

  location /api {
      proxy_pass http://ttl.com;
  }

  location /api/ {
      proxy_pass http://ttl.com;
  }
}
curl ttp.com 返回结果 规则/ / /
curl ttp.com/api  返回结果  规则/api /api --- /api
curl ttp.com/api/a  返回结果 规则/api/ /api/a --- /api/a

可以看出来location 匹配的完整路径将直接透传给 proxy_pass 的uri

场景二 proxy_pass 绝对路径

server {
  listen 80;
  server_name ttl.com;
  root /Users/wangks/Documents/f/my-nginx/location-test;
  charset utf-8;

  # 前缀匹配
  location /v1 {
      default_type 'text/html';
      return 200 '规则 /v1 $uri --- $request_uri';
  }

  # 正则匹配
  location /v1/ {
      default_type 'text/html';
      return 200 '规则/v1/ $uri --- $request_uri';
  }
}

server {
  listen 80;
  server_name ttp.com;

  location / {
      if ($uri ~* ^/apq/) {
          proxy_pass http://ttl.com/; 
      } else {
          proxy_pass http://ttl.com/;
      }
  }

  location /api {
      proxy_pass http://ttl.com/;
  }

  location /api/ {
      proxy_pass http://ttl.com/;
  }

  location /abc {
      proxy_pass http://ttl.com/v1;
  }

  location /abc/ {
      proxy_pass http://ttl.com/v1;
  }

  location /apc {
      proxy_pass http://ttl.com/v1/;
  }

  location /apc/ {
      proxy_pass http://ttl.com/v1/;
  }
}
curl ttp.com  返回结果 规则/ / /  => ttl.com/
curl ttp.com/api   返回结果 规则/ / / => ttl.com/
curl ttp.com/api/b   返回结果  规则/ /b /b => ttl.com/b
curl ttp.com/abc   返回结果 规则 /v1 /v1 --- /v1 => ttl.com/v1
curl ttp.com/abc/b  返回结果  规则 /v1 /v1b --- /v1b => ttl.com/v1b
curl ttp.com/apc  返回结果  规则/v1/ /v1/ --- /v1/ => ttl.com/v1/
curl ttp.com/apc/b 返回结果 规则/v1/ /v1/b --- /v1/b => ttl.com/v1/b

场景三 正则表达式

location ~* ^/apq/ {
  proxy_pass http://ttl.com/;
}

nginx: [emerg] "proxy_pass" cannot have URI part in location given by regular expression, or inside named location, or inside "if" statement, or inside "limit_except" block in /usr/local/etc/nginx/nginx.conf:107

不能使用绝对路径

场景四 rewrite 重写

location /name/ {
  rewrite /name/([^/]+) /api?name=$1 break;
  proxy_pass http://ttl.com;
}

location /age/ {
  rewrite /age/([^/]+) /api?age=$1 break;
  proxy_pass http://ttl.com/;
}

location ^~/user/ {
  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_set_header X-NginX-Proxy true;

  rewrite ^/user/(.*)$ /$1 break;
  proxy_pass http://user;
}

location ^~/order/ {
  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_set_header X-NginX-Proxy true;

  rewrite ^/order/(.*)$ /$1 break;
  proxy_pass http://order;
}
curl ttp.com/name/xiaoming 返回结果  规则/api /api --- /api?name=xiaoming
curl ttp.com/age/18  返回结果 规则/api /api --- /api?age=18

rewrite之后相当于相对路径的透传方式

资源缓存

location ~* \.(js|css)$ {
  expires 7d;
  #expires -1;
  #add_header Cache-Control 'public';
  #add_header Cache-Control 'no-cache';
  #add_header Cache-Control 'no-store';
  #add_header Cache-Control 'max-age=86400';
}

设置资源缓存方式

通过expires设置
expires 日期; 表示资源的缓存日期

expires -1; 表示资源不缓存 相当于add_header Cache-Control 'no-cache';

image

通过add_header来添加Cache-Control
add_header Cache-Control 'public';

Cache-Control常用值

public:客户端和代理服务器可以缓存
private:客户端可以缓存
max-age:缓存的内容将在xxx秒后失效,意思就是在客户端收到信息后,信息会缓存xxx秒;过了xxx秒客户端必须重新获取信息
no-store:不缓存请求的任何返回内容,不缓存请求返回的任何内容
no-cache:强制向服务器端再验证一次

image

no-cache和no-store的区别就是,no-cache会缓存请求返回的内容,而no-store不缓存;但no-cache时,在下次用缓存的内容时,需要向服务器验证一下,缓存到底能不能用

private与public的区别是,priviate不允许代理服务器缓存,一般我们的项目会用nginx做代理服务器,所以priviate不允许nginx等这类代理服务器缓存

ETag验证

ETag:资源唯一标识 nginx默认返回etag

一般会把请求的内容做md5加密,返回唯一的标识;会把ETag的值一起返回给浏览器;浏览器会把ETag存储下来。如W/"5ea904c2-30e0b"

下一次请求时将Etag一并带过去给服务器,服务器只需要比较客户端传来的ETag跟自己服务器上该资源的ETag是否一致,就能很好地判断资源相对客户端而言是否被修改过了。

如果服务器发现ETag匹配不上,那么直接以常规GET 200状态码形式将新的资源(当然也包括了新的ETag)发给客户端;如果ETag是一致的,则直接返回304状态码客户端直接使用本地缓存即可。

那么客户端是如何把标记在资源上的ETag传回给服务器的呢?请求报文中有两个首部字段可以带上ETag值:

If-None-Match: ETag-value

If-None-Match: W/"5ea904c2-30e0b",告诉服务端如果ETag没匹配上需要重发资源数据,否则直接回送304和响应报头即可

当前各浏览器均是使用的该请求首部来向服务器传递保存的ETag值。

If-Match: ETag-value

告诉服务器如果没有匹配到ETag,或者收到了*值而当前并没有该资源实体,则应当返回412(Precondition Failed)状态码给客户端。否则服务器直接忽略该字段

ETag优点:

  • 可以更加精确的判断资源是否被修改,可以识别一秒内多次修改的情况。
  • 不存在版本问题,每次请求都回去服务器进行校验。

缺点:

  • 计算ETag值需要性能损耗。
  • 分布式服务器存储的情况下,计算ETag的算法如果不一样,会导致浏览器从一台服务器上获得页面内容后到另外一台服务器上进行验证时发现ETag不匹配的情况
Last-Modified验证

服务器将资源传递给客户端时,会将资源最后更改的时间以“Last-Modified: GMT”的形式加在实体首部上一起返回给客户端。

Last-Modified: Wed, 29 Apr 2020 04:38:26 GMT

客户端会为资源标记上该信息,下次再次请求时,会把该信息附带在请求头中一并带给服务器去做检查,若传递的时间值与服务器上该资源最终修改时间是一致的,则说明该资源没有被修改过,直接返回304状态码,内容为空。

如果两个时间不一致,则服务器会发回该资源并返回200状态码,和第一次请求时类似。这样保证不向客户端重复发出资源,也保证当服务器有变化时,客户端能够得到最新的资源

也类似ETag,客户端请求报文头,两种参数

If-Modified-Since: Last-Modified-value

If-Modified-Since: Wed, 29 Apr 2020 04:38:26 GMT

该请求首部告诉服务器如果客户端传来的最后修改时间与服务器上的一致,则直接回送304 和响应报头即可。

当前各浏览器均是使用的该请求参数来向服务器传递保存的 Last-Modified 值。

If-Unmodified-Since: Last-Modified-value

该值告诉服务器,若Last-Modified没有匹配上(资源在服务端的最后更新时间改变了),则应当返回412(Precondition Failed) 状态码给客户端。

Last-Modified 存在一个问题,如果在服务器上,一个资源被修改了,但其实际内容根本没发生改变,但修改时间变化了,会导致Last-Modified时间匹配不上而返回了整个实体给客户端(跟客户端缓存里有个一模一样的资源);就是错误的判断内容改变了。

负载均衡

nginx实现负载均衡原理,用户访问首先访问到nginx服务器,然后nginx服务器再从应用服务器集群中选择压力比较小的服务器,然后将该访问请求引向该服务器。如应用服务器集群中某一台服务器崩溃,那么从待选择服务器列表中将该服务器删除,也就是说一个服务器崩溃了,那么nginx服务器不会把请求引向到该服务器

upstream myrule { #myrule代表此负载均衡的规则名称
  server ttu.com:8080; #服务器1的ip地址以及端口
  server ttu.com:8081; #服务器2的ip地址以及端口
}

server {
  listen 80;
  server_name ttk.com;

  location / {
      proxy_pass http://myrule;
  }
}

server {
  listen 8080;
  server_name ttu.com;
  charset utf-8;

  location / {
      default_type 'text/html';m 
      return 200 'ttu.com:8080 $uri';
  }
}

server {
  listen 8081;
  server_name ttu.com;
  charset utf-8;

  location / {
      default_type 'text/html';
      return 200 'ttu.com:8081 $uri';
  }
}

上面的配置代表,用户访问服务器ttk.com,端口80时,nginx会把请求传发给myrule的负载均衡规则去处理;nginx会随机取一个myrule下的服务器来处理用户请求。

随机选取

curl ttk.com   ttu.com:8081 /
curl ttk.com   ttu.com:8080 /
curl ttk.com   ttu.com:8081 /
curl ttk.com   ttu.com:8080 /

权重选取

upstream myrule {
  server ttu.com:8080 weight=1; 
  server ttu.com:8081 weight=5;
}
curl ttk.com  ttu.com:8081 /
curl ttk.com  ttu.com:8081 /
curl ttk.com  ttu.com:8080 /
curl ttk.com  ttu.com:8081 /
curl ttk.com  ttu.com:8081 /
curl ttk.com  ttu.com:8081 /
curl ttk.com  ttu.com:8080 /

优先访问权重大的服务器

ip_hash选取

upstream myrule {
  ip_hash;
  server ttu.com:8080; 
  server ttu.com:8081;
}
curl ttk.com  ttu.com:8080 / 127.0.0.1
curl ttk.com  ttu.com:8080 / 127.0.0.1
curl ttk.com  ttu.com:8080 / 127.0.0.1
curl ttk.com  ttu.com:8080 / 127.0.0.1
curl ttk.com  ttu.com:8080 / 127.0.0.1
curl ttk.com  ttu.com:8080 / 127.0.0.1

将ip固定访问某个服务器,ip_hash这种方式在很多场景会用到,如:session会话,状态转发服务等业务

gzip压缩

gzip  on;
gzip_min_length 1k;
gzip_http_version 1.1;
gzip_vary on;
gzip_types text/plain application/javascript application/x-javascript text/css application/xml;
gzip_buffers 4 16k;

主要一定要保证返回的content-type在gzip_types值内包含

image

日志切割

在了解nginx日志之前我们需要现了解下nginx一些常用的内置变量

server {
  listen 80;
  server_name ttr.com;
  charset utf-8;

  location / {
      default_type 'text/html';
      return 200 'remote_addr => $remote_addr, time_local => $time_local, request => $request, http_host => $http_host, status => $status, body_bytes_sent => $body_bytes_sent, http_referer => $http_referer, http_user_agent => $http_user_agent, request_time => $request_time, host => $host, request_method  => $request_method, args => $args, content_length => $content_length, http_cookie => $http_cookie, remote_port => $remote_port, server_protocol => $server_protocol, server_addr => $server_addr, server_name => $server_name, server_port => $server_port, scheme => $scheme';
  }
}
key 说明 示例
$remote_addr 客户端ip地址 127.0.0.1
$time_local 访问时间和时区 04/May/2020:14:27:46 +0800
$request http请求首部第一行,包含请求方法、URI及http协议版本 GET /?name=xiaoming HTTP/1.1
$http_host 请求地址,即浏览器中你输入的地址(IP或域名) ttr.com
$status HTTP请求状态 200
$body_bytes_sent 发送给客户端文件内容大小 0
$http_referer url跳转来源
$http_user_agent 浏览器user_agent Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36
$request_time 整个请求的总时间 0.000
$host 请求信息中的 Host,如果请求中没有 Host 行,则等于设置的服务器名,不包含端口 ttr.com
$request_method 客户端请求方法 GET
$args 请求中的参数 name=xiaoming&age=18
$content_length 请求头中的 Content-length 字段
$http_cookie 客户群请求携带cookie a=123; key=aaa
$remote_port 客户端的端口 64966
$server_protocol 请求使用的协议 HTTP/1.1
$server_addr 服务器地址 127.0.0.1
$server_name 服务器名称 ttr.com
$server_port 服务器的端口号 80
$scheme HTTP 方法 http
$http_x_forwarded_for 真实客户端访问ip 127.0.0.1

设置access_log

访问日志主要用于记录客户端的请求。客户端向 nginx 服务器发起的每一次请求都会被记录到 access_log 中

定义日志输出格式
log_format <NAME> <Strin---g>;
关键字      格式标签   日志格式

默认log_format格式
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    <FILE>    <NAME>;
关键字         日志文件   格式标签

默认日志文件

access_log logs/access_log.log main;

设置error_log

error_log file [level];
关键字   日志文件 错误等级

默认值 error_log logs/error.log error;

level 可以是:debug、info、notice、warn、error、crit、alert、emerg 中的任意值。只有日志的错误级别大于等于level 指定的值才会被写入错误日志中,默认值是 error
# nginx内统一的错误日志输出
error_log logs/error.log error;

http {
  log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                  '$status $body_bytes_sent "$http_referer" '
                 '"$http_user_agent" "$http_x_forwarded_for"';
    # http内所有虚拟主机统一access_log,使用的日志格式标签是main
    access_log logs/access_log.log main;

    server {
      listen 80;
      server_name ttr.com;

      location / {
          index index.html;
      }

      # 该虚拟主机内单独使用的日志,使用的日志格式标签是main
      access_log logs/access_ttr_log.log main;

      # 该虚拟主机内单独使用的日志,使用的日志的错误等级是error
      error_log logs/error_ttr.log error;
    }
}

定义输出日志文件可以在http内定定义,这个可以记录整个http内的所有server nginx访问日志,也可以定义在单独的某个server内,针对这个server进行单独记录,如果某个server内有进行单独配置,则不会记录到http内的acess_log内

按日期格式输出access_log

if ($time_iso8601 ~ "^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})")
{
    set $year $1;
    set $month $2;
    set $day $3;
    set $hour $4;
    set $minutes $5;
    set $seconds $6;
}

access_log  /logs/access_$year-$month-$day-$hour.log  main;
127.0.0.1 - - [04/May/2020:15:26:08 +0800] "GET / HTTP/1.1" 200 920 "-" "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1" "-"
127.0.0.1 - - [04/May/2020:15:26:09 +0800] "GET /static/css/main.ecedf1df.css HTTP/1.1" 200 36589 "http://ttreact.com/" "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1" "-"
127.0.0.1 - - [04/May/2020:15:26:09 +0800] "GET /static/js/main.5503e124.js HTTP/1.1" 200 133167 "http://ttreact.com/" "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1" "-"

常用场景

部署vue项目、不使用cdn,且publicPath = '/',hash路由,然后把dist目录下的内容拷贝到访问目录vue-nginx下即可

server {
  listen 80;
  server_name tty.com;

  # 匹配html
  location / {
      # 配置跟目录
      root /Users/wangks/Documents/f/my-nginx/vue-nginx/;
      index index.html index.htm;
  }
}

举个例子index.html内的 实际访问的时候会是http://tty.com/vue-nginx/static/js/vendor.1c529e7d1ddeab46c70e.js,只要确保将http://tty.com/替换成/Users/wangks/Documents/f/my-nginx/vue-nginx/之后,地址/Users/wangks/Documents/f/my-nginx/vue-nginx/static/js/vendor.1c529e7d1ddeab46c70e.js是正确访问路径就是ok,如果路径不对则会404这时需要根据静态文件重新配置root

部署vue项目、不使用cdn,且publicPath = '/vue-nginx',hash路由, 然后把dist目录下的内容拷贝到访问目录vue-nginx下即可

server {
  listen 80;
  server_name tty.com;

  # 匹配html
  location / {
      # 配置跟目录
      root /Users/wangks/Documents/f/my-nginx/vue-nginx/;
      index index.html index.htm;
  }

  # 匹配静态资源
  location ^~ /vue-nginx/ {
      root /Users/wangks/Documents/f/my-nginx/;
  }

  # 或者匹配文件
  location ~* \.(gif|jpg|jpeg|png|css|js|ico)$ {
      root /Users/wangks/Documents/f/my-nginx/;
  }
}

部署vue项目、不使用cdn,且publicPath = '/',history路由,然后把dist目录下的内容拷贝到访问目录vue-nginx下即可

server {
  listen 80;
  server_name tto.com;

  # 匹配html
  location / {
      # 配置跟目录
      root /Users/wangks/Documents/f/my-nginx/vue-nginx-history/;
      # 支持history路由
      try_files $uri $uri/ /index.html /index.htm;
  }
}

部署react项目、不使用cdn,且publicPath = '/',history路由

server {
  listen 80;
  server_name ttreact.com;
  location / {
      root /Users/wangks/Documents/f/my-nginx/react-nginx-history/;
      try_files $uri $uri/ /index.html /index.htm;
  }
}

补充知识

MIME互联网媒体类型(Internet media type,也称为MIME类型(MIME type)或内容类型(content type))是给互联网上传输的内容赋予的分类类型。一份内容的互联网媒体类型是由其文件格式与内容决定的。互联网媒体类型与文件拓展名相对应,因此计算机系统常常通过拓展名来确定一个文件的媒体类型并决定与其相关联的软件。互联网媒体类型的分类标准由互联网号码分配局(IANA)发布。1996年十一月,媒体类型在RFC 2045中被最初定义,当时仅被使用在SMTP协议的电子邮件中。现在其他的协议(比如HTTP或者SIP)也都常使用MIME类型。 一个MIME类型至少包括两个部分:一个类型(type)和一个子类型(subtype)。此外,它还可能包括一个或多个可选参数(optional parameter)。比如,HTML文件的互联网媒体类型可能是text/html; charset = UTF-8

在这个例子中,文件类型为text,子类型为html,而charset是一个可选参数,其值为UTF-8。

MIME与HTTP协议

HTTP服务器在发送一份报文主体时,在HTTP报文头部插入解释自身数据类型的MIME头部信息(Content-Type)。

MIME-type和Content-Type的关系: 当web服务器收到静态的资源文件请求时,依据请求文件的后缀名在服务器的MIME配置文件中找到对应的MIME Type,再根据MIME Type设置HTTP Response的Content-Type,然后客户端如浏览器根据Content-Type的值处理文件。

独立类型表明了对文件的分类

类型 描述 典型示例
text 表明文件是普通文本,理论上是人类可读 text/plain, text/html, text/css, text/javascript
image 表明是某种图像。不包括视频,但是动态图(比如动态gif)也使用image类型 image/gif, image/png, image/jpeg, image/bmp, image/webp, image/x-icon, image/vnd.microsoft.icon
audio 表明是某种音频文件 audio/midi, audio/mpeg, audio/webm, audio/ogg, audio/wav
video 表明是某种视频文件 video/webm, video/ogg
application 表明是某种二进制数据 application/octet-stream, application/pkcs12, application/vnd.mspowerpoint, application/xhtml+xml, application/xml, application/pdf

对于text文件类型若没有特定的subtype,就使用 text/plain。类似的,二进制文件没有特定或已知的 subtype,即使用 application/octet-stream。

重要的MIME类型

application/octet-stream 这是应用程序文件的默认值。意思是 未知的应用程序文件 ,浏览器一般不会自动执行或询问执行。浏览器会像对待 设置了HTTP头Content-Disposition 值为 attachment 的文件一样来对待这类文件。

text/plain 文本文件默认值。即使它意味着未知的文本文件,但浏览器认为是可以直接展示的。

text/css 在网页中要被解析为CSS的任何CSS文件必须指定MIME为text/css。通常,服务器不识别以.css为后缀的文件的MIME类型,而是将其以MIME为text/plain 或 application/octet-stream 来发送给浏览器:在这种情况下,大多数浏览器不识别其为CSS文件,直接忽略掉。特别要注意为CSS文件提供正确的MIME类型

text/html

所有的HTML内容都应该使用这种类型。XHTML的其他MIME类型(如application/xml+html)现在基本不再使用(HTML5统一了这些格式)。

在nginx目录下查看mime.types

types {
    text/html                                        html htm shtml;
    text/css                                         css;
    text/xml                                         xml;
    image/gif                                        gif;
    image/jpeg                                       jpeg jpg;
    application/javascript                           js;
    application/atom+xml                             atom;
    application/rss+xml                              rss;

    text/mathml                                      mml;
    text/plain                                       txt;
    text/vnd.sun.j2me.app-descriptor                 jad;
    text/vnd.wap.wml                                 wml;
    text/x-component                                 htc;

    image/png                                        png;
    image/svg+xml                                    svg svgz;
    image/tiff                                       tif tiff;
    image/vnd.wap.wbmp                               wbmp;
    image/webp                                       webp;
    image/x-icon                                     ico;
    image/x-jng                                      jng;
    image/x-ms-bmp                                   bmp;

  ...
}

错误

1、nginx -s reload报错 nginx: [error] invalid PID number "" in "/usr/local/var/run/nginx/nginx.pid"

原因: nginx根本就没有启动过,所以pid文件的值为空没法平滑启动,先启动了才能平滑启动

2、chrome浏览器下css不生效 Resource interpreted as Stylesheet but transferred with MIME type text/plain: "http://ttreact.com/static/css/main.ecedf1df.css".

在nginx内加入include及default_type配置 include mime.types; default_type application/octet-stream;

未添加前 image

添加后 image

参考链接: https://www.cnblogs.com/wangzhisdu/p/7839109.html https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Basics_of_HTTP/MIME_types https://www.cnblogs.com/woshimrf/p/nginx-proxy-rewrite-url.html https://blog.csdn.net/zzhongcy/article/details/86303204