Nginx 反向代理 + 缓存 + 静态资源服务器 + 负载均衡

nginx常常挂在嘴边的就是反向代理,不过他还能够干不少事,我所了解的只是反向代理、静态文件缓存、静态资源服务器,对于负载均衡只是略有涉及。html

Nginx (“engine x”) 是一个高性能的 HTTP 和 反向代理 服务器 ,也是一个 IMAP/POP3/SMTP 代理 服务器 。 Nginx 是由 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的,第一个公开版本0.1.0发布于2004年10月4日。其将源代码以类BSD许可证的形式发布,因它的稳定性、丰富的功能集、示例配置文件和低系统资源的消耗而闻名前端

引用一下菜鸟教程的简介:Nginx功能丰富,可做为HTTP服务器,也可做为反向代理服务器,邮件服务器。支持FastCGI、SSL、Virtual Host、URL Rewrite、Gzip等功能。而且支持不少第三方的模块扩展。Nginx的稳定性、功能集、示例配置文件和低系统资源的消耗让他后来居上,在全球活跃的网站中有12.18%的使用比率,大约为2220万个网站。node

特色 (1):代理服务器,快速高效反向代理,提高网站性能。 (2):负载均衡器,内部支持Rails和PHP,也可支持HTTP代理服务器,对外进行服务。同时支持简单容错和利用算法进行负载均衡。 (3):性能方面,Nginx专门为性能设计,实现注重效率。采用Poll模型,能够支持更多的并发链接,并在大并发时占用很低内存。 (4):稳定性方面,采用分阶段资源分配技术,使CPU资源占用率低。 (5):高可用性方面,支持热备,启动迅速。linux

nginx安装

mac 下安装ios

brew install nginx
复制代码

安装目录为 /usr/local/Cellar/nginx/1.17.2/ 配置文件目录为 /usr/local/etc/nginx/nginx.conf 服务器默认路径 /usr/local/var/wwwnginx

经常使用命令

mac 下的启动命令web

  • 启动 nginx算法

  • 快速中止关闭 nignx -s stopchrome

  • 优雅的关闭 nginx -s quitaxios

  • 承载配置文件 nginx -s reload

  • 查看nginx进程 ps -ef | grep nginx

  • 查看配置文件是否正确 nginx -t

  • 优雅的杀死nginx进程 kill -quit 进程号

  • 快速的杀死nginx进程 kill -term 进程号

nginx配置

nginx 文件的默认配置文件位置 /usr/local/etc/nginx/nginx.conf

打开 /usr/local/etc/nginx/ 目录能够看到,里面有不少的配置文件,启动有一个nginx.confnginx.conf.default两个配置文件,刚开始安装的时候,两个文件的内容是同样的,因此咱们能够肆意的修改nginx.conf搞崩的话就直接把nginx.conf.default中的内容复制过来就好了又是一个新的nginx。

配置文件架构

// nginx全局块
...

// events块
events {
    ...
}

// http 块
http {
    // http全局块
    ...
    
    // server块
    server {
        ...
    }
    
    // http全局块
    ...
}

复制代码

配置文件加注释说明

# 配置nginx的用户组 默认为nobody
#user nobody;

# 配置nginx的主线程数量 nginx是一个主线程下面多个子线程
worker_processes  1;

# 配置nginx的错误日志 格式为 log路径 log级别
# error_log 的日志级别为: debug info notice warn error crit alert emerg 紧急由低到高
# error_log的默认日志级别为error,那么就只有紧急程度大于等于error的才会记录在日志
# error_log 的做用域为 main http mail stream server location

#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;

# 指定nginx进程运行文件存放地址
#pid logs/nginx.pid;


events {
    # poll是多路复用IO中的一种方式,可是仅用于linux2.6以上内核,能够大大提升nginx的性能
    # use poll
    
    # 设置网络的链接序列化 防止惊群现象发生 默认为 on
    # accept_mutex on;
    
    # 设置一个进程是否同时接受多个网络链接 默认为 off
    # multi_accept off
    
    # 最大链接数 默认为 512
    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 指定使用 sendfile 系统调用来传输文件。优势在于在两个文件描述符之间传递数据(彻底在内核中操做),从而避免了数据在内核缓冲区和用户缓冲区之间的拷贝,效率高,称之为零拷贝,这个东西有点讲究,自行百度
    # sendfile 做用域 location server http
    sendfile        on;
    
    #tcp_nopush on;
    
    # 连接超时时间 默认 75s 做用域 http server location
    #keepalive_timeout 0;
    keepalive_timeout  65;
    
    # 开始gzip压缩
    #gzip on;

    server {
        # 端口号
        listen       8080;
        # 域名或ip
        server_name  localhost;

        #charset koi8-r;

        #access_log logs/host.access.log main;
        
        # 对请求的路由进行过滤 正则匹配
        location / {
            root   html;
            index  index.html index.htm;
        }

        ...
    }
    include servers/*;
}

复制代码

nginx 日志

nginx的日志大体分为 access_logerror_log。error_log 记录的是nginx的错误日志。(如下对日志的理解不是很全面,还只是基础的)

error_log

  • 记录nginx错误日志
  • 做用域为 main http mail stream server location
  • 日志级别 debug info notice warn error crit alert emerg
  • 日志级别默认为 error 当级别高于或等于指定级别时才会记录

access_log

  • 记录请求经过的日志
  • 做用域为 http server location limit_except
  • 日志格式默认为 combined
  • 日志格式是能够自定义的
# 定义一个为 main 的日志格式
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;
复制代码

上方的 log_format 后面相似 $remote_addr 是nginx的内置变量,取值以下

$remote_addr, $http_x_forwarded_for(反向) 记录客户端IP地址
$remote_user 记录客户端用户名称
$request 记录请求的URL和HTTP协议
$status 记录请求状态
$body_bytes_sent 发送给客户端的字节数,不包括响应头的大小; 该变量与Apache模块mod_log_config里的“%B”参数兼容。
$bytes_sent 发送给客户端的总字节数。
$connection 链接的序列号。
$connection_requests 当前经过一个链接得到的请求数量。
$msec 日志写入时间。单位为秒,精度是毫秒。
$pipe 若是请求是经过HTTP流水线(pipelined)发送,pipe值为“p”,不然为“.”。
$http_referer 记录从哪一个页面连接访问过来的
$http_user_agent 记录客户端浏览器相关信息
$request_length 请求的长度(包括请求行,请求头和请求正文)。
$request_time 请求处理时间,单位为秒,精度毫秒; 从读入客户端的第一个字节开始,直到把最后一个字符发送给客户端后进行日志写入为止。
$time_iso8601 ISO8601标准格式下的本地时间。
$time_local 通用日志格式下的本地时间。

复制代码

反向代理

正向代理 反向代理

  • 正向代理大概的意思就是,客户端发送一个请求,这个请求包含服务器地址,那么代理服务器收到了请求后会将请求发送到客户端指定的服务器,并将响应内容传递给客户端,在这个过程当中,客户端是知道请求的服务器地址的,可是服务器是不知道哪一个客户端请求的。VPN作的就是这个事。
  • 反向代理大概的意思就是,客户端发送一个请求给代理服务器,由代理服务器来决定这个请求该交给哪一个服务器,这就是实现了服务器负载均衡,能够将请求转发到比较空闲的服务器来响应,这个时候,代理服务器就是相对于客户端的服务器,由于此时客户端也不知道请求交给了哪一个服务器。

我所理解的正向代理和反向代理就是这个意思,若有错误欢迎下方评论。

proxy_pass

那么nginx使用的就是proxy_pass属性来进行反向代理的处理,使用也是很简单。下面以nodejs开启一个创建在 localhost:4000 的服务

const http = require('http');

http.createServer(function(req, res){
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('server 4000');
}).listen(4000);
复制代码

请求 localhost:4000 能够打开咱们的页面

image_1disp1dvl1fii1dhj1dlk1qrnbuj9.png-13.8kB

那么咱们须要作的是将 localhost:5000的全部请求都代理到4000这个server里,这样就会出现咱们访问5000和访问4000同样的效果。具体配置以下

server {
        # 监听 localhost:5000
        listen     5000;
        server_name localhost;
        # / 表示匹配全部的请求,全部的请求都会通过这个过滤器
        location / {
            # 设定请求转发的地址
	        proxy_pass http://localhost:4000;
        }
}
复制代码

这边须要注意的是 proxy_pass 的写法,必须是http://或者https://开头的,http头是不能省的。

请求5000端口效果以下:

image_1dispgpetav110mb1tqkf718ufm.png-15.9kB

本地代理至百度

上方的例子过于简单,那么这一个和上面的有点相似,此次是将4000的端口号代理到www.baidu.com。修改一下proxy_pass

server {
        # 监听 localhost:5000
        listen     5000;
        server_name localhost;
        # / 表示匹配全部的请求,全部的请求都会通过这个过滤器
        location / {
            # 设定请求转发的地址
	        proxy_pass https://www.baidu.com;
        }
}
复制代码

这样就能够了,至于这边写的是http 仍是 https,这个却是不影响,由于百度内部会自动将http转成https毕竟安全嘛。

百度代理至本地

那么按照刚刚的思路就是监听 www.baidu.com 而后设置一下 proxy_passlocalhost:4000

配置以下

server {
        listen     80;
        server_name www.baidu.com;

        location / {
	        proxy_pass http://localhost:4000;
        }
}
复制代码

试一下,是否是没有用,没有用就对了。要是这么轻松的搞定nginx还玩个蛋。那么这个里面又有点操做了,先看正确的配置,修改本地的hosts文件,mac下的文件位置为 /etc/hosts可是须要 sudo 来进行修改,毕竟这个文件比较重要嘛

sudo vim /etc/hosts
复制代码

添加一句

image_1div5kqsk1jpe7gg10jg18k01g.png-4.1kB

server {
        listen     80;
        server_name baidu.com;

        location / {
	        proxy_pass http://127.0.0.1:4000;
        }
}
复制代码

通常在本地开发的时候咱们都会修改本地的hosts文件,可是会遇到一个问题就是有的时候是有用的有的时候又没用了,我这边的解决办法是,每次修改完hosts文件就清楚浏览器的浏览数据,尤为是缓存这一块的东西。

image_1div63u0e1s6j18fgtr11vhq101h1t.png-105.2kB

若是遇到nginx配置彻底正确hosts文件也配置了,可是仍是没有用,不妨清一下缓存,至少在我这是每次都是清完缓存才有用的。

nginx跨域

跨域的解决办法就是在header里面加上容许跨域的源等信息

location / {  
    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
    add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';

    if ($request_method = 'OPTIONS') {
        return 204;
    }
} 
复制代码

可是在实际项目里。origin仍是不要设置为*比较好,由于前端使用axios的话在获取session这一块会出现问题。

nginx缓存

这边有一个须要注意的地方,nginx做为静态资源服务器的时候是不作缓存的,只有当nginx进行反向代理的时候才具有缓存这个功能。我一开始写了半天发现鸟用都没有,最后才发现只有作代理的时候才具有缓存。

各大浏览器自己已经具备缓存了,好比说谷歌,咱们能够写一个html,而后在html引入一张图片,咱们能够看看浏览器是怎么对图片这些静态资源进行缓存的。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <img src="./static/test.jpg" alt="">
</body>
</html>
复制代码

这个html代码我使用http-server部署在了http-server -a 127.0.0.1 -p 4000能够看出是在127.0.0.1 4000端口。

这是首次加载的时候的情况

image_1div6rf381gma14094n10vib32q.png-31.3kB

刷新一下

image_1div6slsb1niq1atp1oonksd1ubt37.png-28.4kB

那么区别仍是很大的

  • html文件的状态码从200到304,304状态码代表该文件是从缓存中读取
  • jpg文件能够发如今size这一列,多了一个memory cache,这代表这个图片是从浏览器缓存中读取的
  • 各个文件的加载事件明显减小,尤为是图片

可能有的人会发现第一次加载的时候,显示的是from desk cache,那么chrome浏览器的缓存分为两种,一种是磁盘缓存一种是内存缓存,

官方文档:

Chrome employs two caches — an on-disk cache and a very fast in-memory cache. The lifetime of an in-memory cache is attached to the lifetime of a render process, which roughly corresponds to a tab. Requests that are answered from the in-memory cache are invisible to the web request API. If a request handler changes its behavior (for example, the behavior according to which requests are blocked), a simple page refresh might not respect this changed behavior. To make sure the behavior change goes through, call handlerBehaviorChanged() to flush the in-memory cache. But don't do it often; flushing the cache is a very expensive operation. You don't need to call handlerBehaviorChanged() after registering or unregistering an event listener.

大概意思就是:chrome有两种缓存,一种是desk cache一种是memory cache,而后memory cache的效率高于前者。

那么打开chrome devtools点开图片能够发现

image_1div7hhk3no5hfq1jft2j41t43k.png-39.4kB

cache-control后面有一个max-age,那么具体的有关缓存的技术这边就不说了我回头整理一下

那么针对不一样类型的文件进行缓存仍是很简单的,须要注意的在于location的正则匹配

那么第一种最简单的缓存,就是直接设置expires 缓存时间

设置expires

location ~ .*\.(jpg|png)$ {
	proxy_pass http://127.0.0.1:4000;
    expires 3m;
}
复制代码

image_1div8c7nk1jv05ds1tvo1a7n1bm841.png-44.2kB

expires是以秒为单位的,那么咱们设置为3m 也就是180秒,发现确实是能够的。

proxy_cache_path 的使用

那么咱们也能够指定咱们的nginx缓存目录,经过proxy_cache_path 属性

proxy_cache_path /tmp/cache/test levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m use_temp_path=off;
location ~ .*\.(jpg|png)$ {
	 proxy_pass http://127.0.0.1:4000;
	 proxy_cache my_cache;
	 proxy_cache_valid 200 304 1y;
	 proxy_cache_valid any 1m;
	 expires 1y;
}
复制代码
  • proxy_cache_path 执行缓存文件的目录,若是没有的话须要提早建立,否则nginx会报错
  • levels 采用2级目录来存储
  • key_zone 在共享内存中设置一块存储区域来存放缓存的key和metadata(相似使用次数),这样nginx能够快速判断一个request是否命中或者未命中缓存,1m能够存储8000个key,10m能够存储80000个key
  • max_size 最大cache空间,若是不指定,会使用掉全部disk space,当达到配额后,会删除最少使用的cache文件
  • inactive 未被访问文件在缓存中保留时间,在指定时间内未被访问的文件会被删除
  • use_temp_path 若是为off,则nginx会将缓存文件直接写入指定的cache文件中,而不是使用temp_path存储,official建议为off,避免文件在不一样文件系统中没必要要的拷贝;
  • proxy_cache 启用proxy cache,对应着配置的key_zone;
  • proxy_cache_valid 根据不一样的状态码设置不一样的缓存时间

能够查看一下nginx进程,会发现这个时候是有缓存的进程在开着的。

image_1div91gppe5h1n0gh751eqv13u54e.png-75.1kB

这边能够看到,咱们的图片的缓存时间已经被设置为1年

image_1div9h7o75lk4vgdqv1iidk5v4r.png-45.4kB

location匹配优先级

在缓存中须要注意的一点就是location的匹配规则和优先级

  • = 开头表示精确匹配
  • ^~ 开头表示uri以某个常规字符串开头,不是正则匹配;
  • ~ 开头表示区分大小写的正则匹配;
  • ~* 开头表示不区分大小写的正则匹配;
  • / 通用匹配, 若是没有其它匹配,任何请求都会匹配到;

upstream负载均衡

负载均衡是nginx的另外一大特色,能够配置多个服务器,将请求分发到最合适的那台服务器,避免某一台服务器请求太多而崩溃。使用upstream属性来配置

upstream favtomcat {
       server 192.168.1.100:4000;
       server 192.168.1.111:5000;
}
location / {
            root   html;
            index  index.html index.htm;
            # 对应上方的 favtomcat
            proxy_pass http://favtomcat;
}
复制代码

这是最基础的负载均衡配置,采用的是轮询的策略进行负载,每一个请求按时间顺序逐一分配到不一样的后端服务器,适用于图片服务器集群和纯静态页面服务器集群。 优势: 方式简便、成本低廉 缺点: 可靠性低和负载分配不均衡

那么upstream还有其余的负载策略

weight权重

能够给每一台服务器设置一个权重,这样权重高的干的活也就会多一点

upstream favtomcat {
       server 192.168.1.100:4000 weight=5;
       server 192.168.1.111:5000 weight=10;
}
复制代码

ip_hash

这种方式是基于客户端的ip地址,采用hash算法计算下一个请求要选择哪个服务器,这样固定的ip会访问同一个服务器,能够解决session问题

upstream favtomcat {
       ip_hash;
       server 192.168.1.100:4000;
       server 192.168.1.111:5000;
}
复制代码

least_conn最少连接

会将下一个请求分发到当前连接数最少的一台服务器

upstream favtomcat {
       least_conn;
       server 192.168.1.100:4000;
       server 192.168.1.111:5000;
}
复制代码

fair

按后端服务器的响应时间来分配请求,响应时间短的优先分配。

upstream favtomcat {
       fair;
       server 192.168.1.100:4000;
       server 192.168.1.111:5000;
}
复制代码

url_hash

按访问url的hash结果来分配请求,使每一个url定向到同一个后端服务器

upstream backserver { 
    server squid1:3128; 
    server squid2:3128; 
    hash $request_uri; 
    hash_method crc32; 
} 
复制代码

参考 juejin.cn/post/684490… Nginx Proxy Cache原理和最佳实践